Bug 629734: Integrate hunspell test suite as an xpcshell test. r=ehsan
authorKyle Huey <khuey@kylehuey.com>
Thu, 17 Feb 2011 19:09:40 -0500
changeset 64025 014b3677edb2aec940ca8ca7f2ba53d7ad4fb63c
parent 64024 d67af0cdc353524000ba4169ed9f3aad1a2624ca
child 64026 139cb01d3a634a534ea00b22f3620b6292553523
push idunknown
push userunknown
push dateunknown
reviewersehsan
bugs629734
milestone2.2a1pre
Bug 629734: Integrate hunspell test suite as an xpcshell test. r=ehsan
extensions/spellcheck/hunspell/Makefile.in
extensions/spellcheck/hunspell/src/mozHunspell.cpp
extensions/spellcheck/hunspell/src/mozHunspell.h
extensions/spellcheck/hunspell/tests/unit/test_hunspell.js
extensions/spellcheck/idl/mozISpellCheckingEngine.idl
--- a/extensions/spellcheck/hunspell/Makefile.in
+++ b/extensions/spellcheck/hunspell/Makefile.in
@@ -32,16 +32,24 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ****** END LICENSE BLOCK ******
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir = extensions/spellcheck/hunspell
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= hunspell
 DIRS		= src
 
