riched20: add EM_FINDTEXT conformance tests and fix 3 problems they expose

tkho at ucla.edu tkho at ucla.edu
Thu Feb 9 21:16:05 CST 2006


Hi,

This patch adds a conformance test of riched20's EM_FINDTEXT and EM_FINDTEXTEX.
It also fixes three issues found in the implementation.

2006-02-09 Thomas Kho <tkho at ucla.edu>

	* dlls/riched20/editor.c:
	riched20: Fixed bounds error when finding text forward (bug 4479)
	Fixed a potential null-pointer dereference (by Lei Zhang)
	Corrected find behavior for null-results

	* configure.ac, dlls/riched20/Makefile.in,
	  dlls/riched20/tests/Makefile.in, dlls/riched20/tests/editor.c:
	riched20: added tests for EM_FINDTEXT and EM_FINDTEXTEX messages

Without the fix to dlls/riched20/editor.c, the following test case to search
for the empty string crashes on Wine:
+  /* Find nothing */
+  {5, 10, "", FR_DOWN, -1, 0},

 configure.ac                    |    1 
 dlls/riched20/Makefile.in       |    2 
 dlls/riched20/editor.c          |   20 ++-
 dlls/riched20/tests/Makefile.in |   13 ++
 dlls/riched20/tests/editor.c    |  215 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 245 insertions(+), 6 deletions(-)

Signed-off-by: Thomas Kho <tkho at ucla.edu>
diff -Naur ../cvs/configure.ac ./configure.ac
--- ../cvs/configure.ac	2006-02-06 13:06:07.000000000 -0800
+++ ./configure.ac	2006-02-09 11:53:10.000000000 -0800
@@ -1578,6 +1578,7 @@
 dlls/quartz/tests/Makefile
 dlls/rasapi32/Makefile
 dlls/riched20/Makefile
+dlls/riched20/tests/Makefile
 dlls/richedit/Makefile
 dlls/rpcrt4/Makefile
 dlls/rpcrt4/tests/Makefile
diff -Naur ../cvs/dlls/riched20/Makefile.in ./dlls/riched20/Makefile.in
--- ../cvs/dlls/riched20/Makefile.in	2006-02-01 05:00:48.000000000 -0800
+++ ./dlls/riched20/Makefile.in	2006-02-09 11:53:10.000000000 -0800
@@ -25,6 +25,8 @@
 	wrap.c \
 	writer.c
 
+SUBDIRS = tests
+
 @MAKE_DLL_RULES@
 
 ### Dependencies:
diff -Naur ../cvs/dlls/riched20/editor.c ./dlls/riched20/editor.c
--- ../cvs/dlls/riched20/editor.c	2006-02-06 03:11:43.000000000 -0800
+++ ./dlls/riched20/editor.c	2006-02-09 11:53:57.000000000 -0800
@@ -755,23 +755,26 @@
     nMax = max(chrg->cpMin, chrg->cpMax);
   }
   
-  if (!nLen)
+  if (!nLen || nMin < 0 || nMax < 0)
   {
     if (chrgText)
-      chrgText->cpMin = chrgText->cpMax = ((flags & FR_DOWN) ? nMin : nMax);
-    return chrgText->cpMin;
+      chrgText->cpMin = chrgText->cpMax = -1;
+    return -1;
   }
  
   if (flags & FR_DOWN) /* Forward search */
   {
     nStart = nMin;
     item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart);
