1 /* Message Sequence Testing Code
2  *
3  * Copyright (C) 2007 James Hawkins
4  * Copyright (C) 2007 Lei Zhang
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #pragma once
22 
23 #include <assert.h>
24 #include <windows.h>
25 #include "wine/heap.h"
26 #include "wine/test.h"
27 
28 /* undocumented SWP flags - from SDK 3.1 */
29 #define SWP_NOCLIENTSIZE	0x0800
30 #define SWP_NOCLIENTMOVE	0x1000
31 
32 typedef enum
33 {
34     sent = 0x1,
35     posted = 0x2,
36     parent = 0x4,
37     wparam = 0x8,
38     lparam = 0x10,
39     defwinproc = 0x20,
40     beginpaint = 0x40,
41     optional = 0x80,
42     hook = 0x100,
43     winevent_hook =0x200,
44     id = 0x400,
45     custdraw = 0x800
46 } msg_flags_t;
47 
48 struct message
49 {
50     UINT message;       /* the WM_* code */
51     msg_flags_t flags;  /* message props */
52     WPARAM wParam;      /* expected value of wParam */
53     LPARAM lParam;      /* expected value of lParam */
54     UINT id;            /* extra message data: id of the window,
55                            notify code etc. */
56     DWORD stage;        /* custom draw stage */
57 };
58 
59 struct msg_sequence
60 {
61     int count;
62     int size;
63     struct message *sequence;
64 };
65 
66 static void add_message(struct msg_sequence **seq, int sequence_index,
67     const struct message *msg)
68 {
69     struct msg_sequence *msg_seq = seq[sequence_index];
70 
71     if (!msg_seq->sequence)
72     {
73         msg_seq->size = 10;
74         msg_seq->sequence = heap_alloc(msg_seq->size * sizeof (*msg_seq->sequence));
75     }
76 
77     if (msg_seq->count == msg_seq->size)
78     {
79         msg_seq->size *= 2;
80         msg_seq->sequence = heap_realloc(msg_seq->sequence, msg_seq->size * sizeof (*msg_seq->sequence));
81     }
82 
83     assert(msg_seq->sequence);
84 
85     msg_seq->sequence[msg_seq->count] = *msg;
86     msg_seq->count++;
87 }
88 
89 static inline void flush_sequence(struct msg_sequence **seg, int sequence_index)
90 {
91     struct msg_sequence *msg_seq = seg[sequence_index];
92     heap_free(msg_seq->sequence);
93     msg_seq->sequence = NULL;
94     msg_seq->count = msg_seq->size = 0;
95 }
96 
97 static inline void flush_sequences(struct msg_sequence **seq, int n)
98 {
99     int i;
100 
101     for (i = 0; i < n; i++)
102         flush_sequence(seq, i);
103 }
104 
105 static void dump_sequence( struct msg_sequence **seq, int sequence_index,
106                            const struct message *expected, const char *context,
107                            const char *file, int line )
108 {
109     struct msg_sequence *msg_seq = seq[sequence_index];
110     const struct message *actual, *sequence;
111     unsigned int count = 0;
112 
113     sequence = msg_seq->sequence;
114     actual = sequence;
115 
116     trace_(file, line)("Failed sequence %s:\n", context );
117     while (expected->message && actual->message)
118     {
119         trace_(file, line)( "  %u: expected: %04x - actual: %04x wp %08lx lp %08lx\n",
120                             count, expected->message, actual->message, actual->wParam, actual->lParam );
121 
122 	if (expected->message == actual->message)
123 	{
124 	    if ((expected->flags & defwinproc) != (actual->flags & defwinproc) &&
125                 (expected->flags & optional))
126             {
127                 /* don't match messages if their defwinproc status differs */
128                 expected++;
129             }
130             else
131             {
132                 expected++;
133                 actual++;
134             }
135 	}
136         else
137         {
138             expected++;
139             actual++;
140         }
141         count++;
142     }
143 
144     /* optional trailing messages */
145     while (expected->message && expected->flags & optional)
146     {
147         trace_(file, line)( "  %u: expected: msg %04x - actual: nothing\n", count, expected->message );
148 	expected++;
149         count++;
150     }
151 
152     if (expected->message)
153     {
154         trace_(file, line)( "  %u: expected: msg %04x - actual: nothing\n", count, expected->message );
155         return;
156     }
157 
158     while (actual->message)
159     {
160         trace_(file, line)( "  %u: expected: nothing - actual: %04x wp %08lx lp %08lx\n",
161                             count, actual->message, actual->wParam, actual->lParam );
162         actual++;
163         count++;
164     }
165 }
166 
167 static inline void ok_sequence_(struct msg_sequence **seq, int sequence_index,
168     const struct message *expected_list, const char *context, BOOL todo,
169     const char *file, int line)
170 {
171     static const struct message end_of_sequence = {0, 0, 0, 0};
172     struct msg_sequence *msg_seq = seq[sequence_index];
173     const struct message *expected = expected_list;
174     const struct message *actual, *sequence;
175     int failcount = 0, dump = 0;
176 
177     add_message(seq, sequence_index, &end_of_sequence);
178 
179     sequence = msg_seq->sequence;
180     actual = sequence;
181 
182     while (expected->message && actual->message)
183     {
184         if (expected->message == actual->message)
185         {
186             if (expected->flags & wparam)
187             {
188                 if (expected->wParam != actual->wParam && todo)
189                 {
190                     todo_wine
191                     {
192                         failcount++;
193                         dump++;
194                         ok_(file, line) (FALSE,
195                             "%s: in msg 0x%04x expecting wParam 0x%lx got 0x%lx\n",
196                             context, expected->message, expected->wParam, actual->wParam);
197                     }
198                 }
199                 else
200                 {
201                     ok_(file, line) (expected->wParam == actual->wParam,
202                         "%s: in msg 0x%04x expecting wParam 0x%lx got 0x%lx\n",
203                         context, expected->message, expected->wParam, actual->wParam);
204                     if (expected->wParam != actual->wParam) dump++;
205                 }
206             }
207 
208             if (expected->flags & lparam)
209             {
210                 if (expected->lParam != actual->lParam && todo)
211                 {
212                     todo_wine
213                     {
214                         failcount++;
215                         dump++;
216                         ok_(file, line) (FALSE,
217                             "%s: in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n",
218                             context, expected->message, expected->lParam, actual->lParam);
219                     }
220                 }
221                 else
222                 {
223                     ok_(file, line) (expected->lParam == actual->lParam,
224                         "%s: in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n",
225                         context, expected->message, expected->lParam, actual->lParam);
226                     if (expected->lParam != actual->lParam) dump++;
227                 }
228             }
229 
230             if (expected->flags & custdraw)
231             {
232                 if (expected->stage != actual->stage && todo)
233                 {
234                     todo_wine
235                     {
236                         failcount++;
237                         dump++;
238                         ok_(file, line) (FALSE,
239                             "%s: in msg 0x%04x expecting cd stage 0x%08x got 0x%08x\n",
240                             context, expected->message, expected->stage, actual->stage);
241                     }
242                 }
243                 else
244                 {
245                     ok_(file, line) (expected->stage == actual->stage,
246                         "%s: in msg 0x%04x expecting cd stage 0x%08x got 0x%08x\n",
247                         context, expected->message, expected->stage, actual->stage);
248                     if (expected->stage != actual->stage) dump++;
249                 }
250             }
251 
252             if (expected->flags & id)
253             {
254                 if (expected->id != actual->id && expected->flags & optional)
255                 {
256                     expected++;
257                     continue;
258                 }
259                 if (expected->id != actual->id && todo)
260                 {
261                     todo_wine
262                     {
263                         failcount++;
264                         dump++;
265                         ok_(file, line) (FALSE,
266                             "%s: in msg 0x%04x expecting id 0x%x got 0x%x\n",
267                             context, expected->message, expected->id, actual->id);
268                     }
269                 }
270                 else
271                 {
272                     ok_(file, line) (expected->id == actual->id,
273                         "%s: in msg 0x%04x expecting id 0x%x got 0x%x\n",
274                         context, expected->message, expected->id, actual->id);
275                     if (expected->id != actual->id) dump++;
276                 }
277             }
278 
279             if ((expected->flags & defwinproc) != (actual->flags & defwinproc) && todo)
280             {
281                 todo_wine
282                 {
283                     failcount++;
284                     dump++;
285                     ok_(file, line) (FALSE,
286                         "%s: the msg 0x%04x should %shave been sent by DefWindowProc\n",
287                         context, expected->message, (expected->flags & defwinproc) ? "" : "NOT ");
288                 }
289             }
290             else
291             {
292                 ok_(file, line) ((expected->flags & defwinproc) == (actual->flags & defwinproc),
293                     "%s: the msg 0x%04x should %shave been sent by DefWindowProc\n",
294                     context, expected->message, (expected->flags & defwinproc) ? "" : "NOT ");
295                 if ((expected->flags & defwinproc) != (actual->flags & defwinproc)) dump++;
296             }
297 
298             ok_(file, line) ((expected->flags & beginpaint) == (actual->flags & beginpaint),
299                 "%s: the msg 0x%04x should %shave been sent by BeginPaint\n",
300                 context, expected->message, (expected->flags & beginpaint) ? "" : "NOT ");
301             if ((expected->flags & beginpaint) != (actual->flags & beginpaint)) dump++;
302 
303             ok_(file, line) ((expected->flags & (sent|posted)) == (actual->flags & (sent|posted)),
304                 "%s: the msg 0x%04x should have been %s\n",
305                 context, expected->message, (expected->flags & posted) ? "posted" : "sent");
306             if ((expected->flags & (sent|posted)) != (actual->flags & (sent|posted))) dump++;
307 
308             ok_(file, line) ((expected->flags & parent) == (actual->flags & parent),
309                 "%s: the msg 0x%04x was expected in %s\n",
310                 context, expected->message, (expected->flags & parent) ? "parent" : "child");
311             if ((expected->flags & parent) != (actual->flags & parent)) dump++;
312 
313             ok_(file, line) ((expected->flags & hook) == (actual->flags & hook),
314                 "%s: the msg 0x%04x should have been sent by a hook\n",
315                 context, expected->message);
316             if ((expected->flags & hook) != (actual->flags & hook)) dump++;
317 
318             ok_(file, line) ((expected->flags & winevent_hook) == (actual->flags & winevent_hook),
319                 "%s: the msg 0x%04x should have been sent by a winevent hook\n",
320                 context, expected->message);
321             if ((expected->flags & winevent_hook) != (actual->flags & winevent_hook)) dump++;
322 
323             expected++;
324             actual++;
325         }
326         else if (expected->flags & optional)
327             expected++;
328         else if (todo)
329         {
330             failcount++;
331             dump++;
332             todo_wine
333             {
334                 ok_(file, line) (FALSE, "%s: the msg 0x%04x was expected, but got msg 0x%04x instead\n",
335                     context, expected->message, actual->message);
336             }
337             goto done;
338         }
339         else
340         {
341             ok_(file, line) (FALSE, "%s: the msg 0x%04x was expected, but got msg 0x%04x instead\n",
342                 context, expected->message, actual->message);
343             dump++;
344             expected++;
345             actual++;
346         }
347     }
348 
349     /* skip all optional trailing messages */
350     while (expected->message && ((expected->flags & optional)))
351         expected++;
352 
353     if (todo)
354     {
355         todo_wine
356         {
357             if (expected->message || actual->message)
358             {
359                 failcount++;
360                 dump++;
361                 ok_(file, line) (FALSE, "%s: the msg sequence is not complete: expected %04x - actual %04x\n",
362                     context, expected->message, actual->message);
363             }
364         }
365     }
366     else if (expected->message || actual->message)
367     {
368         dump++;
369         ok_(file, line) (FALSE, "%s: the msg sequence is not complete: expected %04x - actual %04x\n",
370             context, expected->message, actual->message);
371     }
372 
373     if(todo && !failcount) /* succeeded yet marked todo */
374     {
375         if (!strcmp(winetest_platform, "wine")) dump++;
376         todo_wine
377         {
378             ok_(file, line)(TRUE, "%s: marked \"todo_wine\" but succeeds\n", context);
379         }
380     }
381 
382 done:
383     if (dump) dump_sequence( seq, sequence_index, expected_list, context, file, line );
384     flush_sequence(seq, sequence_index);
385 }
386 
387 #define ok_sequence(seq, index, exp, contx, todo) \
388         ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
389 
390 
391 static inline void init_msg_sequences(struct msg_sequence **seq, int n)
392 {
393     int i;
394 
395     for (i = 0; i < n; i++)
396         seq[i] = heap_alloc_zero(sizeof(*seq[i]));
397 }
398