richedit: Add tests for EM_FINDTEXT in 1.0 emulation, make them pass under Wine.
[wine] / dlls / riched32 / tests / editor.c
1 /*
2 * Unit test suite for rich edit control 1.0
3 *
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
7 * Copyright 2007 Alex VillacĂ­s Lasso
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24 #include <stdarg.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <time.h>
34 #include <wine/test.h>
35
36 static HMODULE hmoduleRichEdit;
37
38 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
39   HWND hwnd;
40   hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
41                       |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
42                       hmoduleRichEdit, NULL);
43   ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
44   return hwnd;
45 }
46
47 static HWND new_richedit(HWND parent) {
48   return new_window(RICHEDIT_CLASS10A, ES_MULTILINE, parent);
49 }
50
51 static void test_WM_SETTEXT()
52 {
53   HWND hwndRichEdit = new_richedit(NULL);
54   const char * TestItem1 = "TestSomeText";
55   const char * TestItem2 = "TestSomeText\r";
56   const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
57   const char * TestItem4 = "TestSomeText\n\nTestSomeText";
58   const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
59   const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
60   const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
61   const char * TestItem8 = "TestSomeText\r\n";
62   const char * TestItem9 = "TestSomeText\r\nSomeMoreText\r\n";
63   const char * TestItem10 = "TestSomeText\r\n\r\nTestSomeText";
64   const char * TestItem11 = "TestSomeText TestSomeText";
65   const char * TestItem12 = "TestSomeText \r\nTestSomeText";
66   const char * TestItem13 = "TestSomeText\r\n \r\nTestSomeText";
67   const char * TestItem14 = "TestSomeText\n";
68   const char * TestItem15 = "TestSomeText\r\r\r";
69   const char * TestItem16 = "TestSomeText\r\r\rSomeMoreText";
70   char buf[1024] = {0};
71   LRESULT result;
72
73   /* This test attempts to show that WM_SETTEXT on a riched32 control does not
74      attempt to modify the text that is pasted into the control, and should
75      return it as is. In particular, \r\r\n is NOT converted, unlike riched20.
76      Currently, builtin riched32 mangles solitary \r or \n when not part of
77      a \r\n pair.
78
79      For riched32, the rules for breaking lines seem to be the following:
80      - \r\n is one line break. This is the normal case.
81      - \r{0,N}\n is one line break. In particular, \n by itself is a line break.
82      - \n{1,N} are that many line breaks.
83      - \r with text or other characters (except \n) past it, is a line break. That
84        is, a run of \r{N} without a terminating \n is considered N line breaks
85      - \r at the end of the text is NOT a line break. This differs from riched20,
86        where \r at the end of the text is a proper line break. This causes
87        TestItem2 to fail its test.
88    */
89
90 #define TEST_SETTEXT(a, b, nlines, is_todo, is_todo2) \
91   result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
92   ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
93   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
94   ok (result == lstrlen(buf), \
95         "WM_GETTEXT returned %ld instead of expected %u\n", \
96         result, lstrlen(buf)); \
97   result = strcmp(b, buf); \
98   if (is_todo) todo_wine { \
99   ok(result == 0, \
100         "WM_SETTEXT round trip: strcmp = %ld\n", result); \
101   } else { \
102   ok(result == 0, \
103         "WM_SETTEXT round trip: strcmp = %ld\n", result); \
104   } \
105   result = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0); \
106   if (is_todo2) todo_wine { \
107   ok(result == nlines, "EM_GETLINECOUNT returned %ld, expected %d\n", result, nlines); \
108   } else { \
109   ok(result == nlines, "EM_GETLINECOUNT returned %ld, expected %d\n", result, nlines); \
110   }
111
112   TEST_SETTEXT(TestItem1, TestItem1, 1, 0, 0)
113   TEST_SETTEXT(TestItem2, TestItem2, 1, 0, 0)
114   TEST_SETTEXT(TestItem3, TestItem3, 2, 0, 0)
115   TEST_SETTEXT(TestItem4, TestItem4, 3, 0, 0)
116   TEST_SETTEXT(TestItem5, TestItem5, 2, 0, 0)
117   TEST_SETTEXT(TestItem6, TestItem6, 3, 0, 0)
118   TEST_SETTEXT(TestItem7, TestItem7, 4, 0, 0)
119   TEST_SETTEXT(TestItem8, TestItem8, 2, 0, 0)
120   TEST_SETTEXT(TestItem9, TestItem9, 3, 0, 0)
121   TEST_SETTEXT(TestItem10, TestItem10, 3, 0, 0)
122   TEST_SETTEXT(TestItem11, TestItem11, 1, 0, 0)
123   TEST_SETTEXT(TestItem12, TestItem12, 2, 0, 0)
124   TEST_SETTEXT(TestItem13, TestItem13, 3, 0, 0)
125   TEST_SETTEXT(TestItem14, TestItem14, 2, 0, 0)
126   TEST_SETTEXT(TestItem15, TestItem15, 3, 0, 0)
127   TEST_SETTEXT(TestItem16, TestItem16, 4, 0, 0)
128
129 #undef TEST_SETTEXT
130   DestroyWindow(hwndRichEdit);
131 }
132
133 static void test_WM_GETTEXTLENGTH(void)
134 {
135     HWND hwndRichEdit = new_richedit(NULL);
136     static const char text3[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
137     static const char text4[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
138     int result;
139
140     /* Test for WM_GETTEXTLENGTH */
141     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text3);
142     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
143     ok(result == lstrlen(text3),
144         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
145         result, lstrlen(text3));
146
147     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text4);
148     result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
149     ok(result == lstrlen(text4),
150         "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
151         result, lstrlen(text4));
152
153     DestroyWindow(hwndRichEdit);
154 }
155
156 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
157                                          LPBYTE pbBuff,
158                                          LONG cb,
159                                          LONG *pcb)
160 {
161   const char** str = (const char**)dwCookie;
162   int size = strlen(*str);
163   *pcb = cb;
164   if (*pcb > size) {
165     *pcb = size;
166   }
167   if (*pcb > 0) {
168     memcpy(pbBuff, *str, *pcb);
169     *str += *pcb;
170   }
171   return 0;
172 }
173
174
175 static void test_EM_STREAMIN(void)
176 {
177   HWND hwndRichEdit = new_richedit(NULL);
178   LRESULT result;
179   EDITSTREAM es;
180   char buffer[1024] = {0};
181
182   const char * streamText0 = "{\\rtf1 TestSomeText}";
183   const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
184   const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
185
186   const char * streamText1 =
187   "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n" \
188   "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n" \
189   "}\r\n";
190
191   /* This should be accepted in richedit 1.0 emulation. See bug #8326 */
192   const char * streamText2 =
193     "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;" \
194     "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255" \
195     "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 " \
196     "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 " \
197     "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 " \
198     "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 " \
199     "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
200
201   const char * streamText3 = "RichEdit1";
202
203   /* Minimal test without \par at the end */
204   es.dwCookie = (DWORD_PTR)&streamText0;
205   es.dwError = 0;
206   es.pfnCallback = test_EM_STREAMIN_esCallback;
207   SendMessage(hwndRichEdit, EM_STREAMIN,
208               (WPARAM)(SF_RTF), (LPARAM)&es);
209
210   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
211   ok (result  == 12,
212       "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
213   result = strcmp (buffer,"TestSomeText");
214   ok (result  == 0,
215       "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
216   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
217
218   /* Native richedit 2.0 ignores last \par */
219   es.dwCookie = (DWORD_PTR)&streamText0a;
220   es.dwError = 0;
221   es.pfnCallback = test_EM_STREAMIN_esCallback;
222   SendMessage(hwndRichEdit, EM_STREAMIN,
223               (WPARAM)(SF_RTF), (LPARAM)&es);
224
225   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
226   ok (result  == 12,
227       "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
228   result = strcmp (buffer,"TestSomeText");
229   ok (result  == 0,
230       "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
231   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
232
233   /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
234   es.dwCookie = (DWORD_PTR)&streamText0b;
235   es.dwError = 0;
236   es.pfnCallback = test_EM_STREAMIN_esCallback;
237   SendMessage(hwndRichEdit, EM_STREAMIN,
238               (WPARAM)(SF_RTF), (LPARAM)&es);
239
240   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
241   ok (result  == 14,
242       "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
243   result = strcmp (buffer,"TestSomeText\r\n");
244   ok (result  == 0,
245       "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
246   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
247
248   es.dwCookie = (DWORD_PTR)&streamText1;
249   es.dwError = 0;
250   es.pfnCallback = test_EM_STREAMIN_esCallback;
251   SendMessage(hwndRichEdit, EM_STREAMIN,
252               (WPARAM)(SF_RTF), (LPARAM)&es);
253
254   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
255   ok (result  == 12,
256       "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
257   result = strcmp (buffer,"TestSomeText");
258   ok (result  == 0,
259       "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
260   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
261
262
263   es.dwCookie = (DWORD_PTR)&streamText2;
264   es.dwError = 0;
265   SendMessage(hwndRichEdit, EM_STREAMIN,
266               (WPARAM)(SF_RTF), (LPARAM)&es);
267
268   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
269   todo_wine {
270   ok (result  == 9,
271       "EM_STREAMIN: Test 2 returned %ld, expected 9\n", result);
272   }
273   result = strcmp (buffer,"RichEdit1");
274   todo_wine {
275   ok (result  == 0,
276       "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
277   }
278   ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
279
280   es.dwCookie = (DWORD_PTR)&streamText3;
281   es.dwError = 0;
282   SendMessage(hwndRichEdit, EM_STREAMIN,
283               (WPARAM)(SF_RTF), (LPARAM)&es);
284
285   result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
286   ok (result  == 0,
287       "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
288   ok (strlen(buffer)  == 0,
289       "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
290   ok(es.dwError == -16, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, -16);
291
292   DestroyWindow(hwndRichEdit);
293 }
294
295 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
296                                          LPBYTE pbBuff,
297                                          LONG cb,
298                                          LONG *pcb)
299 {
300   char** str = (char**)dwCookie;
301   *pcb = cb;
302   if (*pcb > 0) {
303     memcpy(*str, pbBuff, *pcb);
304     *str += *pcb;
305   }
306   return 0;
307 }
308
309 static void test_EM_STREAMOUT(void)
310 {
311   HWND hwndRichEdit = new_richedit(NULL);
312   int r;
313   EDITSTREAM es;
314   char buf[1024] = {0};
315   char * p;
316
317   const char * TestItem1 = "TestSomeText";
318   const char * TestItem2 = "TestSomeText\r";
319   const char * TestItem3 = "TestSomeText\r\n";
320
321   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
322   p = buf;
323   es.dwCookie = (DWORD_PTR)&p;
324   es.dwError = 0;
325   es.pfnCallback = test_WM_SETTEXT_esCallback;
326   memset(buf, 0, sizeof(buf));
327   SendMessage(hwndRichEdit, EM_STREAMOUT,
328               (WPARAM)(SF_TEXT), (LPARAM)&es);
329   r = strlen(buf);
330   ok(r == 12, "streamed text length is %d, expecting 12\n", r);
331   ok(strcmp(buf, TestItem1) == 0,
332         "streamed text different, got %s\n", buf);
333
334   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
335   p = buf;
336   es.dwCookie = (DWORD_PTR)&p;
337   es.dwError = 0;
338   es.pfnCallback = test_WM_SETTEXT_esCallback;
339   memset(buf, 0, sizeof(buf));
340   SendMessage(hwndRichEdit, EM_STREAMOUT,
341               (WPARAM)(SF_TEXT), (LPARAM)&es);
342   r = strlen(buf);
343
344   ok(r == 13, "streamed text length is %d, expecting 13\n", r);
345   ok(strcmp(buf, TestItem2) == 0,
346         "streamed text different, got %s\n", buf);
347
348   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
349   p = buf;
350   es.dwCookie = (DWORD_PTR)&p;
351   es.dwError = 0;
352   es.pfnCallback = test_WM_SETTEXT_esCallback;
353   memset(buf, 0, sizeof(buf));
354   SendMessage(hwndRichEdit, EM_STREAMOUT,
355               (WPARAM)(SF_TEXT), (LPARAM)&es);
356   r = strlen(buf);
357   ok(r == 14, "streamed text length is %d, expecting 14\n", r);
358   ok(strcmp(buf, TestItem3) == 0,
359         "streamed text different, got %s\n", buf);
360
361   DestroyWindow(hwndRichEdit);
362 }
363
364 static const struct getline_s {
365   int line;
366   size_t buffer_len;
367   const char *text;
368   int wine_todo;
369 } gl[] = {
370   {0, 10, "foo bar\r\n", 0},
371   {1, 10, "\n", 0},
372   {2, 10, "bar\n", 0},
373   {3, 10, "\r\n", 0},
374
375   /* Buffer smaller than line length */
376   {0, 2, "foo bar\r", 0},
377   {0, 1, "foo bar\r", 0},
378   {0, 0, "foo bar\r", 0}
379 };
380
381 static void test_EM_GETLINE(void)
382 {
383   int i;
384   HWND hwndRichEdit = new_richedit(NULL);
385   static const int nBuf = 1024;
386   char dest[1024], origdest[1024];
387   const char text[] = "foo bar\r\n"
388       "\n"
389       "bar\n";
390
391   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
392
393   memset(origdest, 0xBB, nBuf);
394   for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
395   {
396     int nCopied;
397     int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
398     int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
399     memset(dest, 0xBB, nBuf);
400     *(WORD *) dest = gl[i].buffer_len;
401
402     /* EM_GETLINE appends a "\r\0" to the end of the line
403      * nCopied counts up to and including the '\r' */
404     nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
405     if (gl[i].wine_todo) todo_wine {
406     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
407        expected_nCopied);
408     } else
409     ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
410        expected_nCopied);
411     /* two special cases since a parameter is passed via dest */
412     if (gl[i].buffer_len == 0)
413       ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
414          "buffer_len=0\n");
415     else if (gl[i].buffer_len == 1)
416       ok(dest[0] == gl[i].text[0] && !dest[1] &&
417          !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
418     else
419     {
420       if (gl[i].wine_todo) todo_wine {
421       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
422          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
423       ok(!strncmp(dest + expected_bytes_written, origdest
424                   + expected_bytes_written, nBuf - expected_bytes_written),
425          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
426       }
427       else
428       {
429       ok(!strncmp(dest, gl[i].text, expected_bytes_written),
430          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
431       ok(!strncmp(dest + expected_bytes_written, origdest
432                   + expected_bytes_written, nBuf - expected_bytes_written),
433          "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
434       }
435     }
436   }
437
438   DestroyWindow(hwndRichEdit);
439 }
440
441 static void test_EM_LINELENGTH(void)
442 {
443   HWND hwndRichEdit = new_richedit(NULL);
444   const char * text =
445         "richedit1\r"
446         "richedit1\n"
447         "richedit1\r\n"
448         "richedit1\r\r\r\r\r\n";
449   int offset_test[10][2] = {
450         {0, 9},
451         {5, 9},
452         {10, 9},
453         {15, 9},
454         {20, 9},
455         {25, 9},
456         {30, 9},
457         {35, 9},
458         {40, 9}, /* <----- in the middle of the \r run, but run not counted */
459         {45, 0},
460   };
461   int i;
462   LRESULT result;
463
464   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
465
466   for (i = 0; i < 10; i++) {
467     result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
468     ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
469         offset_test[i][0], result, offset_test[i][1]);
470   }
471
472   DestroyWindow(hwndRichEdit);
473 }
474
475 static void test_EM_GETTEXTRANGE(void)
476 {
477     HWND hwndRichEdit = new_richedit(NULL);
478     const char * text1 = "foo bar\r\nfoo bar";
479     const char * text2 = "foo bar\rfoo bar";
480     const char * expect1 = "bar\r\nfoo";
481     const char * expect2 = "bar\rfoo";
482     char buffer[1024] = {0};
483     LRESULT result;
484     TEXTRANGEA textRange;
485
486     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
487
488     textRange.lpstrText = buffer;
489     textRange.chrg.cpMin = 4;
490     textRange.chrg.cpMax = 12;
491     result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
492     ok(result == 8, "EM_GETTEXTRANGE returned %ld, expected %d\n",
493         result, strlen(expect1));
494     ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
495
496     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
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, expected %d\n",
503         result, strlen(expect2));
504
505     ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
506
507
508     DestroyWindow(hwndRichEdit);
509 }
510
511 static void test_EM_GETSELTEXT(void)
512 {
513     HWND hwndRichEdit = new_richedit(NULL);
514     const char * text1 = "foo bar\r\nfoo bar";
515     const char * text2 = "foo bar\rfoo bar";
516     const char * expect1 = "bar\r\nfoo";
517     const char * expect2 = "bar\rfoo";
518     char buffer[1024] = {0};
519     LRESULT result;
520
521     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
522
523     SendMessage(hwndRichEdit, EM_SETSEL, 4, 12);
524     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
525     ok(result == 8, "EM_GETTEXTRANGE returned %ld, expected %d\n",
526         result, strlen(expect1));
527     ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
528
529     SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
530
531     SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
532     result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
533     ok(result == 7, "EM_GETTEXTRANGE returned %ld, expected %d\n",
534         result, strlen(expect2));
535
536     ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
537
538
539     DestroyWindow(hwndRichEdit);
540 }
541
542 static const char haystack[] = "WINEWine wineWine wine WineWine";
543                              /* ^0        ^10       ^20       ^30 */
544
545 struct find_s {
546   int start;
547   int end;
548   const char *needle;
549   int flags;
550   int expected_loc;
551   int _todo_wine;
552 };
553
554
555 struct find_s find_tests[] = {
556   /* Find in empty text */
557   {0, -1, "foo", FR_DOWN, -1, 0},
558   {0, -1, "foo", 0, -1, 0},
559   {0, -1, "", FR_DOWN, -1, 0},
560   {20, 5, "foo", FR_DOWN, -1, 0},
561   {5, 20, "foo", FR_DOWN, -1, 0}
562 };
563
564 struct find_s find_tests2[] = {
565   /* No-result find */
566   {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
567   {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
568
569   /* Subsequent finds */
570   {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
571   {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
572   {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
573   {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
574
575   /* Find backwards */
576   {19, 20, "Wine", FR_MATCHCASE, -1, 0},
577   {10, 20, "Wine", FR_MATCHCASE, 13, 0},
578   {20, 10, "Wine", FR_MATCHCASE, -1, 0},
579
580   /* Case-insensitive */
581   {1, 31, "wInE", FR_DOWN, 4, 0},
582   {1, 31, "Wine", FR_DOWN, 4, 0},
583
584   /* High-to-low ranges */
585   {20, 5, "Wine", FR_DOWN, -1, 0},
586   {2, 1, "Wine", FR_DOWN, -1, 0},
587   {30, 29, "Wine", FR_DOWN, -1, 0},
588   {20, 5, "Wine", 0, /*13*/ -1, 0},
589
590   /* Find nothing */
591   {5, 10, "", FR_DOWN, -1, 0},
592   {10, 5, "", FR_DOWN, -1, 0},
593   {0, -1, "", FR_DOWN, -1, 0},
594   {10, 5, "", 0, -1, 0},
595
596   /* Whole-word search */
597   {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
598   {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
599   {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
600   {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
601   {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
602   {11, -1, "winewine", FR_WHOLEWORD, 23, 0},
603   {31, -1, "winewine", FR_WHOLEWORD, -1, 0},
604
605   /* Bad ranges */
606   {5, 200, "XXX", FR_DOWN, -1, 0},
607   {-20, 20, "Wine", FR_DOWN, -1, 0},
608   {-20, 20, "Wine", FR_DOWN, -1, 0},
609   {-15, -20, "Wine", FR_DOWN, -1, 0},
610   {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
611
612   /* Check the case noted in bug 4479 where matches at end aren't recognized */
613   {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
614   {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
615   {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
616   {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
617   {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
618
619   /* The backwards case of bug 4479; bounds look right
620    * Fails because backward find is wrong */
621   {19, 20, "WINE", FR_MATCHCASE, -1, 0},
622   {0, 20, "WINE", FR_MATCHCASE, 0, 0},
623
624   {0, -1, "wineWine wine", FR_DOWN, 0, 0},
625   {0, -1, "wineWine wine", 0, 0, 0},
626   {0, -1, "INEW", 0, 1, 0},
627   {0, 31, "INEW", 0, 1, 0},
628   {4, -1, "INEW", 0, 10, 0},
629 };
630
631 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
632   int findloc;
633   FINDTEXT ft;
634   memset(&ft, 0, sizeof(ft));
635   ft.chrg.cpMin = f->start;
636   ft.chrg.cpMax = f->end;
637   ft.lpstrText = f->needle;
638   findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
639   ok(findloc == f->expected_loc,
640      "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
641      name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
642 }
643
644 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
645     int id) {
646   int findloc;
647   FINDTEXTEX ft;
648   int expected_end_loc;
649
650   memset(&ft, 0, sizeof(ft));
651   ft.chrg.cpMin = f->start;
652   ft.chrg.cpMax = f->end;
653   ft.lpstrText = f->needle;
654   findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
655   ok(findloc == f->expected_loc,
656       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
657       name, id, f->needle, f->start, f->end, f->flags, findloc);
658   ok(ft.chrgText.cpMin == f->expected_loc,
659       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d, expected %d\n",
660       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin, f->expected_loc);
661   expected_end_loc = ((f->expected_loc == -1) ? -1
662         : f->expected_loc + strlen(f->needle));
663   ok(ft.chrgText.cpMax == expected_end_loc,
664       "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
665       name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
666 }
667
668 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
669     int num_tests)
670 {
671   int i;
672
673   for (i = 0; i < num_tests; i++) {
674     if (find[i]._todo_wine) {
675       todo_wine {
676         check_EM_FINDTEXT(hwnd, name, &find[i], i);
677         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
678       }
679     } else {
680         check_EM_FINDTEXT(hwnd, name, &find[i], i);
681         check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
682     }
683   }
684 }
685
686 static void test_EM_FINDTEXT(void)
687 {
688   HWND hwndRichEdit = new_richedit(NULL);
689
690   /* Empty rich edit control */
691   run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
692       sizeof(find_tests)/sizeof(struct find_s));
693
694   SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
695
696   /* Haystack text */
697   run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
698       sizeof(find_tests2)/sizeof(struct find_s));
699
700   DestroyWindow(hwndRichEdit);
701 }
702
703
704
705 START_TEST( editor )
706 {
707   MSG msg;
708   time_t end;
709
710   /* Must explicitly LoadLibrary(). The test has no references to functions in
711    * RICHED32.DLL, so the linker doesn't actually link to it. */
712   hmoduleRichEdit = LoadLibrary("RICHED32.DLL");
713   ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
714
715   test_WM_SETTEXT();
716   test_EM_GETTEXTRANGE();
717   test_EM_GETSELTEXT();
718   test_WM_GETTEXTLENGTH();
719   test_EM_STREAMIN();
720   test_EM_STREAMOUT();
721   test_EM_GETLINE();
722   test_EM_LINELENGTH();
723   test_EM_FINDTEXT();
724
725   /* Set the environment variable WINETEST_RICHED32 to keep windows
726    * responsive and open for 30 seconds. This is useful for debugging.
727    *
728    * The message pump uses PeekMessage() to empty the queue and then sleeps for
729    * 50ms before retrying the queue. */
730   end = time(NULL) + 30;
731   if (getenv( "WINETEST_RICHED32" )) {
732     while (time(NULL) < end) {
733       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
734         TranslateMessage(&msg);
735         DispatchMessage(&msg);
736       } else {
737         Sleep(50);
738       }
739     }
740   }
741
742   OleFlushClipboard();
743   ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
744 }