-    if (!item)
+    if (!item) {
+      if (chrgText)
+        chrgText->cpMin = chrgText->cpMax = -1;
       return -1;
+    }
 
     para = ME_GetParagraph(item);
     while (item
-           && para->member.para.nCharOfs + item->member.run.nCharOfs + nStart + nLen < nMax)
+           && para->member.para.nCharOfs + item->member.run.nCharOfs + nStart + nLen <= nMax)
     {
       ME_DisplayItem *pCurItem = item;
       int nCurStart = nStart;
@@ -811,8 +814,11 @@
   {
     nEnd = nMax;
     item = ME_FindItemAtOffset(editor, diRun, nEnd, &nEnd);
-    if (!item)
+    if (!item) {
+      if (chrgText)
+        chrgText->cpMin = chrgText->cpMax = -1;
       return -1;
+    }
     
     para = ME_GetParagraph(item);
     
@@ -854,6 +860,8 @@
     }
   }
   TRACE("not found\n");
+  if (chrgText)
+    chrgText->cpMin = chrgText->cpMax = -1;
   return -1;
 }
 
diff -Naur ../cvs/dlls/riched20/tests/editor.c ./dlls/riched20/tests/editor.c
--- ../cvs/dlls/riched20/tests/editor.c	1969-12-31 16:00:00.000000000 -0800
+++ ./dlls/riched20/tests/editor.c	2006-02-09 11:53:10.000000000 -0800
@@ -0,0 +1,215 @@
+/*
+* Unit test suite for rich edit control
+*
+* Copyright 2006 Google (Thomas Kho)
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <wine/test.h>
+#include <windows.h>
+#include <richedit.h>
+#include <time.h>
+
+static HMODULE hmoduleRichEdit;
+
+static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
+  HWND hwnd;
+  hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
+                      |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
+                      hmoduleRichEdit, NULL);
+  ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
+  return hwnd;
+}
+
+static HWND new_richedit(HWND parent) {
+  return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
+}
+
+static const char haystack[] = "WINEWine wineWine wine WineWine";
+                             /* ^0        ^10       ^20       ^30 */
+
+struct find_s {
+  int start;
+  int end;
+  char *needle;
+  int flags;
+  int expected_loc;
+  int _todo_wine;
+};
+
+struct find_s find_tests[] = {
+  /* Find in empty text */
+  {0, -1, "foo", FR_DOWN, -1, 0},
+  {0, -1, "foo", 0, -1, 0},
+  {0, -1, "", FR_DOWN, -1, 0},
+  {20, 5, "foo", FR_DOWN, -1, 0},
+  {5, 20, "foo", FR_DOWN, -1, 0}
+};
+
+struct find_s find_tests2[] = {
+  /* No-result find */
+  {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
+  {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
+
+  /* Subsequent finds */
+  {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
+  {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
+  {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
+  {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
+
+  /* Find backwards */
+  {19, 20, "Wine", FR_MATCHCASE, 13, 1},
+  {10, 20, "Wine", FR_MATCHCASE, 4, 1},
+  {20, 10, "Wine", FR_MATCHCASE, 13, 0},
+
+  /* Case-insensitive */
+  {1, 31, "wInE", FR_DOWN, 4, 1},
+  {1, 31, "Wine", FR_DOWN, 4, 0},
+
+  /* High-to-low ranges */
+  {20, 5, "Wine", FR_DOWN, -1, 1},
+  {2, 1, "Wine", FR_DOWN, -1, 0},
+  {30, 29, "Wine", FR_DOWN, -1, 0},
+  {20, 5, "Wine", 0, 13, 0},
+
+  /* Find nothing */
+  {5, 10, "", FR_DOWN, -1, 0},
+  {10, 5, "", FR_DOWN, -1, 0},
+  {0, -1, "", FR_DOWN, -1, 0},
+  {10, 5, "", 0, -1, 0},
+
+  /* Whole-word search */
+  {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 1},
+  {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 1},
+  
+  /* Bad ranges */
+  {-20, 20, "Wine", FR_DOWN, -1, 0},
+  {-20, 20, "Wine", FR_DOWN, -1, 0},
+  {-15, -20, "Wine", FR_DOWN, -1, 0},
+  {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
+
+  /* Check the case noted in bug 4479 where matches at end aren't recognized */
+  {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
+  {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
+  {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
+  {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
+  {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
+
+  /* The backwards case of bug 4479; bounds look right
+   * Fails because backward find is wrong */
+  {19, 20, "WINE", FR_MATCHCASE, 0, 1},
+  {0, 20, "WINE", FR_MATCHCASE, -1, 1}
+};
+
+static void check_EM_FINDTEXT(HWND hwnd, char *name, struct find_s *f, int id) {
+  int findloc;
+  FINDTEXT ft;
+  memset(&ft, 0, sizeof(ft));
+  ft.chrg.cpMin = f->start;
+  ft.chrg.cpMax = f->end;
+  ft.lpstrText = f->needle;
+  findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
+  ok(findloc == f->expected_loc,
+     "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d\n",
+     name, id, f->needle, f->start, f->end, f->flags, findloc);
+}
+
+static void check_EM_FINDTEXTEX(HWND hwnd, char *name, struct find_s *f,
+    int id) {
+  int findloc;
+  FINDTEXTEX ft;
+  memset(&ft, 0, sizeof(ft));
+  ft.chrg.cpMin = f->start;
+  ft.chrg.cpMax = f->end;
+  ft.lpstrText = f->needle;
+  findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
+  ok(findloc == f->expected_loc,
+      "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
+      name, id, f->needle, f->start, f->end, f->flags, findloc);
+  ok(ft.chrgText.cpMin == f->expected_loc,
+      "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %ld\n",
+      name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
+  ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1
+        : f->expected_loc + strlen(f->needle)),
+      "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %ld\n",
+      name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax);
+}
+
+static void run_tests_EM_FINDTEXT(HWND hwnd, char *name, struct find_s *find,
+    int num_tests)
+{
+  int i;
+
+  for (i = 0; i < num_tests; i++) {
+    if (find[i]._todo_wine) {
+      todo_wine {
+        check_EM_FINDTEXT(hwnd, name, &find[i], i);
+        check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
+      }
+    } else {
+        check_EM_FINDTEXT(hwnd, name, &find[i], i);
+        check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
+    }
+  }
+}
+
+static void test_EM_FINDTEXT(void)
+{
+  HWND hwndRichEdit = new_richedit(NULL);
+
+  run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
+      sizeof(find_tests)/sizeof(struct find_s));
+
+  SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
+
+  run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
+      sizeof(find_tests2)/sizeof(struct find_s));
+
+  DestroyWindow(hwndRichEdit);
+}
+
+START_TEST( editor )
+{
+  MSG msg;
+  time_t end;
+
+  /* Must explicitly LoadLibrary(). The test has no references to functions in
+   * RICHED20.DLL, so the linker doesn't actually link to it. */
+  hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
+  ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
+
+  test_EM_FINDTEXT();
+
+  /* Set the environment variable WINETEST_RICHED20 to keep windows
+   * responsive and open for 30 seconds. This is useful for debugging.
+   *
+   * The message pump uses PeekMessage() to empty the queue and then sleeps for
+   * 50ms before retrying the queue. */
+  end = time(NULL) + 30;
+  if (getenv( "WINETEST_RICHED20" )) {
+    while (time(NULL) < end) {
+      if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+      } else {
+        Sleep(50);
+      }
+    }
+  }
+
+  ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());
+}
+
diff -Naur ../cvs/dlls/riched20/tests/Makefile.in ./dlls/riched20/tests/Makefile.in
--- ../cvs/dlls/riched20/tests/Makefile.in	1969-12-31 16:00:00.000000000 -0800
+++ ./dlls/riched20/tests/Makefile.in	2006-02-09 11:53:10.000000000 -0800
@@ -0,0 +1,13 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+TESTDLL   = riched20.dll
+IMPORTS   = riched20 user32 gdi32 kernel32
+
+CTESTS = \
+	editor.c
+
+ at MAKE_TEST_RULES@
+
+### Dependencies:



More information about the wine-patches mailing list