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