jscript: Added SCRIPTITEM_ISVISIBLE flag implementation.
[wine] / dlls / riched32 / tests / editor.c
1 /*
2 * Unit test suite for rich edit control 1.0
3 *
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
7 * Copyright 2007 Alex VillacĂ­s Lasso
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24 #include <stdarg.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <time.h>
34 #include <wine/test.h>
35
36 static HMODULE hmoduleRichEdit;
37
38 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
39   HWND hwnd;
40   hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
41                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
42                       hmoduleRichEdit, NULL);
43   ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
44   return hwnd;
45 }
46
47 static HWND new_richedit(HWND parent) {
48   return new_window(RICHEDIT_CLASS10A, ES_MULTILINE, parent);
49 }
50
51 static void test_WM_SETTEXT()
52 {
53   HWND hwndRichEdit = new_richedit(NULL);
54   const char * TestItem1 = "TestSomeText";
55   const char * TestItem2 = "TestSomeText\r";
56   const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
57   const char * TestItem4 = "TestSomeText\n\nTestSomeText";
58   const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
59   const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
60   const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
61   const char * TestItem8 = "TestSomeText\r\n";
62   const char * TestItem9 = "TestSomeText\r\nSomeMoreText\r\n";
63   const char * TestItem10 = "TestSomeText\r\n\r\nTestSomeText";
64   const char * TestItem11 = "TestSomeText TestSomeText";
65   const char * TestItem12 = "TestSomeText \r\nTestSomeText";
66   const char * TestItem13 = "TestSomeText\r\n \r\nTestSomeText";
67   const char * TestItem14 = "TestSomeText\n";
68   const char * TestItem15 = "TestSomeText\r\r\r";
69   const char * TestItem16 = "TestSomeText\r\r\rSomeMoreText";
70   char buf[1024] = {0};
71   LRESULT result;
72
73   /* This test attempts to show that WM_SETTEXT on a riched32 control does not
74      attempt to modify the text that is pasted into the control, and should
75      return it as is. In particular, \r\r\n is NOT converted, unlike riched20.
76      Currently, builtin riched32 mangles solitary \r or \n when not part of
77      a \r\n pair.
78
79      For riched32, the rules for breaking lines seem to be the following:
80      - \r\n is one line break. This is the normal case.
81      - \r{0,N}\n is one line break. In particular, \n by itself is a line break.
82      - \n{1,N} are that many line breaks.
83      - \r with text or other characters (except \n) past it, is a line break. That
84        is, a run of \r{N} without a terminating \n is considered N line breaks
85      - \r at the end of the text is NOT a line break. This differs from riched20,
86        where \r at the end of the text is a proper line break. This causes
87        TestItem2 to fail its test.
88    */
89
90 #define TEST_SETTEXT(a, b, nlines, is_todo, is_todo2) \
91   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
92   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
93   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
94   ok (result == lstrlen(buf), \
95         "WM_GETTEXT returned %ld instead of expected %u\n", \
96         result, lstrlen(buf)); \
97   result = strcmp(b, buf); \
98   if (is_todo) todo_wine { \
99   ok(result == 0, \
100         "WM_SETTEXT round trip: strcmp = %ld\n", result); \
101   } else { \
102   ok(result == 0, \
103         "WM_SETTEXT round trip: strcmp = %ld\n", result); \
104   } \
105   result = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0); \
106   if (is_todo2) todo_wine { \
107   ok(result == nlines, "EM_GETLINECOUNT returned %ld, expected %d\n", result, nlines); \
108   } else { \
109   ok(result == nlines, "EM_GETLINECOUNT returned %ld, expected %d\n", result, nlines); \
110   }
111
112   TEST_SETTEXT(TestItem1, TestItem1, 1, 0, 0)
113   TEST_SETTEXT(TestItem2, TestItem2, 1, 0, 0)
114   TEST_SETTEXT(TestItem3, TestItem3, 2, 0, 0)
115   TEST_SETTEXT(TestItem4, TestItem4, 3, 0, 0)
116   TEST_SETTEXT(TestItem5, TestItem5, 2, 0, 0)
117   TEST_SETTEXT(TestItem6, TestItem6, 3, 0, 0)
118   TEST_SETTEXT(TestItem7, TestItem7, 4, 0, 0)
119   TEST_SETTEXT(TestItem8, TestItem8, 2, 0, 0)
120   TEST_SETTEXT(TestItem9, TestItem9, 3, 0, 0)
121   TEST_SETTEXT(TestItem10, TestItem10, 3, 0, 0)
122   TEST_SETTEXT(TestItem11, TestItem11, 1, 0, 0)
123   TEST_SETTEXT(TestItem12, TestItem12, 2, 0, 0)
124   TEST_SETTEXT(TestItem13, TestItem13, 3, 0, 0)
125   TEST_SETTEXT(TestItem14, TestItem14, 2, 0, 0)
126   TEST_SETTEXT(TestItem15, TestItem15, 3, 0, 0)
127   TEST_SETTEXT(TestItem16, TestItem16, 4, 0, 0)
128
129 #undef TEST_SETTEXT
130   DestroyWindow(hwndRichEdit);
131 }
132
133 static void test_WM_GETTEXTLENGTH(void)
134 {
135     HWND hwndRichEdit = new_richedit(NULL);
136     static const char text3[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
137     static const char text4[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
138     int result;
139
140     /* Test for WM_GETTEXTLENGTH */
141     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text3);
142     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
143     ok(result == lstrlen(text3),
144         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
145         result, lstrlen(text3));
146
147     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text4);
148     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
149     ok(result == lstrlen(text4),
150         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
151         result, lstrlen(text4));
152
153     DestroyWindow(hwndRichEdit);
154 }
155
156 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
157                                          LPBYTE pbBuff,
158                                          LONG cb,
159                                          LONG *pcb)
160 {
161   const char** str = (const char**)dwCookie;
162   int size = strlen(*str);
163   *pcb = cb;
164   if (*pcb > size) {
165     *pcb = size;
166   }
167   if (*pcb > 0) {
168     memcpy(pbBuff, *str, *pcb);
169     *str += *pcb;
170   }
171   return 0;
172 }
173
174
175 static void test_EM_STREAMIN(void)
176 {
177   HWND hwndRichEdit = new_richedit(NULL);
178   LRESULT result;
179   EDITSTREAM es;
180   char buffer[1024] = {0};
181
182   const char * streamText0 = "{\\rtf1 TestSomeText}";
183   const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
184   const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
185
186   const char * streamText1 =
187   "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
188   "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
189   "}\r\n";
190
191   /* This should be accepted in richedit 1.0 emulation. See bug #8326 */
192   const char * streamText2 =
193     "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
194     "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
195     "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
196     "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
197     "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
198     "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
199     "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
200
201   const char * streamText3 = "RichEdit1";
202
203   /* Minimal test without \par at the end */
204   es.dwCookie = (DWORD_PTR)&streamText0;
205   es.dwError = 0;
206   es.pfnCallback = test_EM_STREAMIN_esCallback;
207   SendMessage(hwndRichEdit, EM_STREAMIN,
208               (WPARAM)(SF_RTF), (LPARAM)&es);
209
210   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
211   ok (result  == 12,
212       "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
213   result = strcmp (buffer,"TestSomeText");
214   ok (result  == 0,
215       "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
216   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
217
218   /* Native richedit 2.0 ignores last \par */
219   es.dwCookie = (DWORD_PTR)&streamText0a;
220   es.dwError = 0;
221   es.pfnCallback = test_EM_STREAMIN_esCallback;
222   SendMessage(hwndRichEdit, EM_STREAMIN,
223               (WPARAM)(SF_RTF), (LPARAM)&es);
224
225   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
226   ok (result  == 12,
227       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
228   result = strcmp (buffer,"TestSomeText");
229   ok (result  == 0,
230       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
231   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
232
233   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
234   es.dwCookie = (DWORD_PTR)&streamText0b;
235   es.dwError = 0;
236   es.pfnCallback = test_EM_STREAMIN_esCallback;
237   SendMessage(hwndRichEdit, EM_STREAMIN,
238               (WPARAM)(SF_RTF), (LPARAM)&es);
239
240   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
241   ok (result  == 14,
242       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
243   result = strcmp (buffer,"TestSomeText\r\n");
244   ok (result  == 0,
245       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
246   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
247
248   es.dwCookie = (DWORD_PTR)&streamText1;
249   es.dwError = 0;
250   es.pfnCallback = test_EM_STREAMIN_esCallback;
251   SendMessage(hwndRichEdit, EM_STREAMIN,
252               (WPARAM)(SF_RTF), (LPARAM)&es);
253
254   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
255   ok (result  == 12,
256       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
257   result = strcmp (buffer,"TestSomeText");
258   ok (result  == 0,
259       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
260   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
261
262
263   es.dwCookie = (DWORD_PTR)&streamText2;
264   es.dwError = 0;
265   SendMessage(hwndRichEdit, EM_STREAMIN,
266               (WPARAM)(SF_RTF), (LPARAM)&es);
267
268   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
269   todo_wine {
270   ok (result  == 9,
271       "EM_STREAMIN: Test 2 returned %ld, expected 9\n", result);
272   }
273   result = strcmp (buffer,"RichEdit1");
274   todo_wine {
275   ok (result  == 0,
276       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
277   }
278   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
279
280   es.dwCookie = (DWORD_PTR)&streamText3;
281   es.dwError = 0;
282   SendMessage(hwndRichEdit, EM_STREAMIN,
283               (WPARAM)(SF_RTF), (LPARAM)&es);
284
285   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
286   ok (result  == 0,
287       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
288   ok (strlen(buffer)  == 0,
289       "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
290   ok(es.dwError == -16, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, -16);
291
292   DestroyWindow(hwndRichEdit);
293 }
294
295 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
296                                          LPBYTE pbBuff,
297                                          LONG cb,
298                                          LONG *pcb)
299 {
300   char** str = (char**)dwCookie;
301   *pcb = cb;
302   if (*pcb > 0) {
303     memcpy(*str, pbBuff, *pcb);
304     *str += *pcb;
305   }
306   return 0;
307 }
308
309 static void test_EM_STREAMOUT(void)
310 {
311   HWND hwndRichEdit = new_richedit(NULL);
312   int r;
313   EDITSTREAM es;
314   char buf[1024] = {0};
315   char * p;
316
317   const char * TestItem1 = "TestSomeText";
318   const char * TestItem2 = "TestSomeText\r";
319   const char * TestItem3 = "TestSomeText\r\n";
320
321   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
322   p = buf;
323   es.dwCookie = (DWORD_PTR)&p;
324   es.dwError = 0;
325   es.pfnCallback = test_WM_SETTEXT_esCallback;
326   memset(buf, 0, sizeof(buf));
327   SendMessage(hwndRichEdit, EM_STREAMOUT,
328               (WPARAM)(SF_TEXT), (LPARAM)&es);
329   r = strlen(buf);
330   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
331   ok(strcmp(buf, TestItem1) == 0,
332         "streamed text different, got %s\n", buf);
333
334   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
335   p = buf;
336   es.dwCookie = (DWORD_PTR)&p;
337   es.dwError = 0;
338   es.pfnCallback = test_WM_SETTEXT_esCallback;
339   memset(buf, 0, sizeof(buf));
340   SendMessage(hwndRichEdit, EM_STREAMOUT,
341               (WPARAM)(SF_TEXT), (LPARAM)&es);
342   r = strlen(buf);
343
344   ok(r == 13, "streamed text length is %d, expecting 13\n", r);
345   ok(strcmp(buf, TestItem2) == 0,
346         "streamed text different, got %s\n", buf);
347
348   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
349   p = buf;
350   es.dwCookie = (DWORD_PTR)&p;
351   es.dwError = 0;
352   es.pfnCallback = test_WM_SETTEXT_esCallback;
353   memset(buf, 0, sizeof(buf));
354   SendMessage(hwndRichEdit, EM_STREAMOUT,
355               (WPARAM)(SF_TEXT), (LPARAM)&es);
356   r = strlen(buf);
357   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
358   ok(strcmp(buf, TestItem3) == 0,
359         "streamed text different, got %s\n", buf);
360
361   DestroyWindow(hwndRichEdit);
362 }
363
364 static const struct getline_s {
365   int line;
366   size_t buffer_len;
367   const char *text;
368   int wine_todo;
369 } gl[] = {
370   {0, 10, "foo bar\r\n", 0},
371   {1, 10, "\n", 0},
372   {2, 10, "bar\n", 0},
373   {3, 10, "\r\n", 0},
374
375   /* Buffer smaller than line length */
376   {0, 2, "foo bar\r", 0},
377   {0, 1, "foo bar\r", 0},
378   {0, 0, "foo bar\r", 0}
379 };
380
381 static void test_EM_GETLINE(void)
382 {
383   int i;
384   HWND hwndRichEdit = new_richedit(NULL);
385   static const int nBuf = 1024;
386   char dest[1024], origdest[1024];
387   const char text[] = "foo bar\r\n"
388       "\n"
389       "bar\n";
390
391   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
392
393   memset(origdest, 0xBB, nBuf);
394   for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
395   {
396     int nCopied;
397     int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
398     int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
399     memset(dest, 0xBB, nBuf);
400     *(WORD *) dest = gl[i].buffer_len;
401
402     /* EM_GETLINE appends a "\r\0" to the end of the line
403      * nCopied counts up to and including the '\r' */
404     nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
405     if (gl[i].wine_todo) todo_wine {
406     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
407        expected_nCopied);
408     } else
409     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
410        expected_nCopied);
411     /* two special cases since a parameter is passed via dest */
412     if (gl[i].buffer_len == 0)
413       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
414          "buffer_len=0\n");
415     else if (gl[i].buffer_len == 1)
416       ok(dest[0] == gl[i].text[0] && !dest[1] &&
417          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
418     else
419     {
420       if (gl[i].wine_todo) todo_wine {
421       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
422          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
423       ok(!strncmp(dest + expected_bytes_written, origdest
424                   + expected_bytes_written, nBuf - expected_bytes_written),
425          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
426       }
427       else
428       {
429       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
430          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
431       ok(!strncmp(dest + expected_bytes_written, origdest
432                   + expected_bytes_written, nBuf - expected_bytes_written),
433          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
434       }
435     }
436   }
437
438   DestroyWindow(hwndRichEdit);
439 }
440
441 static void test_EM_LINELENGTH(void)
442 {
443   HWND hwndRichEdit = new_richedit(NULL);
444   const char * text =
445         "richedit1\r"
446         "richedit1\n"
447         "richedit1\r\n"
448         "richedit1\r\r\r\r\r\n";
449   int offset_test[10][2] = {
450         {0, 9},
451         {5, 9},
452         {10, 9},
453         {15, 9},
454         {20, 9},
455         {25, 9},
456         {30, 9},
457         {35, 9},
458         {40, 9}, /* <----- in the middle of the \r run, but run not counted */
459         {45, 0},
460   };
461   int i;
462   LRESULT result;
463
464   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
465
466   for (i = 0; i < 10; i++) {
467     result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
468     ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
469         offset_test[i][0], result, offset_test[i][1]);
470   }
471
472   DestroyWindow(hwndRichEdit);
473 }
474
475 static void test_EM_GETTEXTRANGE(void)
476 {
477     HWND hwndRichEdit = new_richedit(NULL);
478     const char * text1 = "foo bar\r\nfoo bar";
479     const char * text2 = "foo bar\rfoo bar";
480     const char * expect1 = "bar\r\nfoo";
481     const char * expect2 = "bar\rfoo";
482     char buffer[1024] = {0};
483     LRESULT result;
484     TEXTRANGEA textRange;
485
486     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
487
488     textRange.lpstrText = buffer;
489     textRange.chrg.cpMin = 4;
490     textRange.chrg.cpMax = 12;
491     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
492     ok(result == 8, "EM_GETTEXTRANGE returned %ld\n", result);
493     ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
494
495     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
496
497     textRange.lpstrText = buffer;
498     textRange.chrg.cpMin = 4;
499     textRange.chrg.cpMax = 11;
500     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
501     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
502
503     ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
504
505
506     DestroyWindow(hwndRichEdit);
507 }
508
509 static void test_EM_GETSELTEXT(void)
510 {
511     HWND hwndRichEdit = new_richedit(NULL);
512     const char * text1 = "foo bar\r\nfoo bar";
513     const char * text2 = "foo bar\rfoo bar";
514     const char * expect1 = "bar\r\nfoo";
515     const char * expect2 = "bar\rfoo";
516     char buffer[1024] = {0};
517     LRESULT result;
518
519     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
520
521     SendMessage(hwndRichEdit, EM_SETSEL, 4, 12);
522     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
523     ok(result == 8, "EM_GETTEXTRANGE returned %ld\n", result);
524     ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
525
526     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
527
528     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
529     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
530     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
531
532     ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
533
534
535     DestroyWindow(hwndRichEdit);
536 }
537
538 static const char haystack[] = "WINEWine wineWine wine WineWine";
539                              /* ^0        ^10       ^20       ^30 */
540
541 struct find_s {
542   int start;
543   int end;
544   const char *needle;
545   int flags;
546   int expected_loc;
547   int _todo_wine;
548 };
549
550
551 struct find_s find_tests[] = {
552   /* Find in empty text */
553   {0, -1, "foo", FR_DOWN, -1, 0},
554   {0, -1, "foo", 0, -1, 0},
555   {0, -1, "", FR_DOWN, -1, 0},
556   {20, 5, "foo", FR_DOWN, -1, 0},
557   {5, 20, "foo", FR_DOWN, -1, 0}
558 };
559
560 struct find_s find_tests2[] = {
561   /* No-result find */
562   {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
563   {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
564
565   /* Subsequent finds */
566   {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
567   {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
568   {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
569   {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
570
571   /* Find backwards */
572   {19, 20, "Wine", FR_MATCHCASE, -1, 0},
573   {10, 20, "Wine", FR_MATCHCASE, 13, 0},
574   {20, 10, "Wine", FR_MATCHCASE, -1, 0},
575
576   /* Case-insensitive */
577   {1, 31, "wInE", FR_DOWN, 4, 0},
578   {1, 31, "Wine", FR_DOWN, 4, 0},
579
580   /* High-to-low ranges */
581   {20, 5, "Wine", FR_DOWN, -1, 0},
582   {2, 1, "Wine", FR_DOWN, -1, 0},
583   {30, 29, "Wine", FR_DOWN, -1, 0},
584   {20, 5, "Wine", 0, /*13*/ -1, 0},
585
586   /* Find nothing */
587   {5, 10, "", FR_DOWN, -1, 0},
588   {10, 5, "", FR_DOWN, -1, 0},
589   {0, -1, "", FR_DOWN, -1, 0},
590   {10, 5, "", 0, -1, 0},
591
592   /* Whole-word search */
593   {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
594   {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
595   {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
596   {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
597   {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
598   {11, -1, "winewine", FR_WHOLEWORD, 23, 0},
599   {31, -1, "winewine", FR_WHOLEWORD, -1, 0},
600
601   /* Bad ranges */
602   {5, 200, "XXX", FR_DOWN, -1, 0},
603   {-20, 20, "Wine", FR_DOWN, -1, 0},
604   {-20, 20, "Wine", FR_DOWN, -1, 0},
605   {-15, -20, "Wine", FR_DOWN, -1, 0},
606   {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
607
608   /* Check the case noted in bug 4479 where matches at end aren't recognized */
609   {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
610   {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
611   {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
612   {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
613   {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
614
615   /* The backwards case of bug 4479; bounds look right
616    * Fails because backward find is wrong */
617   {19, 20, "WINE", FR_MATCHCASE, -1, 0},
618   {0, 20, "WINE", FR_MATCHCASE, 0, 0},
619
620   {0, -1, "wineWine wine", FR_DOWN, 0, 0},
621   {0, -1, "wineWine wine", 0, 0, 0},
622   {0, -1, "INEW", 0, 1, 0},
623   {0, 31, "INEW", 0, 1, 0},
624   {4, -1, "INEW", 0, 10, 0},
625 };
626
627 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
628   int findloc;
629   FINDTEXT ft;
630   memset(&ft, 0, sizeof(ft));
631   ft.chrg.cpMin = f->start;
632   ft.chrg.cpMax = f->end;
633   ft.lpstrText = f->needle;
634   findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
635   ok(findloc == f->expected_loc,
636      "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
637      name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
638 }
639
640 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
641     int id) {
642   int findloc;
643   FINDTEXTEX ft;
644   int expected_end_loc;
645
646   memset(&ft, 0, sizeof(ft));
647   ft.chrg.cpMin = f->start;
648   ft.chrg.cpMax = f->end;
649   ft.lpstrText = f->needle;
650   findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
651   ok(findloc == f->expected_loc,
652       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
653       name, id, f->needle, f->start, f->end, f->flags, findloc);
654   ok(ft.chrgText.cpMin == f->expected_loc,
655       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d, expected %d\n",
656       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin, f->expected_loc);
657   expected_end_loc = ((f->expected_loc == -1) ? -1
658         : f->expected_loc + strlen(f->needle));
659   ok(ft.chrgText.cpMax == expected_end_loc,
660       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
661       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
662 }
663
664 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
665     int num_tests)
666 {
667   int i;
668
669   for (i = 0; i < num_tests; i++) {
670     if (find[i]._todo_wine) {
671       todo_wine {
672         check_EM_FINDTEXT(hwnd, name, &find[i], i);
673         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
674       }
675     } else {
676         check_EM_FINDTEXT(hwnd, name, &find[i], i);
677         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
678     }
679   }
680 }
681
682 static void test_EM_FINDTEXT(void)
683 {
684   HWND hwndRichEdit = new_richedit(NULL);
685
686   /* Empty rich edit control */
687   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
688       sizeof(find_tests)/sizeof(struct find_s));
689
690   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
691
692   /* Haystack text */
693   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
694       sizeof(find_tests2)/sizeof(struct find_s));
695
696   DestroyWindow(hwndRichEdit);
697 }
698
699 static void test_EM_POSFROMCHAR(void)
700 {
701   HWND hwndRichEdit = new_richedit(NULL);
702   int i;
703   POINTL pl;
704   LRESULT result;
705   unsigned int height = 0;
706   int xpos = 0;
707   static const char text[] = "aa\n"
708       "this is a long line of text that should be longer than the "
709       "control's width\n"
710       "cc\n"
711       "dd\n"
712       "ee\n"
713       "ff\n"
714       "gg\n"
715       "hh\n";
716
717   /* Fill the control to lines to ensure that most of them are offscreen */
718   for (i = 0; i < 50; i++)
719   {
720     /* Do not modify the string; it is exactly 16 characters long. */
721     SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
722     SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCD\r\n");
723   }
724
725   /*
726    Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
727    Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
728    Richedit 3.0 accepts either of the above API conventions.
729    */
730
731   /* Testing Richedit 1.0 API format */
732
733   /* Testing start of lines. X-offset should be constant on all cases (native is 1).
734      Since all lines are identical and drawn with the same font,
735      they should have the same height... right?
736    */
737   for (i = 0; i < 50; i++)
738   {
739     /* All the lines are 16 characters long */
740     result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
741     ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
742     if (i == 0)
743     {
744       ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
745       ok(pl.x == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
746       xpos = pl.x;
747     }
748     else if (i == 1)
749     {
750       ok(pl.y > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", pl.y);
751       ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
752       height = pl.y;
753     }
754     else
755     {
756       ok(pl.y == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, i * height);
757       ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
758     }
759   }
760
761   /* Testing position at end of text */
762   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
763   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
764   ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
765   ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
766
767   /* Testing position way past end of text */
768   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
769   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
770   ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
771   ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
772
773
774   /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
775   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
776   for (i = 0; i < 50; i++)
777   {
778     /* All the lines are 16 characters long */
779     result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
780     ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
781     ok(pl.y == (i - 1) * height,
782         "EM_POSFROMCHAR reports y=%d, expected %d\n",
783         pl.y, (i - 1) * height);
784     ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
785   }
786
787   /* Testing position at end of text */
788   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
789   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
790   ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
791   ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
792
793   /* Testing position way past end of text */
794   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
795   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
796   ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
797   ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
798
799   /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
800   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
801   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
802
803   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
804   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
805   ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
806   ok(pl.x == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
807   xpos = pl.x;
808
809   SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
810   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
811   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
812   ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
813   todo_wine {
814   /* Fails on builtin because horizontal scrollbar is not being shown */
815   ok(pl.x < xpos, "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n", pl.x, xpos);
816   }
817   DestroyWindow(hwndRichEdit);
818 }
819
820 static void test_word_wrap(void)
821 {
822     HWND hwnd;
823     POINTL point = {0, 60}; /* This point must be below the first line */
824     const char *text = "Must be long enough to test line wrapping";
825     DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
826     int res, pos, lines;
827
828     /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
829      * when specified on window creation and set later. */
830     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
831                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
832     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
833     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
834     ok(res, "WM_SETTEXT failed.\n");
835     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
836     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
837     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
838     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
839
840     SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
841     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
842     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
843     DestroyWindow(hwnd);
844
845     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle|WS_HSCROLL,
846                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
847     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
848
849     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
850     ok(res, "WM_SETTEXT failed.\n");
851     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
852     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
853     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
854     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
855
856     SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
857     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
858     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
859     DestroyWindow(hwnd);
860
861     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
862                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
863     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
864     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
865     ok(res, "WM_SETTEXT failed.\n");
866     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
867     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
868
869     SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle);
870     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
871     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
872     DestroyWindow(hwnd);
873
874     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL,
875                         dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
876                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
877     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
878     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
879     ok(res, "WM_SETTEXT failed.\n");
880     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
881     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
882
883     SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle);
884     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
885     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
886
887     /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
888     res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
889     todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
890     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
891     ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
892
893     res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
894     todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
895     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
896     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
897     DestroyWindow(hwnd);
898
899     /* Test to see if wrapping happens with redraw disabled. */
900     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
901                         0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
902     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
903     ok(IsWindowVisible(hwnd), "Window should be visible.\n");
904     SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
905     /* redraw is disabled by making the window invisible. */
906     ok(!IsWindowVisible(hwnd), "Window shouldn't be visible.\n");
907     res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
908     ok(res, "EM_REPLACESEL failed.\n");
909     MoveWindow(hwnd, 0, 0, 100, 80, TRUE);
910     SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
911     /* Wrapping didn't happen while redraw was disabled. */
912     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
913     todo_wine ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
914     /* There isn't even a rewrap from resizing the window. */
915     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
916     todo_wine ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
917     res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
918     ok(res, "EM_REPLACESEL failed.\n");
919     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
920     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
921
922     DestroyWindow(hwnd);
923 }
924
925
926 START_TEST( editor )
927 {
928   MSG msg;
929   time_t end;
930
931   /* Must explicitly LoadLibrary(). The test has no references to functions in
932    * RICHED32.DLL, so the linker doesn't actually link to it. */
933   hmoduleRichEdit = LoadLibrary("RICHED32.DLL");
934   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
935
936   test_WM_SETTEXT();
937   test_EM_GETTEXTRANGE();
938   test_EM_GETSELTEXT();
939   test_WM_GETTEXTLENGTH();
940   test_EM_STREAMIN();
941   test_EM_STREAMOUT();
942   test_EM_GETLINE();
943   test_EM_LINELENGTH();
944   test_EM_FINDTEXT();
945   test_EM_POSFROMCHAR();
946   test_word_wrap();
947
948   /* Set the environment variable WINETEST_RICHED32 to keep windows
949    * responsive and open for 30 seconds. This is useful for debugging.
950    *
951    * The message pump uses PeekMessage() to empty the queue and then sleeps for
952    * 50ms before retrying the queue. */
953   end = time(NULL) + 30;
954   if (getenv( "WINETEST_RICHED32" )) {
955     while (time(NULL) < end) {
956       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
957         TranslateMessage(&msg);
958         DispatchMessage(&msg);
959       } else {
960         Sleep(50);
961       }
962     }
963   }
964
965   OleFlushClipboard();
966   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
967 }