+XPCSHELL_TESTS = tests/unit
+
+libs::
+	$(NSINSTALL) -D $(DEPTH)/_tests/xpcshell/$(relativesrcdir)/tests/unit/data
+	$(INSTALL) $(srcdir)/tests/* \
+	  $(DEPTH)/_tests/xpcshell/$(relativesrcdir)/tests/unit/data/
+
 include $(topsrcdir)/config/rules.mk
 
--- a/extensions/spellcheck/hunspell/src/mozHunspell.cpp
+++ b/extensions/spellcheck/hunspell/src/mozHunspell.cpp
@@ -356,38 +356,38 @@ mozHunspell::LoadDictionaryList()
     dictDirs->GetNext(getter_AddRefs(elem));
 
     dictDir = do_QueryInterface(elem);
     if (dictDir)
       LoadDictionariesFromDir(dictDir);
   }
 }
 
-void
+NS_IMETHODIMP
 mozHunspell::LoadDictionariesFromDir(nsIFile* aDir)
 {
   nsresult rv;
 
   PRBool check = PR_FALSE;
   rv = aDir->Exists(&check);
   if (NS_FAILED(rv) || !check)
-    return;
+    return NS_ERROR_UNEXPECTED;
 
   rv = aDir->IsDirectory(&check);
   if (NS_FAILED(rv) || !check)
-    return;
+    return NS_ERROR_UNEXPECTED;
 
   nsCOMPtr<nsISimpleEnumerator> e;
   rv = aDir->GetDirectoryEntries(getter_AddRefs(e));
   if (NS_FAILED(rv))
-    return;
+    return NS_ERROR_UNEXPECTED;
 
   nsCOMPtr<nsIDirectoryEnumerator> files(do_QueryInterface(e));
   if (!files)
-    return;
+    return NS_ERROR_UNEXPECTED;
 
   nsCOMPtr<nsIFile> file;
   while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file))) && file) {
     nsAutoString leafName;
     file->GetLeafName(leafName);
     if (!StringEndsWith(leafName, NS_LITERAL_STRING(".dic")))
       continue;
 
@@ -403,16 +403,18 @@ mozHunspell::LoadDictionariesFromDir(nsI
       continue;
 
 #ifdef DEBUG_bsmedberg
     printf("Adding dictionary: %s\n", NS_ConvertUTF16toUTF8(dict).get());
 #endif
 
     mDictionaries.Put(dict, file);
   }
+
+  return NS_OK;
 }
 
 nsresult mozHunspell::ConvertCharset(const PRUnichar* aStr, char ** aDst)
 {
   NS_ENSURE_ARG_POINTER(aDst);
   NS_ENSURE_TRUE(mEncoder, NS_ERROR_NULL_POINTER);
 
   PRInt32 outLength;
--- a/extensions/spellcheck/hunspell/src/mozHunspell.h
+++ b/extensions/spellcheck/hunspell/src/mozHunspell.h
@@ -88,17 +88,16 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozHunspell, mozISpellCheckingEngine)
 
   mozHunspell() : mHunspell(nsnull) { }
   virtual ~mozHunspell();
 
   nsresult Init();
 
   void LoadDictionaryList();
-  void LoadDictionariesFromDir(nsIFile* aDir);
 
   // helper method for converting a word to the charset of the dictionary
   nsresult ConvertCharset(const PRUnichar* aStr, char ** aDst);
 
 protected:
  
   nsCOMPtr<mozIPersonalDictionary> mPersonalDictionary;
   nsCOMPtr<nsIUnicodeEncoder>      mEncoder; 
new file mode 100644
--- /dev/null
+++ b/extensions/spellcheck/hunspell/tests/unit/test_hunspell.js
@@ -0,0 +1,220 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const tests = [
+    ["affixes", "iso-8859-1"],
+    ["condition", "iso-8859-1"],
+    ["condition_utf", "UTF-8"],
+    ["base", "iso-8859-1"],
+    ["base_utf", "UTF-8"],
+    ["allcaps", "iso-8859-1"],
+    ["allcaps_utf", "UTF-8"],
+    ["allcaps2", "iso-8859-1"],
+    ["allcaps3", "iso-8859-1"],
+    ["keepcase", "iso-8859-1"],
+    ["i58202", "iso-8859-1"],
+    ["map", "iso-8859-1"],
+    ["rep", "iso-8859-1"],
+    ["sug", "iso-8859-1"],
+    ["sugutf", "UTF-8"],
+    ["phone", "iso-8859-1"],
+    ["flag", "iso-8859-1"],
+    ["flaglong", "iso-8859-1"],
+    ["flagnum", "iso-8859-1"],
+    ["flagutf8", "UTF-8"],
+    ["slash", "iso-8859-1"],
+    ["forbiddenword", "iso-8859-1"],
+    ["nosuggest", "iso-8859-1"],
+    ["alias", "iso-8859-1"],
+    ["alias2", "iso-8859-1"],
+    ["alias3", "iso-8859-1"],
+    ["breakdefault", "iso-8859-1"],
+    ["break", "UTF-8"],
+    ["needaffix", "iso-8859-1"],
+    ["needaffix2", "iso-8859-1"],
+    ["needaffix3", "iso-8859-1"],
+    ["needaffix4", "iso-8859-1"],
+    ["needaffix5", "iso-8859-1"],
+    ["circumfix", "iso-8859-1"],
+    ["fogemorpheme", "iso-8859-1"],
+    ["onlyincompound", "iso-8859-1"],
+    ["complexprefixes", "iso-8859-1"],
+    ["complexprefixes2", "iso-8859-1"],
+    ["complexprefixesutf", "UTF-8"],
+    ["conditionalprefix", "iso-8859-1"],
+    ["zeroaffix", "iso-8859-1"],
+    ["utf8", "UTF-8"],
+    ["utf8_bom", "UTF-8", {1: "todo"}],
+    ["utf8_bom2", "UTF-8", {1: "todo"}],
+    ["utf8_nonbmp", "UTF-8", {1: "todo", 2: "todo", 3: "todo", 4: "todo"}],
+    ["compoundflag", "iso-8859-1"],
+    ["compoundrule", "iso-8859-1"],
+    ["compoundrule2", "iso-8859-1"],
+    ["compoundrule3", "iso-8859-1"],
+    ["compoundrule4", "iso-8859-1"],
+    ["compoundrule5", "UTF-8"],
+    ["compoundrule6", "iso-8859-1"],
+    ["compoundrule7", "iso-8859-1"],
+    ["compoundrule8", "iso-8859-1"],
+    ["compoundaffix", "iso-8859-1"],
+    ["compoundaffix2", "iso-8859-1"],
+    ["compoundaffix3", "iso-8859-1"],
+    ["checkcompounddup", "iso-8859-1"],
+    ["checkcompoundtriple", "iso-8859-1"],
+    ["simplifiedtriple", "iso-8859-1"],
+    ["checkcompoundrep", "iso-8859-1", null, {2: "todo"}],
+    ["checkcompoundcase2", "iso-8859-1"],
+    ["checkcompoundcaseutf", "UTF-8"],
+    ["checkcompoundpattern", "iso-8859-1"],
+    ["checkcompoundpattern2", "iso-8859-1"],
+    ["checkcompoundpattern3", "iso-8859-1"],
+    ["checkcompoundpattern4", "iso-8859-1"],
+    ["utfcompound", "UTF-8"],
+    ["checksharps", "iso-8859-1"],
+    ["checksharpsutf", "UTF-8"],
+    ["germancompounding", "iso-8859-1"],
+    ["germancompoundingold", "iso-8859-1"],
+    ["i35725", "iso-8859-1"],
+    ["i53643", "iso-8859-1"],
+    ["i54633", "iso-8859-1"],
+    ["i54980", "iso-8859-1", {1: "todo", 3: "todo"}],
+    ["maputf", "UTF-8"],
+    ["reputf", "UTF-8"],
+    ["ignore", "iso-8859-1"],
+    ["ignoreutf", "UTF-8",
+     {1: "todo", 2: "todo", 3: "todo", 4: "todo", 5: "todo", 6: "todo",
+      7: "todo", 8: "todo"}],
+    ["1592880", "iso-8859-1"],
+    ["1695964", "iso-8859-1"],
+    ["1463589", "iso-8859-1"],
+    ["1463589_utf", "UTF-8"],
+    ["IJ", "iso-8859-1"],
+    ["i68568", "iso-8859-1"],
+    ["i68568utf", "UTF-8"],
+    ["1706659", "iso-8859-1"],
+    ["digits_in_words", "iso-8859-1"],
+//    ["colons_in_words", "iso-8859-1"], Suggestion test only
+    ["ngram_utf_fix", "UTF-8"],
+    ["morph", "us-ascii",
+     {11: "todo", 12: "todo", 13: "todo", 14: "todo", 15: "todo", 16: "todo",
+      17: "todo", 18: "todo", 19: "todo", 20: "todo", 21: "todo", 22: "todo",
+      23: "todo", 24: "todo", 25: "todo", 26: "todo", 27: "todo"}],
+    ["1975530", "UTF-8"],
+    ["fullstrip", "iso-8859-1"],
+    ["iconv", "UTF-8"],
+    ["oconv", "UTF-8"],
+    ["encoding", "iso-8859-1", {1: "todo", 3: "todo"}],
+    ["korean", "UTF-8"],
+    ["opentaal_forbiddenword1", "UTF-8"],
+    ["opentaal_forbiddenword2", "UTF-8"],
+    ["opentaal_keepcase", "UTF-8", null, {7: "todo"}],
+    ["arabic", "UTF-8"],
+    ["2970240", "iso-8859-1"],
+    ["2970242", "iso-8859-1"],
+    ["breakoff", "iso-8859-1"],
+    ["opentaal_cpdpat", "iso-8859-1"],
+    ["opentaal_cpdpat2", "iso-8859-1"],
+    ["2999225", "iso-8859-1", {1: "todo"}],
+    ["onlyincompound2", "iso-8859-1", null, {2: "todo"}],
+    ["forceucase", "iso-8859-1", null, {1: "todo"}],
+    ["warn", "iso-8859-1"]
+];
+
+function do_get_file_by_line(file, charset) {
+    dump("getting file by line for file " + file.path + "\n");
+    dump("using charset " + charset +"\n");
+    let fis = Cc["@mozilla.org/network/file-input-stream;1"].
+              createInstance(Ci.nsIFileInputStream);
+    fis.init(file, 0x1 /* READONLY */,
+	     0444, Ci.nsIFileInputStream.CLOSE_ON_EOF);
+
+    let lis = Cc["@mozilla.org/intl/converter-input-stream;1"].
+              createInstance(Ci.nsIConverterInputStream);
+    lis.init(fis, charset, 1024, 0);
+    lis.QueryInterface(Ci.nsIUnicharLineInputStream);
+
+    var val = {};
+    while (lis.readLine(val)) {
+	yield val.value;
+	val = {};
+    }
+}
+
+function do_run_test(checker, name, charset, todo_good, todo_bad) {
+    dump("\n\n\n\n");
+    dump("running test for " + name + "\n");
+    if (!checker) {
+	do_throw("Need spell checker here!");
+    }
+
+    let good = do_get_file("data/" + name + ".good", true);
+    let bad = do_get_file("data/" + name + ".wrong", true);
+    let sug = do_get_file("data/" + name + ".sug", true);
+
+    dump("Need some expected output\n")
+    do_check_true(good.exists() || bad.exists() || sug.exists());
+
+    dump("Setting dictionary to " + name + "\n");
+    checker.dictionary = name;
+
+    if (good.exists()) {
+	var good_counter = 0;
+	for (val in do_get_file_by_line(good, charset)) {
+	    let todo = false;
+	    good_counter++;
+	    if (todo_good && todo_good[good_counter]) {
+		todo = true;
+		dump("TODO\n");
+	    }
+
+	    dump("Expect word " + val + " is spelled correctly\n");
+	    if (todo) {
+		todo_check_true(checker.check(val));
+	    } else {
+		do_check_true(checker.check(val));
+	    }
+	}
+    }
+
+    if (bad.exists()) {
+	var bad_counter = 0;
+	for (val in do_get_file_by_line(bad, charset)) {
+	    let todo = false;
+	    bad_counter++;
+	    if (todo_bad && todo_bad[bad_counter]) {
+		todo = true;
+		dump("TODO\n");
+	    }
+
+	    dump("Expect word " + val + " is spelled wrong\n");
+	    if (todo) {
+		todo_check_false(checker.check(val));
+	    } else {
+		do_check_false(checker.check(val));
+	    }
+	}
+    }
+
+    // XXXkhuey test suggestions
+}
+
+function run_test() {
+    let spellChecker = Cc["@mozilla.org/spellchecker/engine;1"].
+	getService(Ci.mozISpellCheckingEngine);
+
+    do_check_true(!!spellChecker, "Should have a spell checker");
+    spellChecker.QueryInterface(Ci.mozISpellCheckingEngine);
+    let testdir = do_get_file("data/", false);
+    spellChecker.loadDictionariesFromDir(testdir);
+
+    function do_run_test_closure(test) {
+	[name, charset, todo_good, todo_bad] = test;
+	do_run_test(spellChecker, name, charset, todo_good, todo_bad);
+    }
+
+    tests.forEach(do_run_test_closure);
+}
--- a/extensions/spellcheck/idl/mozISpellCheckingEngine.idl
+++ b/extensions/spellcheck/idl/mozISpellCheckingEngine.idl
@@ -32,19 +32,20 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
+interface nsIFile;
 interface mozIPersonalDictionary;
 
-[scriptable, uuid(43987F7B-0FAA-4019-811E-42BECAC73FC5)]
+[scriptable, uuid(6eb307d6-3567-481a-971a-feb666b8ae72)]
 
 /**
  * This interface represents a SpellChecker.
  */
 
 interface mozISpellCheckingEngine : nsISupports {
   /**
    * The name of the current dictionary
@@ -90,14 +91,19 @@ interface mozISpellCheckingEngine : nsIS
    * check a word
    */
   boolean check(in wstring word);
 
   /**
    * get a list of suggestions for a misspelled word
    */
   void suggest(in wstring word,[array, size_is(count)] out wstring suggestions, out PRUint32 count);
+
+  /**
+   * Load dictionaries from the specified dir
+   */
+  void loadDictionariesFromDir(in nsIFile dir);
 };
 
 %{C++
 #define DICTIONARY_SEARCH_DIRECTORY "DictD"
 #define DICTIONARY_SEARCH_DIRECTORY_LIST "DictDL"
 %}