ole32: Fix memory leaks in the storage test.
[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(void)
52 {
53   static const struct {
54     const char *itemtext;
55     DWORD lines;
56     DWORD lines_broken;
57   } testitems[] = {
58     { "TestSomeText", 1},
59     { "TestSomeText\r", 1},
60     { "TestSomeText\rSomeMoreText\r", 2, 1}, /* NT4 and below */
61     { "TestSomeText\n\nTestSomeText", 3},
62     { "TestSomeText\r\r\nTestSomeText", 2},
63     { "TestSomeText\r\r\n\rTestSomeText", 3, 2}, /* NT4 and below */
64     { "TestSomeText\r\n\r\r\n\rTestSomeText", 4, 3}, /* NT4 and below */
65     { "TestSomeText\r\n" ,2},
66     { "TestSomeText\r\nSomeMoreText\r\n", 3},
67     { "TestSomeText\r\n\r\nTestSomeText", 3},
68     { "TestSomeText TestSomeText" ,1},
69     { "TestSomeText \r\nTestSomeText", 2},
70     { "TestSomeText\r\n \r\nTestSomeText", 3},
71     { "TestSomeText\n", 2},
72     { "TestSomeText\r\r\r", 3, 1}, /* NT4 and below */
73     { "TestSomeText\r\r\rSomeMoreText", 4, 2} /* NT4 and below */
74   };
75   HWND hwndRichEdit = new_richedit(NULL);
76   int i;
77
78   /* This test attempts to show that WM_SETTEXT on a riched32 control does not
79    * attempt to modify the text that is pasted into the control, and should
80    * return it as is. In particular, \r\r\n is NOT converted, unlike riched20.
81    *
82    * For riched32, the rules for breaking lines seem to be the following:
83    * - \r\n is one line break. This is the normal case.
84    * - \r{0,2}\n is one line break. In particular, \n by itself is a line break.
85    * - \r{0,N-1}\r\r\n is N line breaks.
86    * - \n{1,N} are that many line breaks.
87    * - \r with text or other characters (except \n) past it, is a line break. That
88    *   is, a run of \r{N} without a terminating \n is considered N line breaks
89    * - \r at the end of the text is NOT a line break. This differs from riched20,
90    *   where \r at the end of the text is a proper line break.
91    */
92
93   for (i = 0; i < sizeof(testitems)/sizeof(testitems[0]); i++) {
94
95     char buf[1024] = {0};
96     LRESULT result;
97
98     result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) testitems[i].itemtext);
99     ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
100     result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
101     ok (result == lstrlen(buf),
102         "[%d] WM_GETTEXT returned %ld instead of expected %u\n",
103         i, result, lstrlen(buf));
104     result = strcmp(testitems[i].itemtext, buf);
105     ok (result == 0,
106         "[%d] WM_SETTEXT round trip: strcmp = %ld\n", i, result);
107     result = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
108     ok (result == testitems[i].lines ||
109         broken(testitems[i].lines_broken && result == testitems[i].lines_broken),
110         "[%d] EM_GETLINECOUNT returned %ld, expected %d\n", i, result, testitems[i].lines);
111   }
112
113   DestroyWindow(hwndRichEdit);
114 }
115
116 static void test_WM_GETTEXTLENGTH(void)
117 {
118     HWND hwndRichEdit = new_richedit(NULL);
119     static const char text3[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
120     static const char text4[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
121     int result;
122
123     /* Test for WM_GETTEXTLENGTH */
124     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text3);
125     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
126     ok(result == lstrlen(text3),
127         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
128         result, lstrlen(text3));
129
130     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text4);
131     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
132     ok(result == lstrlen(text4),
133         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
134         result, lstrlen(text4));
135
136     DestroyWindow(hwndRichEdit);
137 }
138
139 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
140                                          LPBYTE pbBuff,
141                                          LONG cb,
142                                          LONG *pcb)
143 {
144   const char** str = (const char**)dwCookie;
145   int size = strlen(*str);
146   *pcb = cb;
147   if (*pcb > size) {
148     *pcb = size;
149   }
150   if (*pcb > 0) {
151     memcpy(pbBuff, *str, *pcb);
152     *str += *pcb;
153   }
154   return 0;
155 }
156
157
158 static void test_EM_STREAMIN(void)
159 {
160   HWND hwndRichEdit = new_richedit(NULL);
161   LRESULT result;
162   EDITSTREAM es;
163   char buffer[1024] = {0};
164
165   const char * streamText0 = "{\\rtf1 TestSomeText}";
166   const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
167   const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
168
169   const char * streamText1 =
170   "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
171   "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
172   "}\r\n";
173
174   /* This should be accepted in richedit 1.0 emulation. See bug #8326 */
175   const char * streamText2 =
176     "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
177     "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
178     "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
179     "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
180     "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
181     "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
182     "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
183
184   const char * streamText3 = "RichEdit1";
185
186   /* Minimal test without \par at the end */
187   es.dwCookie = (DWORD_PTR)&streamText0;
188   es.dwError = 0;
189   es.pfnCallback = test_EM_STREAMIN_esCallback;
190   SendMessage(hwndRichEdit, EM_STREAMIN,
191               (WPARAM)(SF_RTF), (LPARAM)&es);
192
193   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
194   ok (result  == 12,
195       "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
196   result = strcmp (buffer,"TestSomeText");
197   ok (result  == 0,
198       "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
199   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
200
201   /* Native richedit 2.0 ignores last \par */
202   es.dwCookie = (DWORD_PTR)&streamText0a;
203   es.dwError = 0;
204   es.pfnCallback = test_EM_STREAMIN_esCallback;
205   SendMessage(hwndRichEdit, EM_STREAMIN,
206               (WPARAM)(SF_RTF), (LPARAM)&es);
207
208   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
209   ok (result  == 12,
210       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
211   result = strcmp (buffer,"TestSomeText");
212   ok (result  == 0,
213       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
214   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
215
216   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
217   es.dwCookie = (DWORD_PTR)&streamText0b;
218   es.dwError = 0;
219   es.pfnCallback = test_EM_STREAMIN_esCallback;
220   SendMessage(hwndRichEdit, EM_STREAMIN,
221               (WPARAM)(SF_RTF), (LPARAM)&es);
222
223   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
224   ok (result  == 14,
225       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
226   result = strcmp (buffer,"TestSomeText\r\n");
227   ok (result  == 0,
228       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
229   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
230
231   es.dwCookie = (DWORD_PTR)&streamText1;
232   es.dwError = 0;
233   es.pfnCallback = test_EM_STREAMIN_esCallback;
234   SendMessage(hwndRichEdit, EM_STREAMIN,
235               (WPARAM)(SF_RTF), (LPARAM)&es);
236
237   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
238   ok (result  == 12,
239       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
240   result = strcmp (buffer,"TestSomeText");
241   ok (result  == 0,
242       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
243   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
244
245
246   es.dwCookie = (DWORD_PTR)&streamText2;
247   es.dwError = 0;
248   SendMessage(hwndRichEdit, EM_STREAMIN,
249               (WPARAM)(SF_RTF), (LPARAM)&es);
250
251   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
252   todo_wine {
253   ok (result  == 9,
254       "EM_STREAMIN: Test 2 returned %ld, expected 9\n", result);
255   }
256   result = strcmp (buffer,"RichEdit1");
257   todo_wine {
258   ok (result  == 0,
259       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
260   }
261   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
262
263   es.dwCookie = (DWORD_PTR)&streamText3;
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   ok (result  == 0,
270       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
271   ok (strlen(buffer)  == 0,
272       "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
273   ok(es.dwError == -16, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, -16);
274
275   DestroyWindow(hwndRichEdit);
276 }
277
278 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
279                                          LPBYTE pbBuff,
280                                          LONG cb,
281                                          LONG *pcb)
282 {
283   char** str = (char**)dwCookie;
284   *pcb = cb;
285   if (*pcb > 0) {
286     memcpy(*str, pbBuff, *pcb);
287     *str += *pcb;
288   }
289   return 0;
290 }
291
292 static void test_EM_STREAMOUT(void)
293 {
294   HWND hwndRichEdit = new_richedit(NULL);
295   int r;
296   EDITSTREAM es;
297   char buf[1024] = {0};
298   char * p;
299
300   const char * TestItem1 = "TestSomeText";
301   const char * TestItem2 = "TestSomeText\r";
302   const char * TestItem3 = "TestSomeText\r\n";
303
304   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
305   p = buf;
306   es.dwCookie = (DWORD_PTR)&p;
307   es.dwError = 0;
308   es.pfnCallback = test_WM_SETTEXT_esCallback;
309   memset(buf, 0, sizeof(buf));
310   SendMessage(hwndRichEdit, EM_STREAMOUT,
311               (WPARAM)(SF_TEXT), (LPARAM)&es);
312   r = strlen(buf);
313   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
314   ok(strcmp(buf, TestItem1) == 0,
315         "streamed text different, got %s\n", buf);
316
317   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
318   p = buf;
319   es.dwCookie = (DWORD_PTR)&p;
320   es.dwError = 0;
321   es.pfnCallback = test_WM_SETTEXT_esCallback;
322   memset(buf, 0, sizeof(buf));
323   SendMessage(hwndRichEdit, EM_STREAMOUT,
324               (WPARAM)(SF_TEXT), (LPARAM)&es);
325   r = strlen(buf);
326
327   ok(r == 13, "streamed text length is %d, expecting 13\n", r);
328   ok(strcmp(buf, TestItem2) == 0,
329         "streamed text different, got %s\n", buf);
330
331   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
332   p = buf;
333   es.dwCookie = (DWORD_PTR)&p;
334   es.dwError = 0;
335   es.pfnCallback = test_WM_SETTEXT_esCallback;
336   memset(buf, 0, sizeof(buf));
337   SendMessage(hwndRichEdit, EM_STREAMOUT,
338               (WPARAM)(SF_TEXT), (LPARAM)&es);
339   r = strlen(buf);
340   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
341   ok(strcmp(buf, TestItem3) == 0,
342         "streamed text different, got %s\n", buf);
343
344   DestroyWindow(hwndRichEdit);
345 }
346
347 static const struct getline_s {
348   int line;
349   size_t buffer_len;
350   const char *text;
351   const char *broken_text;
352 } gl[] = {
353   {0, 10, "foo bar\r\n", "foo bar\r\n"},
354   {1, 10, "\r",          "\r\r\r\n"},
355   {2, 10, "\r\r\n",      "bar\n"},
356   {3, 10, "bar\n",       "\r\n"},
357   {4, 10, "\r\n"},
358
359   /* Buffer smaller than line length */
360   {0, 2, "foo bar\r"},
361   {0, 1, "foo bar\r"},
362   {0, 0, "foo bar\r"}
363 };
364
365 static void test_EM_GETLINE(void)
366 {
367   int i;
368   HWND hwndRichEdit = new_richedit(NULL);
369   static const int nBuf = 1024;
370   char dest[1024], origdest[1024];
371   LRESULT linecount;
372   const char text[] = "foo bar\r\n"
373       "\r"
374       "\r\r\n"
375       "bar\n";
376   BOOL broken_os = FALSE;
377
378   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
379   linecount = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
380   if (linecount == 4)
381   {
382     broken_os = TRUE;
383     win_skip("Win9x, WinME and NT4 handle '\\r only' differently\n");
384   }
385
386   memset(origdest, 0xBB, nBuf);
387   for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
388   {
389     int nCopied, expected_nCopied, expected_bytes_written;
390     char gl_text[1024];
391
392     if (gl[i].line >= linecount)
393       continue; /* Win9x, WinME and NT4 */
394
395     if (broken_os && gl[i].broken_text)
396       /* Win9x, WinME and NT4 */
397       strcpy(gl_text, gl[i].broken_text);
398     else
399       strcpy(gl_text, gl[i].text);
400
401     expected_nCopied = min(gl[i].buffer_len, strlen(gl_text));
402     /* Cater for the fact that Win9x, WinME and NT4 don't append the '\0' */
403     expected_bytes_written = min(gl[i].buffer_len, strlen(gl_text) + (broken_os ? 0 : 1));
404
405     memset(dest, 0xBB, nBuf);
406     *(WORD *) dest = gl[i].buffer_len;
407
408     /* EM_GETLINE appends a "\r\0" to the end of the line
409      * nCopied counts up to and including the '\r' */
410     nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
411     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
412        expected_nCopied);
413     /* two special cases since a parameter is passed via dest */
414     if (gl[i].buffer_len == 0)
415       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
416          "buffer_len=0\n");
417     else if (gl[i].buffer_len == 1)
418       ok(dest[0] == gl_text[0] && !dest[1] &&
419          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
420     else
421     {
422       ok(!strncmp(dest, gl_text, expected_bytes_written),
423          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
424       ok(!strncmp(dest + expected_bytes_written, origdest
425                   + expected_bytes_written, nBuf - expected_bytes_written),
426          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
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         "short\r"
441         "richedit1\r"
442         "\r"
443         "\r"
444         "\r\r\n";
445   int offset_test[16][2] = {
446         {0, 9},  /* Line 1: |richedit1\r */
447         {5, 9},  /* Line 1: riche|dit1\r */
448         {10, 9}, /* Line 2: |richedit1\n */
449         {15, 9}, /* Line 2: riche|dit1\n */
450         {20, 9}, /* Line 3: |richedit1\r\n */
451         {25, 9}, /* Line 3: riche|dit1\r\n */
452         {30, 9}, /* Line 3: richedit1\r|\n */
453         {31, 5}, /* Line 4: |short\r */
454         {42, 9}, /* Line 5: riche|dit1\r */
455         {46, 9}, /* Line 5: richedit1|\r */
456         {47, 0}, /* Line 6: |\r */
457         {48, 0}, /* Line 7: |\r */
458         {49, 0}, /* Line 8: |\r\r\n */
459         {50, 0}, /* Line 8: \r|\r\n */
460         {51, 0}, /* Line 8: \r\r|\n */
461         {52, 0}, /* Line 9: \r\r\n| */
462   };
463   int i;
464   LRESULT result;
465
466   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
467
468   result = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
469   if (result == 4) {
470      win_skip("Win9x, WinME and NT4 don't handle '\\r only' correctly\n");
471      return;
472   }
473   ok(result == 9, "Incorrect line count of %ld\n", result);
474
475   for (i = 0; i < sizeof(offset_test)/sizeof(offset_test[0]); i++) {
476     result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
477     ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
478        offset_test[i][0], result, offset_test[i][1]);
479   }
480
481   DestroyWindow(hwndRichEdit);
482 }
483
484 static void test_EM_GETTEXTRANGE(void)
485 {
486     HWND hwndRichEdit = new_richedit(NULL);
487     const char * text1 = "foo bar\r\nfoo bar";
488     const char * text3 = "foo bar\rfoo bar";
489     const char * expect1 = "bar\r\nfoo";
490     const char * expect2 = "\nfoo";
491     const char * expect3 = "bar\rfoo";
492     char buffer[1024] = {0};
493     LRESULT result;
494     TEXTRANGEA textRange;
495
496     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
497
498     textRange.lpstrText = buffer;
499     textRange.chrg.cpMin = 4;
500     textRange.chrg.cpMax = 12;
501     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
502     ok(result == 8, "EM_GETTEXTRANGE returned %ld\n", result);
503     ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
504
505     textRange.lpstrText = buffer;
506     textRange.chrg.cpMin = 8;
507     textRange.chrg.cpMax = 12;
508     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
509     ok(result == 4, "EM_GETTEXTRANGE returned %ld\n", result);
510     ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
511
512     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
513
514     textRange.lpstrText = buffer;
515     textRange.chrg.cpMin = 4;
516     textRange.chrg.cpMax = 11;
517     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
518     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
519
520     ok(!strcmp(expect3, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
521
522
523     DestroyWindow(hwndRichEdit);
524 }
525
526 static void test_EM_GETSELTEXT(void)
527 {
528     HWND hwndRichEdit = new_richedit(NULL);
529     const char * text1 = "foo bar\r\nfoo bar";
530     const char * text2 = "foo bar\rfoo bar";
531     const char * expect1 = "bar\r\nfoo";
532     const char * expect2 = "bar\rfoo";
533     char buffer[1024] = {0};
534     LRESULT result;
535
536     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
537
538     SendMessage(hwndRichEdit, EM_SETSEL, 4, 12);
539     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
540     ok(result == 8, "EM_GETTEXTRANGE returned %ld\n", result);
541     ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
542
543     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
544
545     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
546     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
547     ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
548
549     ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
550
551
552     DestroyWindow(hwndRichEdit);
553 }
554
555 static const char haystack[] = "WINEWine wineWine wine WineWine";
556                              /* ^0        ^10       ^20       ^30 */
557
558 static const char haystack2[] = "first\r\r\nsecond";
559
560 struct find_s {
561   int start;
562   int end;
563   const char *needle;
564   int flags;
565   int expected_loc;
566 };
567
568
569 struct find_s find_tests[] = {
570   /* Find in empty text */
571   {0, -1, "foo", FR_DOWN, -1},
572   {0, -1, "foo", 0, -1},
573   {0, -1, "", FR_DOWN, -1},
574   {20, 5, "foo", FR_DOWN, -1},
575   {5, 20, "foo", FR_DOWN, -1}
576 };
577
578 struct find_s find_tests2[] = {
579   /* No-result find */
580   {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
581   {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
582
583   /* Subsequent finds */
584   {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
585   {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
586   {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
587   {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
588
589   /* Find backwards */
590   {19, 20, "Wine", FR_MATCHCASE, -1},
591   {10, 20, "Wine", FR_MATCHCASE, 13},
592   {20, 10, "Wine", FR_MATCHCASE, -1},
593
594   /* Case-insensitive */
595   {1, 31, "wInE", FR_DOWN, 4},
596   {1, 31, "Wine", FR_DOWN, 4},
597
598   /* High-to-low ranges */
599   {20, 5, "Wine", FR_DOWN, -1},
600   {2, 1, "Wine", FR_DOWN, -1},
601   {30, 29, "Wine", FR_DOWN, -1},
602   {20, 5, "Wine", 0, /*13*/ -1},
603
604   /* Find nothing */
605   {5, 10, "", FR_DOWN, -1},
606   {10, 5, "", FR_DOWN, -1},
607   {0, -1, "", FR_DOWN, -1},
608   {10, 5, "", 0, -1},
609
610   /* Whole-word search */
611   {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
612   {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
613   {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
614   {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
615   {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
616   {11, -1, "winewine", FR_WHOLEWORD, 23},
617   {31, -1, "winewine", FR_WHOLEWORD, -1},
618
619   /* Bad ranges */
620   {5, 200, "XXX", FR_DOWN, -1},
621   {-20, 20, "Wine", FR_DOWN, -1},
622   {-20, 20, "Wine", FR_DOWN, -1},
623   {-15, -20, "Wine", FR_DOWN, -1},
624   {1<<12, 1<<13, "Wine", FR_DOWN, -1},
625
626   /* Check the case noted in bug 4479 where matches at end aren't recognized */
627   {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
628   {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
629   {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
630   {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
631   {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
632
633   /* The backwards case of bug 4479; bounds look right
634    * Fails because backward find is wrong */
635   {19, 20, "WINE", FR_MATCHCASE, -1},
636   {0, 20, "WINE", FR_MATCHCASE, 0},
637
638   {0, -1, "wineWine wine", FR_DOWN, 0},
639   {0, -1, "wineWine wine", 0, 0},
640   {0, -1, "INEW", 0, 1},
641   {0, 31, "INEW", 0, 1},
642   {4, -1, "INEW", 0, 10},
643 };
644
645 struct find_s find_tests3[] = {
646   /* Searching for end of line characters */
647   {0, -1, "t\r\r\ns", FR_DOWN | FR_MATCHCASE, 4},
648   {6, -1, "\r\n", FR_DOWN | FR_MATCHCASE, 6},
649   {7, -1, "\n", FR_DOWN | FR_MATCHCASE, 7},
650 };
651
652 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
653   int findloc;
654   FINDTEXT ft;
655   memset(&ft, 0, sizeof(ft));
656   ft.chrg.cpMin = f->start;
657   ft.chrg.cpMax = f->end;
658   ft.lpstrText = f->needle;
659   findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
660   ok(findloc == f->expected_loc,
661      "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
662      name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
663 }
664
665 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
666     int id) {
667   int findloc;
668   FINDTEXTEX ft;
669   int expected_end_loc;
670
671   memset(&ft, 0, sizeof(ft));
672   ft.chrg.cpMin = f->start;
673   ft.chrg.cpMax = f->end;
674   ft.lpstrText = f->needle;
675   ft.chrgText.cpMax = 0xdeadbeef;
676   findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
677   ok(findloc == f->expected_loc,
678       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
679       name, id, f->needle, f->start, f->end, f->flags, findloc);
680   ok(ft.chrgText.cpMin == f->expected_loc,
681       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d, expected %d\n",
682       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin, f->expected_loc);
683   expected_end_loc = ((f->expected_loc == -1) ? -1
684         : f->expected_loc + strlen(f->needle));
685   ok(ft.chrgText.cpMax == expected_end_loc ||
686       broken(ft.chrgText.cpMin == -1 && ft.chrgText.cpMax == 0xdeadbeef), /* Win9x, WinME and NT4 */
687       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
688       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
689 }
690
691 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
692     int num_tests)
693 {
694   int i;
695
696   for (i = 0; i < num_tests; i++) {
697     check_EM_FINDTEXT(hwnd, name, &find[i], i);
698     check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
699   }
700 }
701
702 static void test_EM_FINDTEXT(void)
703 {
704   HWND hwndRichEdit = new_richedit(NULL);
705
706   /* Empty rich edit control */
707   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
708       sizeof(find_tests)/sizeof(struct find_s));
709
710   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
711
712   /* Haystack text */
713   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
714       sizeof(find_tests2)/sizeof(struct find_s));
715
716   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack2);
717
718   /* Haystack text 2 (with EOL characters) */
719   run_tests_EM_FINDTEXT(hwndRichEdit, "3", find_tests3,
720       sizeof(find_tests3)/sizeof(struct find_s));
721
722   DestroyWindow(hwndRichEdit);
723 }
724
725 static void test_EM_POSFROMCHAR(void)
726 {
727   HWND hwndRichEdit = new_richedit(NULL);
728   int i;
729   POINTL pl;
730   LRESULT result;
731   unsigned int height = 0;
732   int xpos = 0;
733   static const char text[] = "aa\n"
734       "this is a long line of text that should be longer than the "
735       "control's width\n"
736       "cc\n"
737       "dd\n"
738       "ee\n"
739       "ff\n"
740       "gg\n"
741       "hh\n";
742
743   /* Fill the control to lines to ensure that most of them are offscreen */
744   for (i = 0; i < 50; i++)
745   {
746     /* Do not modify the string; it is exactly 16 characters long. */
747     SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
748     SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCD\r\n");
749   }
750
751   /*
752    Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
753    Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
754    Richedit 3.0 accepts either of the above API conventions.
755    */
756
757   /* Testing Richedit 1.0 API format */
758
759   /* Testing start of lines. X-offset should be constant on all cases (native is 1).
760      Since all lines are identical and drawn with the same font,
761      they should have the same height... right?
762    */
763   for (i = 0; i < 50; i++)
764   {
765     /* All the lines are 16 characters long */
766     result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
767     ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
768     if (i == 0)
769     {
770       ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
771       ok(pl.x == 1 ||
772           broken(pl.x == 0), /* Win9x, WinME and NT4 */
773           "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
774       xpos = pl.x;
775     }
776     else if (i == 1)
777     {
778       ok(pl.y > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", pl.y);
779       ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
780       height = pl.y;
781     }
782     else
783     {
784       ok(pl.y == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, i * height);
785       ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
786     }
787   }
788
789   /* Testing position at end of text */
790   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
791   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
792   ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
793   ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
794
795   /* Testing position way past end of text */
796   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
797   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
798   ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
799   ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
800
801
802   /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
803   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
804   for (i = 0; i < 50; i++)
805   {
806     /* All the lines are 16 characters long */
807     result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
808     ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
809     ok(pl.y == (i - 1) * height,
810         "EM_POSFROMCHAR reports y=%d, expected %d\n",
811         pl.y, (i - 1) * height);
812     ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
813   }
814
815   /* Testing position at end of text */
816   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
817   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
818   ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
819   ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
820
821   /* Testing position way past end of text */
822   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
823   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
824   ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
825   ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
826
827   /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
828   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
829   SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
830
831   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
832   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
833   ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
834   ok(pl.x == 1 ||
835       broken(pl.x == 0), /* Win9x, WinME and NT4 */
836       "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
837   xpos = pl.x;
838
839   SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
840   result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
841   ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
842   ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
843   todo_wine {
844   /* Fails on builtin because horizontal scrollbar is not being shown */
845   ok(pl.x < xpos ||
846       broken(pl.x == xpos), /* Win9x, WinME and NT4 */
847       "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n", pl.x, xpos);
848   }
849   DestroyWindow(hwndRichEdit);
850 }
851
852 static void test_word_wrap(void)
853 {
854     HWND hwnd;
855     POINTL point = {0, 60}; /* This point must be below the first line */
856     const char *text = "Must be long enough to test line wrapping";
857     DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
858     int res, pos, lines, prevlines, reflines[3];
859
860     /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
861      * when specified on window creation and set later. */
862     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
863                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
864     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
865     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
866     ok(res, "WM_SETTEXT failed.\n");
867     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
868     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
869     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
870     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
871
872     SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
873     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
874     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
875     DestroyWindow(hwnd);
876
877     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle|WS_HSCROLL,
878                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
879     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
880
881     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
882     ok(res, "WM_SETTEXT failed.\n");
883     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
884     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
885     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
886     ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
887
888     SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
889     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
890     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
891     DestroyWindow(hwnd);
892
893     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
894                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
895     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
896     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
897     ok(res, "WM_SETTEXT failed.\n");
898     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
899     ok(!pos ||
900         broken(pos == lstrlen(text)), /* Win9x, WinME and NT4 */
901         "pos=%d indicating word wrap when none is expected.\n", pos);
902     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
903     ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
904
905     SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle);
906     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
907     ok(!pos ||
908         broken(pos == lstrlen(text)), /* Win9x, WinME and NT4 */
909         "pos=%d indicating word wrap when none is expected.\n", pos);
910     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
911     ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
912     DestroyWindow(hwnd);
913
914     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL,
915                         dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
916                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
917     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
918     res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
919     ok(res, "WM_SETTEXT failed.\n");
920     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
921     ok(!pos ||
922         broken(pos == lstrlen(text)), /* Win9x, WinME and NT4 */
923         "pos=%d indicating word wrap when none is expected.\n", pos);
924     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
925     ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
926
927     SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle);
928     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
929     ok(!pos ||
930         broken(pos == lstrlen(text)), /* Win9x, WinME and NT4 */
931         "pos=%d indicating word wrap when none is expected.\n", pos);
932     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
933     ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
934
935     /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
936     res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
937     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
938     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
939     ok(!pos ||
940         broken(pos == lstrlen(text)), /* Win9x, WinME and NT4 */
941         "pos=%d indicating word wrap when none is expected.\n", pos);
942     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
943     ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
944
945     res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
946     ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
947     pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
948     ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
949     DestroyWindow(hwnd);
950
951     /* First lets see if the text would wrap normally (needed for reference) */
952     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
953                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
954     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
955     ok(IsWindowVisible(hwnd), "Window should be visible.\n");
956     res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
957     ok(res, "EM_REPLACESEL failed.\n");
958     /* Should have wrapped */
959     reflines[0] = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
960     ok(reflines[0] > 1, "Line was expected to wrap (%d lines).\n", reflines[0]);
961     /* Resize the window to fit the line */
962     MoveWindow(hwnd, 0, 0, 600, 80, TRUE);
963     /* Text should not be wrapped */
964     reflines[1] = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
965     ok(reflines[1] == 1, "Line wasn't expected to wrap (%d lines).\n", reflines[1]);
966     /* Resize the window again to make sure the line wraps again */
967     MoveWindow(hwnd, 0, 0, 10, 80, TRUE);
968     reflines[2] = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
969     ok(reflines[2] > 1, "Line was expected to wrap (%d lines).\n", reflines[2]);
970     DestroyWindow(hwnd);
971
972     /* Same test with redraw disabled */
973     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
974                         0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
975     ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
976     ok(IsWindowVisible(hwnd), "Window should be visible.\n");
977     /* Redraw is disabled by making the window invisible. */
978     SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
979     ok(!IsWindowVisible(hwnd), "Window shouldn't be visible.\n");
980     res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
981     ok(res, "EM_REPLACESEL failed.\n");
982     /* Should have wrapped */
983     prevlines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
984     ok(prevlines == reflines[0],
985         "Line was expected to wrap (%d lines).\n", prevlines);
986     /* Resize the window to fit the line, no change to the number of lines */
987     MoveWindow(hwnd, 0, 0, 600, 80, TRUE);
988     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
989     todo_wine
990     ok(lines == prevlines ||
991         broken(lines == reflines[1]), /* Win98, WinME and NT4 */
992         "Expected no change in the number of lines\n");
993     /* Resize the window again to make sure the line wraps again */
994     MoveWindow(hwnd, 0, 0, 10, 80, TRUE);
995     lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
996     todo_wine
997     ok(lines == prevlines ||
998         broken(lines == reflines[2]), /* Win98, WinME and NT4 */
999         "Expected no change in the number of lines\n");
1000     DestroyWindow(hwnd);
1001 }
1002
1003 static void test_EM_GETOPTIONS(void)
1004 {
1005     HWND hwnd;
1006     DWORD options;
1007
1008     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL,
1009                         WS_POPUP,
1010                         0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1011     options = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
1012     ok(options == 0, "Incorrect options %x\n", options);
1013     DestroyWindow(hwnd);
1014
1015     hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL,
1016                         WS_POPUP|WS_VSCROLL|WS_HSCROLL,
1017                         0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1018     options = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
1019     ok(options == ECO_AUTOVSCROLL ||
1020        broken(options == 0), /* Win9x, WinME and NT4 */
1021        "Incorrect initial options %x\n", options);
1022     DestroyWindow(hwnd);
1023 }
1024
1025 static void test_autoscroll(void)
1026 {
1027     HWND hwnd;
1028     UINT ret;
1029
1030     /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
1031      * auto vertical/horizontal scrolling options. */
1032     hwnd = CreateWindowEx(0, RICHEDIT_CLASS10A, NULL,
1033                           WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
1034                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1035     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS10A, (int) GetLastError());
1036     ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
1037     ok(ret & ECO_AUTOVSCROLL ||
1038         broken(!(ret & ECO_AUTOVSCROLL)), /* Win9x, WinME and NT4 */
1039         "ECO_AUTOVSCROLL isn't set.\n");
1040     ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
1041     ret = GetWindowLong(hwnd, GWL_STYLE);
1042     todo_wine
1043     ok(ret & ES_AUTOVSCROLL ||
1044         broken(!(ret & ES_AUTOVSCROLL)), /* Win9x, WinMe and NT4 */
1045         "ES_AUTOVSCROLL isn't set.\n");
1046     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
1047     DestroyWindow(hwnd);
1048
1049     hwnd = CreateWindowEx(0, RICHEDIT_CLASS10A, NULL,
1050                           WS_POPUP|ES_MULTILINE,
1051                           0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1052     ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS10A, (int) GetLastError());
1053     ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
1054     ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
1055     ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
1056     ret = GetWindowLong(hwnd, GWL_STYLE);
1057     ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
1058     ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
1059     DestroyWindow(hwnd);
1060 }
1061
1062 START_TEST( editor )
1063 {
1064   MSG msg;
1065   time_t end;
1066
1067   /* Must explicitly LoadLibrary(). The test has no references to functions in
1068    * RICHED32.DLL, so the linker doesn't actually link to it. */
1069   hmoduleRichEdit = LoadLibrary("RICHED32.DLL");
1070   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
1071
1072   test_WM_SETTEXT();
1073   test_EM_GETTEXTRANGE();
1074   test_EM_GETSELTEXT();
1075   test_WM_GETTEXTLENGTH();
1076   test_EM_STREAMIN();
1077   test_EM_STREAMOUT();
1078   test_EM_GETLINE();
1079   test_EM_LINELENGTH();
1080   test_EM_FINDTEXT();
1081   test_EM_POSFROMCHAR();
1082   test_word_wrap();
1083   test_EM_GETOPTIONS();
1084   test_autoscroll();
1085
1086   /* Set the environment variable WINETEST_RICHED32 to keep windows
1087    * responsive and open for 30 seconds. This is useful for debugging.
1088    *
1089    * The message pump uses PeekMessage() to empty the queue and then sleeps for
1090    * 50ms before retrying the queue. */
1091   end = time(NULL) + 30;
1092   if (getenv( "WINETEST_RICHED32" )) {
1093     while (time(NULL) < end) {
1094       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1095         TranslateMessage(&msg);
1096         DispatchMessage(&msg);
1097       } else {
1098         Sleep(50);
1099       }
1100     }
1101   }
1102
1103   OleFlushClipboard();
1104   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
1105 }