richedit: Implement rule that in 1.0 emulation, a single CR that terminates the text...
[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
217   /* Native richedit 2.0 ignores last \par */
218   es.dwCookie = (DWORD_PTR)&streamText0a;
219   es.dwError = 0;
220   es.pfnCallback = test_EM_STREAMIN_esCallback;
221   SendMessage(hwndRichEdit, EM_STREAMIN,
222               (WPARAM)(SF_RTF), (LPARAM)&es);
223
224   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
225   ok (result  == 12,
226       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
227   result = strcmp (buffer,"TestSomeText");
228   ok (result  == 0,
229       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
230
231   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
232   es.dwCookie = (DWORD_PTR)&streamText0b;
233   es.dwError = 0;
234   es.pfnCallback = test_EM_STREAMIN_esCallback;
235   SendMessage(hwndRichEdit, EM_STREAMIN,
236               (WPARAM)(SF_RTF), (LPARAM)&es);
237
238   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
239   ok (result  == 14,
240       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
241   result = strcmp (buffer,"TestSomeText\r\n");
242   ok (result  == 0,
243       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
244
245   es.dwCookie = (DWORD_PTR)&streamText1;
246   es.dwError = 0;
247   es.pfnCallback = test_EM_STREAMIN_esCallback;
248   SendMessage(hwndRichEdit, EM_STREAMIN,
249               (WPARAM)(SF_RTF), (LPARAM)&es);
250
251   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
252   ok (result  == 12,
253       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
254   result = strcmp (buffer,"TestSomeText");
255   ok (result  == 0,
256       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
257
258
259   es.dwCookie = (DWORD_PTR)&streamText2;
260   SendMessage(hwndRichEdit, EM_STREAMIN,
261               (WPARAM)(SF_RTF), (LPARAM)&es);
262
263   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
264   todo_wine {
265   ok (result  == 9,
266       "EM_STREAMIN: Test 2 returned %ld, expected 9\n", result);
267   }
268   result = strcmp (buffer,"RichEdit1");
269   todo_wine {
270   ok (result  == 0,
271       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
272   }
273
274   es.dwCookie = (DWORD_PTR)&streamText3;
275   SendMessage(hwndRichEdit, EM_STREAMIN,
276               (WPARAM)(SF_RTF), (LPARAM)&es);
277
278   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
279   ok (result  == 0,
280       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
281   ok (strlen(buffer)  == 0,
282       "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
283
284   DestroyWindow(hwndRichEdit);
285 }
286
287 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
288                                          LPBYTE pbBuff,
289                                          LONG cb,
290                                          LONG *pcb)
291 {
292   char** str = (char**)dwCookie;
293   *pcb = cb;
294   if (*pcb > 0) {
295     memcpy(*str, pbBuff, *pcb);
296     *str += *pcb;
297   }
298   return 0;
299 }
300
301 static void test_EM_STREAMOUT(void)
302 {
303   HWND hwndRichEdit = new_richedit(NULL);
304   int r;
305   EDITSTREAM es;
306   char buf[1024] = {0};
307   char * p;
308
309   const char * TestItem1 = "TestSomeText";
310   const char * TestItem2 = "TestSomeText\r";
311   const char * TestItem3 = "TestSomeText\r\n";
312
313   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
314   p = buf;
315   es.dwCookie = (DWORD_PTR)&p;
316   es.dwError = 0;
317   es.pfnCallback = test_WM_SETTEXT_esCallback;
318   memset(buf, 0, sizeof(buf));
319   SendMessage(hwndRichEdit, EM_STREAMOUT,
320               (WPARAM)(SF_TEXT), (LPARAM)&es);
321   r = strlen(buf);
322   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
323   ok(strcmp(buf, TestItem1) == 0,
324         "streamed text different, got %s\n", buf);
325
326   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
327   p = buf;
328   es.dwCookie = (DWORD_PTR)&p;
329   es.dwError = 0;
330   es.pfnCallback = test_WM_SETTEXT_esCallback;
331   memset(buf, 0, sizeof(buf));
332   SendMessage(hwndRichEdit, EM_STREAMOUT,
333               (WPARAM)(SF_TEXT), (LPARAM)&es);
334   r = strlen(buf);
335
336   ok(r == 13, "streamed text length is %d, expecting 13\n", r);
337   ok(strcmp(buf, TestItem2) == 0,
338         "streamed text different, got %s\n", buf);
339
340   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
341   p = buf;
342   es.dwCookie = (DWORD_PTR)&p;
343   es.dwError = 0;
344   es.pfnCallback = test_WM_SETTEXT_esCallback;
345   memset(buf, 0, sizeof(buf));
346   SendMessage(hwndRichEdit, EM_STREAMOUT,
347               (WPARAM)(SF_TEXT), (LPARAM)&es);
348   r = strlen(buf);
349   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
350   ok(strcmp(buf, TestItem3) == 0,
351         "streamed text different, got %s\n", buf);
352
353   DestroyWindow(hwndRichEdit);
354 }
355
356 static const struct getline_s {
357   int line;
358   size_t buffer_len;
359   const char *text;
360   int wine_todo;
361 } gl[] = {
362   {0, 10, "foo bar\r\n", 0},
363   {1, 10, "\n", 0},
364   {2, 10, "bar\n", 0},
365   {3, 10, "\r\n", 0},
366
367   /* Buffer smaller than line length */
368   {0, 2, "foo bar\r", 0},
369   {0, 1, "foo bar\r", 0},
370   {0, 0, "foo bar\r", 0}
371 };
372
373 static void test_EM_GETLINE(void)
374 {
375   int i;
376   HWND hwndRichEdit = new_richedit(NULL);
377   static const int nBuf = 1024;
378   char dest[1024], origdest[1024];
379   const char text[] = "foo bar\r\n"
380       "\n"
381       "bar\n";
382
383   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
384
385   memset(origdest, 0xBB, nBuf);
386   for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
387   {
388     int nCopied;
389     int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
390     int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
391     memset(dest, 0xBB, nBuf);
392     *(WORD *) dest = gl[i].buffer_len;
393
394     /* EM_GETLINE appends a "\r\0" to the end of the line
395      * nCopied counts up to and including the '\r' */
396     nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
397     if (gl[i].wine_todo) todo_wine {
398     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
399        expected_nCopied);
400     } else
401     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
402        expected_nCopied);
403     /* two special cases since a parameter is passed via dest */
404     if (gl[i].buffer_len == 0)
405       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
406          "buffer_len=0\n");
407     else if (gl[i].buffer_len == 1)
408       ok(dest[0] == gl[i].text[0] && !dest[1] &&
409          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
410     else
411     {
412       if (gl[i].wine_todo) todo_wine {
413       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
414          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
415       ok(!strncmp(dest + expected_bytes_written, origdest
416                   + expected_bytes_written, nBuf - expected_bytes_written),
417          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
418       }
419       else
420       {
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     }
428   }
429
430   DestroyWindow(hwndRichEdit);
431 }
432
433 static void test_EM_LINELENGTH(void)
434 {
435   HWND hwndRichEdit = new_richedit(NULL);
436   const char * text =
437         "richedit1\r"
438         "richedit1\n"
439         "richedit1\r\n"
440         "richedit1\r\r\r\r\r\n";
441   int offset_test[10][2] = {
442         {0, 9},
443         {5, 9},
444         {10, 9},
445         {15, 9},
446         {20, 9},
447         {25, 9},
448         {30, 9},
449         {35, 9},
450         {40, 9}, /* <----- in the middle of the \r run, but run not counted */
451         {45, 0},
452   };
453   int i;
454   LRESULT result;
455
456   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
457
458   for (i = 0; i < 10; i++) {
459     result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
460     ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
461         offset_test[i][0], result, offset_test[i][1]);
462   }
463
464   DestroyWindow(hwndRichEdit);
465 }
466
467 static void test_EM_GETTEXTRANGE(void)
468 {
469     HWND hwndRichEdit = new_richedit(NULL);
470     const char * text1 = "foo bar\r\nfoo bar";
471     const char * text2 = "foo bar\rfoo bar";
472     const char * expect1 = "bar\r\nfoo";
473     const char * expect2 = "bar\rfoo";
474     char buffer[1024] = {0};
475     LRESULT result;
476     TEXTRANGEA textRange;
477
478     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
479
480     textRange.lpstrText = buffer;
481     textRange.chrg.cpMin = 4;
482     textRange.chrg.cpMax = 12;
483     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
484     ok(result == 8, "EM_GETTEXTRANGE returned %ld, expected %d\n",
485         result, strlen(expect1));
486     ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
487
488     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
489
490     textRange.lpstrText = buffer;
491     textRange.chrg.cpMin = 4;
492     textRange.chrg.cpMax = 11;
493     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
494     ok(result == 7, "EM_GETTEXTRANGE returned %ld, expected %d\n",
495         result, strlen(expect2));
496
497     ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
498
499
500     DestroyWindow(hwndRichEdit);
501 }
502
503 static void test_EM_GETSELTEXT(void)
504 {
505     HWND hwndRichEdit = new_richedit(NULL);
506     const char * text1 = "foo bar\r\nfoo bar";
507     const char * text2 = "foo bar\rfoo bar";
508     const char * expect1 = "bar\r\nfoo";
509     const char * expect2 = "bar\rfoo";
510     char buffer[1024] = {0};
511     LRESULT result;
512
513     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
514
515     SendMessage(hwndRichEdit, EM_SETSEL, 4, 12);
516     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
517     ok(result == 8, "EM_GETTEXTRANGE returned %ld, expected %d\n",
518         result, strlen(expect1));
519     ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
520
521     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
522
523     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
524     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
525     ok(result == 7, "EM_GETTEXTRANGE returned %ld, expected %d\n",
526         result, strlen(expect2));
527
528     ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
529
530
531     DestroyWindow(hwndRichEdit);
532 }
533
534 START_TEST( editor )
535 {
536   MSG msg;
537   time_t end;
538
539   /* Must explicitly LoadLibrary(). The test has no references to functions in
540    * RICHED32.DLL, so the linker doesn't actually link to it. */
541   hmoduleRichEdit = LoadLibrary("RICHED32.DLL");
542   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
543
544   test_WM_SETTEXT();
545   test_EM_GETTEXTRANGE();
546   test_EM_GETSELTEXT();
547   test_WM_GETTEXTLENGTH();
548   test_EM_STREAMIN();
549   test_EM_STREAMOUT();
550   test_EM_GETLINE();
551   test_EM_LINELENGTH();
552
553   /* Set the environment variable WINETEST_RICHED32 to keep windows
554    * responsive and open for 30 seconds. This is useful for debugging.
555    *
556    * The message pump uses PeekMessage() to empty the queue and then sleeps for
557    * 50ms before retrying the queue. */
558   end = time(NULL) + 30;
559   if (getenv( "WINETEST_RICHED32" )) {
560     while (time(NULL) < end) {
561       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
562         TranslateMessage(&msg);
563         DispatchMessage(&msg);
564       } else {
565         Sleep(50);
566       }
567     }
568   }
569
570   OleFlushClipboard();
571   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
572 }