Hi,

I started writing a test system for d3d state management. Here's what I have in mind (attached). I've converted the constant and recording of missing lights tests to this framework to demonstrate. As a result I've already found two bugs related to lights behavior (see the tests that fail). It's rather complicated, but I think it's more extensive than we we currently have, and may be useful.

Feedback welcome...
--------------

How to use to write tests for a particular state:
- define a data structure, which will represent the state data.
- define initial data sample (this is state the data should be in after the device is created, before any writes take place) - define default data sample (this is the data as it is read later on (may be affected by side effects of writes), and the data that the device is restored to on failure).
- define test_in data sample (what to try to set the device to)
- define test_out data sample (what windows will actually set the device to - i.e. if you do get afterwards)
- define a return buffer to write data to (from get requests)
- define a poison data sample to preinitialize the return buffer to
- define a get handler
- define a set handler
- define a print handler (for debugging, when comparison fails)
- pack all of the above, along with some other data into a state_test structure
 add a queue_test function, and run it from test_state_management()

The following tests will be ran [ in that order ], on all states that have a test simultaneously.
The "compare" part is a memcmp test on the defined structure.
After each test, the data is restored to default data samples.

1. Initial read:
- read states
- compare to initial data sample

2. Write/Read
- write states
- read states
- compare to test data samples

3. Stateblock Recording
- begin stateblock
- write states
- end stateblock
- release stateblock [abort]
- compare to default data samples

4. Stateblock Recording + Apply
- begin stateblock
- write states
- end stateblock
- apply stateblock
- compare to test data samples

5. Stateblock Capture and Reapply
- begin stateblock
- write states
- end stateblock
- capture stateblock
- compare to default data samples
- write states [ again, as poison ]
- apply stateblock
- compare to default data samples

Planned:
- Render target switch test
- Render target switch + stateblock interactions

----------------
How it works - a test is represented by an event sequence to be ran on the selected states. Each event is a funciton, and a command state [ which is combined with the function output, to abort on error ]. The test function loops over the events, executes the function, and takes action based on the specified commands: EVENT_CHECK_DEFAULT, EVENT_CHECK_INITIAL, EVENT_CHECK_TESt, EVENT_APPLY_DATA, EVENT_ERROR, EVENT_OK.

The tests above are represented as events sequences, defining new ones isn't too hard.

diff --git a/dlls/d3d9/tests/device.c b/dlls/d3d9/tests/device.c
diff --git a/dlls/d3d9/tests/shader.c b/dlls/d3d9/tests/shader.c
index e6a10b4..522c8d9 100644
--- a/dlls/d3d9/tests/shader.c
+++ b/dlls/d3d9/tests/shader.c
@@ -104,43 +104,6 @@ static void test_get_set_vertex_shader(I
         "Expected hret 0x%lx, current_shader_ptr %p, refcount %d.\n", hret, current_shader_ptr, shader_refcount, D3D_OK, shader_ptr, i);
 }
 
