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