-static void test_get_set_vertex_shader_constants(IDirect3DDevice9 *device_ptr)
-{
-    HRESULT hret;
-    int int_in[4] = {0xdead0000, 0xdead0001, 0xdead0002, 0xdead0003};
-    int int_out[4] = {0};
-    float float_in[4] = {0.14f, 0.15f, 0.92f, 0.65f};
-    float float_out[4] = {0};
-    BOOL bool_in[4] = {TRUE, FALSE, FALSE, TRUE};
-    BOOL bool_out[4] = {0};
-
-    hret = IDirect3DDevice9_SetVertexShaderConstantI(device_ptr, 0, int_in, 1);
-    ok(hret == D3D_OK, "SetVertexShaderConstantI returned %#lx.\n", hret);
-    hret = IDirect3DDevice9_SetVertexShaderConstantF(device_ptr, 0, float_in, 1);
-    ok(hret == D3D_OK, "SetVertexShaderConstantF returned %#lx.\n", hret);
-    hret = IDirect3DDevice9_SetVertexShaderConstantB(device_ptr, 0, bool_in, 4);
-    ok(hret == D3D_OK, "SetVertexShaderConstantB returned %#lx.\n", hret);
-
-    hret = IDirect3DDevice9_GetVertexShaderConstantI(device_ptr, 0, int_out, 1);
-    ok(hret == D3D_OK, "GetVertexShaderConstantI returned %#lx.\n", hret);
-    hret = IDirect3DDevice9_GetVertexShaderConstantF(device_ptr, 0, float_out, 1);
-    ok(hret == D3D_OK, "GetVertexShaderConstantF returned %#lx.\n", hret);
-    hret = IDirect3DDevice9_GetVertexShaderConstantB(device_ptr, 0, bool_out, 4);
-    ok(hret == D3D_OK, "GetVertexShaderConstantB returned %#lx.\n", hret);
-
-    /* The constant arrays aren't shared between I/F/B, so they shouldn't
-     * overwrite eachother's values */
-    ok(!memcmp(int_in, int_out, sizeof(int_in)),
-            "Int constant data doesn't match (%#x, %#x, %#x, %#x).\n",
-            int_out[0], int_out[1], int_out[2], int_out[3]);
-    ok(!memcmp(float_in, float_out, sizeof(float_in)),
-            "Float constant data doesn't match (%f, %f, %f, %f).\n",
-            float_out[0], float_out[1], float_out[2], float_out[3]);
-    ok(!memcmp(bool_in, bool_out, sizeof(bool_in)),
-            "Bool constant data doesn't match (%#x, %#x, %#x, %#x).\n",
-            bool_out[0], bool_out[1], bool_out[2], bool_out[3]);
-}
-
 static void test_get_set_pixel_shader(IDirect3DDevice9 *device_ptr)
 {
     static DWORD simple_ps[] = {0xFFFF0101,                                     /* ps_1_1                       */
@@ -198,7 +161,6 @@ START_TEST(shader)
     if (caps.VertexShaderVersion & 0xffff)
     {
         test_get_set_vertex_shader(device_ptr);
-        test_get_set_vertex_shader_constants(device_ptr);
     }
     else trace("No vertex shader support, skipping test\n");
 
diff --git a/dlls/d3d9/tests/stateblock.c b/dlls/d3d9/tests/stateblock.c
index 178edf5..4127918 100644
--- a/dlls/d3d9/tests/stateblock.c
+++ b/dlls/d3d9/tests/stateblock.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2005 Henri Verbeet
+ * Copyright (C) 2006 Ivan Gyurdiev
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -91,91 +92,579 @@ static void test_begin_end_state_block(I
         "Expected hret 0x%lx, state_block_ptr 0xdeadbeef.\n", hret, state_block_ptr, D3DERR_INVALIDCALL);
 }
 
-static void test_missing_light_recording(IDirect3DDevice9 *device_ptr)
+/* ============================ State Testing Framework ========================== */
+
+typedef struct state_test {
+    const char* test_name;
+
+    /* The initial data is usually the same
+     * as the default data, but a write can have side effects.
+     * The initial data is tested first, before any writes take place
+     * The default data can be tested after a write */
+    const void* initial_data;
+
+    /* The default data is the standard state to compare
+     * against, and restore to */
+    const void* default_data;
+
+    /* The test data is the experiment data to try
+     * in - what we want to write
+     * out - what windows will actually write (not necessarily the same)  */
+    const void* test_data_in;
+    const void* test_data_out;
+
+    /* The poison data is the data to preinitialize the return buffer to */
+    const void* poison_data;
+    
+    /* Return buffer */
+    void* return_data;
+
+    unsigned int data_size;
+
+    void (*set_handler) (IDirect3DDevice9* device, const void* data, void* set_arg);
+    void (*get_handler) (IDirect3DDevice9* device, const void* data, void* get_arg);
+    void (*print_handler) (const void* data);
+
+    void* set_arg;
+    void* get_arg;
+
+} state_test;
+
+/* See below for explanation of the flags */
+#define EVENT_OK             0x00
+#define EVENT_CHECK_DEFAULT  0x01 
+#define EVENT_CHECK_INITIAL  0x02
+#define EVENT_CHECK_TEST     0x04
+#define EVENT_ERROR          0x08
+#define EVENT_APPLY_DATA     0x10
+
+typedef struct event {
+   int (*event_fn) (IDirect3DDevice9* device, void* arg);
+   int status;
+} event;
+
+/* This is an event-machine, which tests things.
+ * It tests get and set operations for a batch of states, based on
+ * results from the event function, which directs what's to be done */
+
+static void execute_test_chain(
+    IDirect3DDevice9* device,
+    state_test* test,
+    unsigned int ntests,
+    event *event,
+    unsigned int nevents,
+    void* event_arg) {
+
+    int outcome;
+    unsigned int i = 0, j;
+
+    /* For each queued event */
+    for (j=0; j < nevents; j++) {
+
+        /* Execute the next event handler (if available) or just set the supplied status */
+        outcome = event[j].status;
+        if (event[j].event_fn)
+            outcome |= event[j].event_fn(device, event_arg);
+
+        /* Now verify correct outcome depending on what was signaled by the handler.
+         * An EVENT_CHECK_TEST signal means check the returned data against the test_data (out).
+         * An EVENT_CHECK_DEFAULT signal means check the returned data against the default_data.
+         * An EVENT_CHECK_INITIAL signal means check the returned data against the initial_data.
+         * An EVENT_ERROR signal means the test isn't going to work, exit the event loop.
+         * An EVENT_APPLY_DATA signal means load the test data (after checks) */
+
+        if (outcome & EVENT_ERROR) {
+            trace("Test %s, Stage %u in error state, aborting\n", test[i].test_name, j);
+            break;
+
+        } else if (outcome & EVENT_CHECK_TEST || outcome & EVENT_CHECK_DEFAULT || outcome & EVENT_CHECK_INITIAL) {
+
+            for (i=0; i < ntests; i++) {
+
+                memcpy(test[i].return_data, test[i].poison_data, test[i].data_size);
+                test[i].get_handler(device, test[i].return_data, test[i].get_arg);
+                if ((outcome & EVENT_CHECK_TEST) &&
+                    memcmp(test[i].test_data_out, test[i].return_data, test[i].data_size)) {
+
+                    ok(FALSE, "Test %s, Stage %u: change applied, but returned data does not "
+                        "match test data [csize=%u]\n", test[i].test_name, j, test[i].data_size);
+
+                    if (test[i].print_handler) {
+                        trace("Returned data was:\n");
+                        test[i].print_handler(test[i].return_data);
+                        trace("Test data was:\n");
+                        test[i].print_handler(test[i].test_data_out);
+                    }
+                }
+
+                else if ((outcome & EVENT_CHECK_DEFAULT) &&
+                    memcmp(test[i].default_data, test[i].return_data, test[i].data_size)) {
+
+                    ok (FALSE, "Test %s, Stage %u: change aborted, but returned data does not "
+                         "match default data [csize=%u]\n", test[i].test_name, j, test[i].data_size);
+
+                    if (test[i].print_handler) {
+                        trace("Returned data was:\n");
+                        test[i].print_handler(test[i].return_data);
+                        trace("Default data was:\n");
+                        test[i].print_handler(test[i].default_data);
+                    }
+                }
+
+                else if ((outcome & EVENT_CHECK_INITIAL) &&
+                    memcmp(test[i].initial_data, test[i].return_data, test[i].data_size)) {
+
+                    ok (FALSE, "Test %s, Stage %u: returned data does not "
+                         "match initial data [csize=%u]\n", test[i].test_name, j, test[i].data_size);
+
+                    if (test[i].print_handler) {
+                        trace("Returned data was:\n");
+                        test[i].print_handler(test[i].return_data);
+                        trace("Initial data was:\n");
+                        test[i].print_handler(test[i].initial_data);
+                    }
+                }
+            }
+        }
+
+        if (outcome & EVENT_APPLY_DATA) {
+            for (i=0; i < ntests; i++)
+                test[i].set_handler(device, test[i].test_data_in, test[i].set_arg);
+        }
+     }
+
+     /* Attempt to reset any changes made */
+     for (i=0; i < ntests; i++)
+         test[i].set_handler(device, test[i].default_data, test[i].set_arg);
+}
+
+typedef struct stateblock_event_data {
+    IDirect3DStateBlock9* stateblock;
+} stateblock_event_data;
+
+static int begin_stateblock(
+    IDirect3DDevice9* device,
+    void* data) {
+
+    HRESULT hret;
+
+    data = NULL;
+    hret = IDirect3DDevice9_BeginStateBlock(device);
+    ok(hret == D3D_OK, "BeginStateBlock returned %#lx.\n", hret);
+    if (hret != D3D_OK) return EVENT_ERROR;
+    return EVENT_OK;
+}
+
+static int end_stateblock(
+    IDirect3DDevice9* device,
+    void* data) {
+
+    HRESULT hret;
+    stateblock_event_data* sdata = (stateblock_event_data*) data;
+
+    hret = IDirect3DDevice9_EndStateBlock(device, &sdata->stateblock);
+    ok(hret == D3D_OK, "EndStateBlock returned %#lx.\n", hret);
+    if (hret != D3D_OK) return EVENT_ERROR;
+    return EVENT_OK;
+}
+
+static int abort_stateblock(
+    IDirect3DDevice9* device,
+    void* data) {
+
+    stateblock_event_data* sdata = (stateblock_event_data*) data;
+
+    IUnknown_Release(sdata->stateblock);
+    return EVENT_OK;
+}
+
+static int apply_stateblock(
+    IDirect3DDevice9* device,
+    void* data) {
+
+    stateblock_event_data* sdata = (stateblock_event_data*) data;
+    HRESULT hret;
+
+    hret = IDirect3DStateBlock9_Apply(sdata->stateblock);
+    ok(hret == D3D_OK, "Apply returned %#lx.\n", hret);
+    if (hret != D3D_OK) {
+        IUnknown_Release(sdata->stateblock);
+        return EVENT_ERROR;
+    }
+
+    IUnknown_Release(sdata->stateblock);
+    return EVENT_OK;
+}
+
+static int capture_stateblock(
+    IDirect3DDevice9* device,
+    void* data) {
+
+    HRESULT hret;
+    stateblock_event_data* sdata = (stateblock_event_data*) data;
+
+    hret = IDirect3DStateBlock9_Capture(sdata->stateblock);
+    ok(hret == D3D_OK, "Capture returned %#lx.\n", hret);
+    if (hret != D3D_OK)
+        return EVENT_ERROR;
+
+    return EVENT_OK;
+}
+
+static void execute_test_chain_all(
+    IDirect3DDevice9* device,
+    state_test* test,
+    unsigned int ntests) {
+
+    stateblock_event_data sarg;
+
+    event read_events[1] = {
+        { NULL, EVENT_CHECK_INITIAL }
+    };
+
+    event write_read_events[2] = {
+        { NULL, EVENT_APPLY_DATA },
+        { NULL, EVENT_CHECK_TEST }
+    };
+
+    event abort_stateblock_events[3] = {
+        { begin_stateblock, EVENT_APPLY_DATA },
+        { end_stateblock, EVENT_OK },
+        { abort_stateblock, EVENT_CHECK_DEFAULT }
+    };
+
+    event apply_stateblock_events[3] = {
+        { begin_stateblock, EVENT_APPLY_DATA },
+        { end_stateblock, EVENT_OK },
+        { apply_stateblock, EVENT_CHECK_TEST }
+    };
+
+    event capture_reapply_stateblock_events[4] = {
+          { begin_stateblock, EVENT_APPLY_DATA },
+          { end_stateblock, EVENT_OK },
+          { capture_stateblock, EVENT_CHECK_DEFAULT | EVENT_APPLY_DATA },
+          { apply_stateblock, EVENT_CHECK_DEFAULT }
+    };
+
+    trace("Running initial read state tests\n");
+    execute_test_chain(device, test, ntests, read_events, 1, NULL);
+
+    trace("Running write-read state tests\n");
+    execute_test_chain(device, test, ntests, write_read_events, 2, NULL);
+
+    trace("Running stateblock abort state tests\n");
+    execute_test_chain(device, test, ntests, abort_stateblock_events, 3, &sarg);
+
+    trace("Running stateblock apply state tests\n");
+    execute_test_chain(device, test, ntests, apply_stateblock_events, 3, &sarg);
+
+    trace("Running stateblock capture/reapply state tests\n");
+    execute_test_chain(device, test, ntests, capture_reapply_stateblock_events, 4, &sarg);
+
+    /* TODO: test render target switch */
+}
+
+/* =================== State test: Pixel and Vertex Shader constants ============ */
+
+typedef struct shader_constant_data {
+    int int_constant[4];     /* 1x4 integer constant */
+    float float_constant[4]; /* 1x4 float constant */
+    BOOL bool_constant[4];   /* 4x1 boolean constants */
+} shader_constant_data;
+
+typedef struct shader_constant_arg {
+    unsigned int idx;
+    BOOL pshader;
+} shader_constant_arg;
+
+static void shader_constant_print_handler(
+    const void* data) {
+
+    shader_constant_data* scdata = (shader_constant_data*) data;
+
+    trace("Integer constant = { %#x, %#x, %#x, %#x }\n",
+        scdata->int_constant[0], scdata->int_constant[1],
+        scdata->int_constant[2], scdata->int_constant[3]);
+
+    trace("Float constant = { %f, %f, %f, %f }\n",
+        scdata->float_constant[0], scdata->float_constant[1],
+        scdata->float_constant[2], scdata->float_constant[3]);
+
+    trace("Boolean constants = [ %#x, %#x, %#x, %#x ]\n",
+        scdata->bool_constant[0], scdata->bool_constant[1],
+        scdata->bool_constant[2], scdata->bool_constant[3]);
+}
+
+static void shader_constant_set_handler(
+    IDirect3DDevice9* device, const void* data, void* arg) {
+
+    HRESULT hret;
+    shader_constant_data* scdata = (shader_constant_data*) data;
+    shader_constant_arg* scarg = (shader_constant_arg*) arg;
+    unsigned int index = scarg->idx;
+
+    if (!scarg->pshader) {
+        hret = IDirect3DDevice9_SetVertexShaderConstantI(device, index, scdata->int_constant, 1);
+        ok(hret == D3D_OK, "SetVertexShaderConstantI returned %#lx.\n", hret);
+        hret = IDirect3DDevice9_SetVertexShaderConstantF(device, index, scdata->float_constant, 1);
+        ok(hret == D3D_OK, "SetVertexShaderConstantF returned %#lx.\n", hret);
+        hret = IDirect3DDevice9_SetVertexShaderConstantB(device, index, scdata->bool_constant, 4);
+        ok(hret == D3D_OK, "SetVertexShaderConstantB returned %#lx.\n", hret);
+
+    } else {
+        hret = IDirect3DDevice9_SetPixelShaderConstantI(device, index, scdata->int_constant, 1);
+        ok(hret == D3D_OK, "SetPixelShaderConstantI returned %#lx.\n", hret);
+        hret = IDirect3DDevice9_SetPixelShaderConstantF(device, index, scdata->float_constant, 1);
+        ok(hret == D3D_OK, "SetPixelShaderConstantF returned %#lx.\n", hret);
+        hret = IDirect3DDevice9_SetPixelShaderConstantB(device, index, scdata->bool_constant, 4);
+        ok(hret == D3D_OK, "SetPixelShaderConstantB returned %#lx.\n", hret);
+    }
+}
+
+static void shader_constant_get_handler(
+    IDirect3DDevice9* device, const void* data, void* arg) {
+
+    HRESULT hret;
+    shader_constant_data* scdata = (shader_constant_data*) data;
+    shader_constant_arg* scarg = (shader_constant_arg*) arg;
+    unsigned int index = scarg->idx;
+
+    if (!scarg->pshader) {
+        hret = IDirect3DDevice9_GetVertexShaderConstantI(device, index, scdata->int_constant, 1);
+        ok(hret == D3D_OK, "GetVertexShaderConstantI returned %#lx.\n", hret);
+        hret = IDirect3DDevice9_GetVertexShaderConstantF(device, index, scdata->float_constant, 1);
+        ok(hret == D3D_OK, "GetVertexShaderConstantF returned %#lx.\n", hret);
+        hret = IDirect3DDevice9_GetVertexShaderConstantB(device, index, scdata->bool_constant, 4);
+        ok(hret == D3D_OK, "GetVertexShaderConstantB returned %#lx.\n", hret);
+
+    } else {
+        hret = IDirect3DDevice9_GetPixelShaderConstantI(device, index, scdata->int_constant, 1);
+        ok(hret == D3D_OK, "GetPixelShaderConstantI returned %#lx.\n", hret);
+        hret = IDirect3DDevice9_GetPixelShaderConstantF(device, index, scdata->float_constant, 1);
+        ok(hret == D3D_OK, "GetPixelShaderConstantF returned %#lx.\n", hret);
+        hret = IDirect3DDevice9_GetPixelShaderConstantB(device, index, scdata->bool_constant, 4);
+        ok(hret == D3D_OK, "GetPixelShaderConstantB returned %#lx.\n", hret);
+    }
+}
+
+static const shader_constant_data shader_constant_poison_data = {
+    { 0x1337c0de, 0x1337c0de, 0x1337c0de, 0x1337c0de },
+    { 1.0f, 2.0f, 3.0f, 4.0f },
+    { FALSE, TRUE, FALSE, TRUE }
+};
+
+static const shader_constant_data shader_constant_default_data = {
+        { 0 }, { 0 }, { 0 }
+};
+
+static const shader_constant_data shader_constant_test_data = {
+    { 0xdead0000, 0xdead0001, 0xdead0002, 0xdead0003 },
+    { 0.0f, 0.0f, 0.0f, 0.0f },
+    { TRUE, FALSE, FALSE, TRUE }
+};
+
+#define SHADER_CONSTANTS_REQ_BUFFER \
+     (sizeof(shader_constant_data) + sizeof(shader_constant_arg))
+
+static void shader_constants_queue_test(
+    IDirect3DDevice9 *device,
+    state_test* test,
+    void* buffer,
+    BOOL pshader)
 {
+    shader_constant_arg* arg = buffer;
+    shader_constant_data* return_data = (shader_constant_data*) (arg + 1);
+
+    test->test_data_in = &shader_constant_test_data;
+    test->test_data_out = &shader_constant_test_data;
+    test->default_data = &shader_constant_default_data;
+    test->initial_data = &shader_constant_default_data;
+    test->poison_data = &shader_constant_poison_data;
+    test->return_data = return_data;
+    test->data_size = sizeof(shader_constant_data);
+    test->set_handler = shader_constant_set_handler;
+    test->get_handler = shader_constant_get_handler;
+    test->print_handler = shader_constant_print_handler;
+    test->get_arg = test->set_arg = arg;
+    test->test_name = pshader? "set_get_pshader_constants": "set_get_vshader_constants";
+    arg->idx = 0;
+    arg->pshader = pshader;
+}
 
-    D3DLIGHT9 garbage_light = { 1, 
-                                { 2.0, 2.0, 2.0, 2.0 }, { 3.0, 3.0, 3.0, 3.0 }, { 4.0, 4.0, 4.0, 4.0 }, 
-                                { 5.0, 5.0, 5.0 }, { 6.0, 6.0, 6.0 }, 
-                                7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0 };
+/* =================== State test: Lights ===================================== */
+
+typedef struct light_data {
+    D3DLIGHT9 light;
+    BOOL enabled;
+    HRESULT get_light_result;
+    HRESULT get_enabled_result;
+} light_data;
+
+typedef struct light_arg {
+    unsigned int idx;
+} light_arg;
+
+static void light_print_handler(
+    const void* data) {
+
+    light_data* ldata = (light_data*) data;
+
+    trace("Get Light return value: %#lx\n", ldata->get_light_result);
+    trace("Get Light enable return value: %#lx\n", ldata->get_enabled_result);
+
+    trace("Light Enabled = %u\n", ldata->enabled);
+    trace("Light Type = %u\n", ldata->light.Type);
+    trace("Light Diffuse = { %f, %f, %f, %f }\n",
+        ldata->light.Diffuse.r, ldata->light.Diffuse.g,
+        ldata->light.Diffuse.b, ldata->light.Diffuse.a);
+    trace("Light Specular = { %f, %f, %f, %f}\n",
+        ldata->light.Specular.r, ldata->light.Specular.g,
+        ldata->light.Specular.b, ldata->light.Specular.a);
+    trace("Light Ambient = { %f, %f, %f, %f }\n",
+        ldata->light.Ambient.r, ldata->light.Ambient.g,
+        ldata->light.Ambient.b, ldata->light.Ambient.a);
+    trace("Light Position = { %f, %f, %f }\n",
+        ldata->light.Position.x, ldata->light.Position.y, ldata->light.Position.z);
+    trace("Light Direction = { %f, %f, %f }\n",
+        ldata->light.Direction.x, ldata->light.Direction.y, ldata->light.Direction.z);
+    trace("Light Range = %f\n", ldata->light.Range);
+    trace("Light Fallof = %f\n", ldata->light.Falloff);
+    trace("Light Attenuation0 = %f\n", ldata->light.Attenuation0);
+    trace("Light Attenuation1 = %f\n", ldata->light.Attenuation1);
+    trace("Light Attenuation2 = %f\n", ldata->light.Attenuation2);
+    trace("Light Theta = %f\n", ldata->light.Theta);
+    trace("Light Phi = %f\n", ldata->light.Phi);
+}
 
-    D3DLIGHT9 expected_light = { D3DLIGHT_DIRECTIONAL, 
-                                { 1.0, 1.0, 1.0, 0.0 }, { 0.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0, 0.0 },
-                                { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 1.0 }, 
-                                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
-    D3DLIGHT9 result_light;
-    BOOL result_light_enable;
+static void light_set_handler(
+    IDirect3DDevice9* device, const void* data, void* arg) {
 
-    HRESULT hret = 0;
-    IDirect3DStateBlock9 *state_block_ptr = 0;
+    HRESULT hret;
+    light_data* ldata = (light_data*) data;
+    light_arg* larg = (light_arg*) arg;
+    unsigned int index = larg->idx;
+
+    hret = IDirect3DDevice9_SetLight(device, index, &ldata->light);
+    ok(hret == D3D_OK, "SetLight returned %#lx.\n", hret);
 
-    /* Before we make any changes, GetLightEnable should fail, and GetLight should fail */
-    hret = IDirect3DDevice9_GetLightEnable(device_ptr, 0, &result_light_enable);
-    ok(hret == D3DERR_INVALIDCALL, "GetLightEnable returned: hret 0x%lx. Expected hret 0x%lx. Aborting.\n", 
-        hret, D3DERR_INVALIDCALL);
-    if (hret != D3DERR_INVALIDCALL) goto cleanup;
+    hret = IDirect3DDevice9_LightEnable(device, index, ldata->enabled);
+    ok(hret == D3D_OK, "SetLightEnable returned %#lx.\n", hret);
+}
 
-    hret = IDirect3DDevice9_GetLight(device_ptr, 0, &result_light);
-    ok(hret == D3DERR_INVALIDCALL, "GetLight returned: hret 0x%lx. Expected hret 0x%lx. Aborting.\n", 
-        hret, D3DERR_INVALIDCALL);
-    if (hret != D3DERR_INVALIDCALL) goto cleanup;
+static void light_get_handler(
+    IDirect3DDevice9* device, const void* data, void* arg) {
 
-    /* Set states to record with garbage data (light, light_enable) */
-    hret = IDirect3DDevice9_BeginStateBlock(device_ptr);
-    ok(hret == D3D_OK, "BeginStateBlock returned: hret 0x%lx. Expected hret 0x%lx. Aborting.\n", hret, D3D_OK);
-    if (hret != D3D_OK) goto cleanup;
+    HRESULT hret;
+    light_data* ldata = (light_data*) data;
+    light_arg* larg = (light_arg*) arg;
+    unsigned int index = larg->idx;
 
-    hret = IDirect3DDevice9_SetLight(device_ptr, 0, &garbage_light);
-    ok(hret == D3D_OK, "SetLight returned: hret 0x%lx. Expected hret 0x%lx. Aborting.\n", hret, D3D_OK);
-    if (hret != D3D_OK) goto cleanup;
+    hret = IDirect3DDevice9_GetLightEnable(device, index, &ldata->enabled);
+    ldata->get_enabled_result = hret;
 
-    hret = IDirect3DDevice9_LightEnable(device_ptr, 0, TRUE);
-    ok(hret == D3D_OK, "SetLightEnable returned: hret 0x%lx, Expected hret 0x%lx. Aborting.\n", hret, D3D_OK);
-    if (hret != D3D_OK) goto cleanup;
+    hret = IDirect3DDevice9_GetLight(device, index, &ldata->light);
+    ldata->get_light_result = hret;
+}
 
-    state_block_ptr = (IDirect3DStateBlock9 *)0xdeadbeef;
-    hret = IDirect3DDevice9_EndStateBlock(device_ptr, &state_block_ptr);
-    ok(hret == D3D_OK, "EndStateBlock returned: hret 0x%lx. Expected hret 0x%lx. Aborting.\n", hret, D3D_OK);
-    if (hret != D3D_OK) goto cleanup; 
-
-    /* Now capture light data */
-    hret = IDirect3DStateBlock9_Capture(state_block_ptr);
-    ok(hret == D3D_OK, "Capture returned: hret 0x%lx. Expected hret 0x%lx. Aborting.\n", hret, D3D_OK);
-    if (hret != D3D_OK) goto cleanup;
-
-    /* Now apply the resultant stateblock */
-    hret = IDirect3DStateBlock9_Apply(state_block_ptr);
-    ok(hret == D3D_OK, "Apply returned: hret 0x%lx. Expected hret 0x%lx. Aborting.\n", hret, D3D_OK);
-    if (hret != D3D_OK) goto cleanup;
-
-    /* Now try to fetch the light set by the stateblock.
-     * Light enable should have been set to 0, and a default light should have been
-     * created as a side effect of that */
-    hret = IDirect3DDevice9_GetLightEnable(device_ptr, 0, &result_light_enable);
-    ok(hret == D3D_OK, "GetLightEnable returned: hret 0x%lx. Expected hret 0x%lx. Aborting.\n", hret, D3D_OK);
-    if (hret != D3D_OK) goto cleanup;
-    ok(result_light_enable == 0, "Light enabled status was %u, instead of 0\n", result_light_enable);
-
-    hret = IDirect3DDevice9_GetLight(device_ptr, 0, &result_light);
-    ok(hret == D3D_OK, "GetLight returned: hret 0x%lx. Expected hret 0x%lx. Aborting.\n", hret, D3D_OK);
-    if (hret != D3D_OK) goto cleanup;
-
-    ok (!memcmp(&expected_light, &result_light, sizeof(D3DLIGHT9)), 
-        "Light returned = { %u, { %f, %f, %f, %f }, { %f, %f, %f, %f}, { %f, %f, %f, %f }, "
-                         "{ %f, %f, %f }, { %f, %f, %f }, %f, %f, %f, %f, %f, %f, %f }\n",
-
-         result_light.Type, 
-         result_light.Diffuse.r, result_light.Diffuse.g, result_light.Diffuse.b, result_light.Diffuse.a,
-         result_light.Specular.r, result_light.Specular.g, result_light.Specular.b, result_light.Specular.a,
-         result_light.Ambient.r, result_light.Ambient.g, result_light.Ambient.b, result_light.Ambient.a,
-         result_light.Position.x, result_light.Position.y, result_light.Position.z,
-         result_light.Direction.x, result_light.Direction.y, result_light.Direction.z, 
-         result_light.Range, result_light.Falloff,
-         result_light.Attenuation0, result_light.Attenuation1, result_light.Attenuation2,
-         result_light.Theta, result_light.Phi);
-
-    cleanup:
-    if (state_block_ptr) IUnknown_Release( state_block_ptr );
+static const light_data light_poison_data = 
+    { { 0x1337c0de,
+        { 7.0, 4.0, 2.0, 1.0 }, { 7.0, 4.0, 2.0, 1.0 }, { 7.0, 4.0, 2.0, 1.0 },
+        { 3.3, 4.4, 5.5 }, { 6.6, 7.7, 8.8 },
+        12.12, 13.13, 14.14, 15.15, 16.16, 17.17, 18.18 }, 1, 0x1337c0de, 0x1337c0de };
+
+static const light_data light_default_data =
+    { { D3DLIGHT_DIRECTIONAL,
+        { 1.0, 1.0, 1.0, 0.0 }, { 0.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0, 0.0 },
+        { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 1.0 },
+        0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }, 0, D3D_OK, D3D_OK };
+
+/* This is used for the initial read state (before a write causes side effects)
+ * The proper return status is D3DERR_INVALIDCALL */
+static const light_data light_initial_data =
+    { { 0x1337c0de,
+        { 7.0, 4.0, 2.0, 1.0 }, { 7.0, 4.0, 2.0, 1.0 }, { 7.0, 4.0, 2.0, 1.0 },
+        { 3.3, 4.4, 5.5 }, { 6.6, 7.7, 8.8 },
+        12.12, 13.13, 14.14, 15.15, 16.16, 17.17, 18.18 }, 1, D3DERR_INVALIDCALL, D3DERR_INVALIDCALL };
+
+static const light_data light_test_data_in =
+    { { 1,
+        { 2.0, 2.0, 2.0, 2.0 }, { 3.0, 3.0, 3.0, 3.0 }, { 4.0, 4.0, 4.0, 4.0 },
+        { 5.0, 5.0, 5.0 }, { 6.0, 6.0, 6.0 },
+        7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0 }, 1, D3D_OK, D3D_OK};
+
+/* SetLight will use 128 as the "enabled" value */
+static const light_data light_test_data_out = 
+    { { 1,
+        { 2.0, 2.0, 2.0, 2.0 }, { 3.0, 3.0, 3.0, 3.0 }, { 4.0, 4.0, 4.0, 4.0 },
+        { 5.0, 5.0, 5.0 }, { 6.0, 6.0, 6.0 },
+        7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0 }, 128, D3D_OK, D3D_OK};
+
+#define LIGHTS_REQ_BUFFER \
+     (sizeof(light_data) + sizeof(light_arg))
+
+static void lights_queue_test(
+    IDirect3DDevice9 *device,
+    state_test* test,
+    void* buffer)
+{
+    light_arg* arg = buffer;
+    light_data* return_data = (light_data*) (arg + 1);
+
+    test->test_data_in = &light_test_data_in;
+    test->test_data_out = &light_test_data_out;
+    test->default_data = &light_default_data;
+    test->initial_data = &light_initial_data;
+    test->poison_data = &light_poison_data;
+    test->return_data = return_data;
+    test->data_size = sizeof(light_data);
+    test->set_handler = light_set_handler;
+    test->get_handler = light_get_handler;
+    test->print_handler = light_print_handler;
+    test->get_arg = test->set_arg = arg;
+    test->test_name = "set_get_light";
+    arg->idx = 0;
+}
+
+/* =================== Main state tests function =============================== */
+
+static void test_state_management(
+    IDirect3DDevice9 *device) {
+
+    HRESULT hret;
+    D3DCAPS9 caps;
+
+    /* Test count: 2 for shader constants 
+                   1 for lights
+     */
+    const int max_tests = 2 + 1;
+    const int max_buffer = SHADER_CONSTANTS_REQ_BUFFER * 2 + LIGHTS_REQ_BUFFER * 1;
+    unsigned int tcount = 0;
+    unsigned int bcount = 0;
+
+    state_test tests[max_tests];
+    BYTE buffer[max_buffer];
+
+    hret = IDirect3DDevice9_GetDeviceCaps(device, &caps);
+    ok(hret == D3D_OK, "GetDeviceCaps returned %#lx.\n", hret);
+    if (hret != D3D_OK) return;
+
+    if (caps.VertexShaderVersion & 0xffff) {
+        shader_constants_queue_test(device, &tests[tcount], &buffer[bcount], FALSE);
+        bcount += SHADER_CONSTANTS_REQ_BUFFER;
+        tcount++;
+    }
+
+    if (caps.PixelShaderVersion & 0xffff) {
+        shader_constants_queue_test(device, &tests[tcount], &buffer[bcount], TRUE);
+        bcount += SHADER_CONSTANTS_REQ_BUFFER;
+        tcount++;
+    }
+
+    lights_queue_test(device, &tests[tcount], &buffer[bcount]);
+    bcount += LIGHTS_REQ_BUFFER;
+    tcount++;
+
+    execute_test_chain_all(device, tests, tcount);
 }
 
 START_TEST(stateblock)
@@ -193,5 +682,7 @@ START_TEST(stateblock)
     if (!device_ptr) return;
 
     test_begin_end_state_block(device_ptr);
-    test_missing_light_recording(device_ptr);
+    test_state_management(device_ptr);
+
+    if (device_ptr) IUnknown_Release(device_ptr);
 }


Reply via email to