Home | History | Annotate | Download | only in spellchecker
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/file_util.h"
      6 #include "base/message_loop/message_loop.h"
      7 #include "base/path_service.h"
      8 #include "base/strings/sys_string_conversions.h"
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "chrome/common/chrome_paths.h"
     11 #include "chrome/common/spellcheck_common.h"
     12 #include "chrome/common/spellcheck_result.h"
     13 #include "chrome/renderer/spellchecker/hunspell_engine.h"
     14 #include "chrome/renderer/spellchecker/spellcheck.h"
     15 #include "testing/gtest/include/gtest/gtest.h"
     16 #include "third_party/WebKit/public/web/WebTextCheckingCompletion.h"
     17 #include "third_party/WebKit/public/web/WebTextCheckingResult.h"
     18 #include "ui/base/l10n/l10n_util.h"
     19 
     20 namespace {
     21 
     22 base::FilePath GetHunspellDirectory() {
     23   base::FilePath hunspell_directory;
     24   if (!PathService::Get(base::DIR_SOURCE_ROOT, &hunspell_directory))
     25     return base::FilePath();
     26 
     27   hunspell_directory = hunspell_directory.AppendASCII("third_party");
     28   hunspell_directory = hunspell_directory.AppendASCII("hunspell_dictionaries");
     29   return hunspell_directory;
     30 }
     31 
     32 }  // namespace
     33 
     34 // TODO(groby): This needs to be a BrowserTest for OSX.
     35 class SpellCheckTest : public testing::Test {
     36  public:
     37   SpellCheckTest() {
     38     ReinitializeSpellCheck("en-US");
     39   }
     40 
     41   void ReinitializeSpellCheck(const std::string& language) {
     42     spell_check_.reset(new SpellCheck());
     43     InitializeSpellCheck(language);
     44   }
     45 
     46   void UninitializeSpellCheck() {
     47     spell_check_.reset(new SpellCheck());
     48   }
     49 
     50   bool InitializeIfNeeded() {
     51     return spell_check()->InitializeIfNeeded();
     52   }
     53 
     54   void InitializeSpellCheck(const std::string& language) {
     55     base::FilePath hunspell_directory = GetHunspellDirectory();
     56     EXPECT_FALSE(hunspell_directory.empty());
     57     base::File file(
     58         chrome::spellcheck_common::GetVersionedFileName(language,
     59                                                         hunspell_directory),
     60         base::File::FLAG_OPEN | base::File::FLAG_READ);
     61 #if defined(OS_MACOSX)
     62     // TODO(groby): Forcing spellcheck to use hunspell, even on OSX.
     63     // Instead, tests should exercise individual spelling engines.
     64     spell_check_->spellcheck_.platform_spelling_engine_.reset(
     65         new HunspellEngine);
     66 #endif
     67     spell_check_->Init(file.Pass(), std::set<std::string>(), language);
     68   }
     69 
     70   void EnableAutoCorrect(bool enable_autocorrect) {
     71     spell_check_->OnEnableAutoSpellCorrect(enable_autocorrect);
     72   }
     73 
     74   virtual ~SpellCheckTest() {
     75   }
     76 
     77   SpellCheck* spell_check() { return spell_check_.get(); }
     78 
     79   bool CheckSpelling(const std::string& word, int tag) {
     80     return spell_check_->spellcheck_.platform_spelling_engine_->CheckSpelling(
     81         base::ASCIIToUTF16(word), tag);
     82   }
     83 
     84 #if !defined(OS_MACOSX)
     85  protected:
     86   void TestSpellCheckParagraph(
     87       const base::string16& input,
     88       const std::vector<SpellCheckResult>& expected) {
     89     blink::WebVector<blink::WebTextCheckingResult> results;
     90     spell_check()->SpellCheckParagraph(input,
     91                                        &results);
     92 
     93     EXPECT_EQ(results.size(), expected.size());
     94     size_t size = std::min(results.size(), expected.size());
     95     for (size_t j = 0; j < size; ++j) {
     96       EXPECT_EQ(results[j].decoration, blink::WebTextDecorationTypeSpelling);
     97       EXPECT_EQ(results[j].location, expected[j].location);
     98       EXPECT_EQ(results[j].length, expected[j].length);
     99     }
    100   }
    101 #endif
    102 
    103  private:
    104   scoped_ptr<SpellCheck> spell_check_;
    105   base::MessageLoop loop;
    106 };
    107 
    108 // A fake completion object for verification.
    109 class MockTextCheckingCompletion : public blink::WebTextCheckingCompletion {
    110  public:
    111   MockTextCheckingCompletion()
    112       : completion_count_(0) {
    113   }
    114 
    115   virtual void didFinishCheckingText(
    116       const blink::WebVector<blink::WebTextCheckingResult>& results)
    117           OVERRIDE {
    118     completion_count_++;
    119     last_results_ = results;
    120   }
    121 
    122   virtual void didCancelCheckingText() OVERRIDE {
    123     completion_count_++;
    124   }
    125 
    126   size_t completion_count_;
    127   blink::WebVector<blink::WebTextCheckingResult> last_results_;
    128 };
    129 
    130 // Operates unit tests for the webkit_glue::SpellCheckWord() function
    131 // with the US English dictionary.
    132 // The unit tests in this function consist of:
    133 //   * Tests for the function with empty strings;
    134 //   * Tests for the function with a valid English word;
    135 //   * Tests for the function with a valid non-English word;
    136 //   * Tests for the function with a valid English word with a preceding
    137 //     space character;
    138 //   * Tests for the function with a valid English word with a preceding
    139 //     non-English word;
    140 //   * Tests for the function with a valid English word with a following
    141 //     space character;
    142 //   * Tests for the function with a valid English word with a following
    143 //     non-English word;
    144 //   * Tests for the function with two valid English words concatenated
    145 //     with space characters or non-English words;
    146 //   * Tests for the function with an invalid English word;
    147 //   * Tests for the function with an invalid English word with a preceding
    148 //     space character;
    149 //   * Tests for the function with an invalid English word with a preceding
    150 //     non-English word;
    151 //   * Tests for the function with an invalid English word with a following
    152 //     space character;
    153 //   * Tests for the function with an invalid English word with a following
    154 //     non-English word, and;
    155 //   * Tests for the function with two invalid English words concatenated
    156 //     with space characters or non-English words.
    157 // A test with a "[ROBUSTNESS]" mark shows it is a robustness test and it uses
    158 // grammatically incorrect string.
    159 // TODO(groby): Please feel free to add more tests.
    160 TEST_F(SpellCheckTest, SpellCheckStrings_EN_US) {
    161   static const struct {
    162     // A string to be tested.
    163     const wchar_t* input;
    164     // An expected result for this test case.
    165     //   * true: the input string does not have any invalid words.
    166     //   * false: the input string has one or more invalid words.
    167     bool expected_result;
    168     // The position and the length of the first invalid word.
    169     int misspelling_start;
    170     int misspelling_length;
    171   } kTestCases[] = {
    172     // Empty strings.
    173     {L"", true},
    174     {L" ", true},
    175     {L"\xA0", true},
    176     {L"\x3000", true},
    177 
    178     // A valid English word "hello".
    179     {L"hello", true},
    180     // A valid Chinese word (meaning "hello") consisting of two CJKV
    181     // ideographs
    182     {L"\x4F60\x597D", true},
    183     // A valid Korean word (meaning "hello") consisting of five hangul
    184     // syllables
    185     {L"\xC548\xB155\xD558\xC138\xC694", true},
    186     // A valid Japanese word (meaning "hello") consisting of five Hiragana
    187     // letters
    188     {L"\x3053\x3093\x306B\x3061\x306F", true},
    189     // A valid Hindi word (meaning ?) consisting of six Devanagari letters
    190     // (This word is copied from "http://b/issue?id=857583".)
    191     {L"\x0930\x093E\x091C\x0927\x093E\x0928", true},
    192     // A valid English word "affix" using a Latin ligature 'ffi'
    193     {L"a\xFB03x", true},
    194     // A valid English word "hello" (fullwidth version)
    195     {L"\xFF28\xFF45\xFF4C\xFF4C\xFF4F", true},
    196     // Two valid Greek words (meaning "hello") consisting of seven Greek
    197     // letters
    198     {L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5", true},
    199     // A valid Russian word (meaning "hello") consisting of twelve Cyrillic
    200     // letters
    201     {L"\x0437\x0434\x0440\x0430\x0432\x0441"
    202      L"\x0442\x0432\x0443\x0439\x0442\x0435", true},
    203     // A valid English contraction
    204     {L"isn't", true},
    205     // A valid English word enclosed with underscores.
    206     {L"_hello_", true},
    207 
    208     // A valid English word with a preceding whitespace
    209     {L" " L"hello", true},
    210     // A valid English word with a preceding no-break space
    211     {L"\xA0" L"hello", true},
    212     // A valid English word with a preceding ideographic space
    213     {L"\x3000" L"hello", true},
    214     // A valid English word with a preceding Chinese word
    215     {L"\x4F60\x597D" L"hello", true},
    216     // [ROBUSTNESS] A valid English word with a preceding Korean word
    217     {L"\xC548\xB155\xD558\xC138\xC694" L"hello", true},
    218     // A valid English word with a preceding Japanese word
    219     {L"\x3053\x3093\x306B\x3061\x306F" L"hello", true},
    220     // [ROBUSTNESS] A valid English word with a preceding Hindi word
    221     {L"\x0930\x093E\x091C\x0927\x093E\x0928" L"hello", true},
    222     // [ROBUSTNESS] A valid English word with two preceding Greek words
    223     {L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5"
    224      L"hello", true},
    225     // [ROBUSTNESS] A valid English word with a preceding Russian word
    226     {L"\x0437\x0434\x0440\x0430\x0432\x0441"
    227      L"\x0442\x0432\x0443\x0439\x0442\x0435" L"hello", true},
    228 
    229     // A valid English word with a following whitespace
    230     {L"hello" L" ", true},
    231     // A valid English word with a following no-break space
    232     {L"hello" L"\xA0", true},
    233     // A valid English word with a following ideographic space
    234     {L"hello" L"\x3000", true},
    235     // A valid English word with a following Chinese word
    236     {L"hello" L"\x4F60\x597D", true},
    237     // [ROBUSTNESS] A valid English word with a following Korean word
    238     {L"hello" L"\xC548\xB155\xD558\xC138\xC694", true},
    239     // A valid English word with a following Japanese word
    240     {L"hello" L"\x3053\x3093\x306B\x3061\x306F", true},
    241     // [ROBUSTNESS] A valid English word with a following Hindi word
    242     {L"hello" L"\x0930\x093E\x091C\x0927\x093E\x0928", true},
    243     // [ROBUSTNESS] A valid English word with two following Greek words
    244     {L"hello"
    245      L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5", true},
    246     // [ROBUSTNESS] A valid English word with a following Russian word
    247     {L"hello" L"\x0437\x0434\x0440\x0430\x0432\x0441"
    248      L"\x0442\x0432\x0443\x0439\x0442\x0435", true},
    249 
    250     // Two valid English words concatenated with a whitespace
    251     {L"hello" L" " L"hello", true},
    252     // Two valid English words concatenated with a no-break space
    253     {L"hello" L"\xA0" L"hello", true},
    254     // Two valid English words concatenated with an ideographic space
    255     {L"hello" L"\x3000" L"hello", true},
    256     // Two valid English words concatenated with a Chinese word
    257     {L"hello" L"\x4F60\x597D" L"hello", true},
    258     // [ROBUSTNESS] Two valid English words concatenated with a Korean word
    259     {L"hello" L"\xC548\xB155\xD558\xC138\xC694" L"hello", true},
    260     // Two valid English words concatenated with a Japanese word
    261     {L"hello" L"\x3053\x3093\x306B\x3061\x306F" L"hello", true},
    262     // [ROBUSTNESS] Two valid English words concatenated with a Hindi word
    263     {L"hello" L"\x0930\x093E\x091C\x0927\x093E\x0928" L"hello" , true},
    264     // [ROBUSTNESS] Two valid English words concatenated with two Greek words
    265     {L"hello" L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5"
    266      L"hello", true},
    267     // [ROBUSTNESS] Two valid English words concatenated with a Russian word
    268     {L"hello" L"\x0437\x0434\x0440\x0430\x0432\x0441"
    269      L"\x0442\x0432\x0443\x0439\x0442\x0435" L"hello", true},
    270     // [ROBUSTNESS] Two valid English words concatenated with a contraction
    271     // character.
    272     {L"hello:hello", true},
    273 
    274     // An invalid English word
    275     {L"ifmmp", false, 0, 5},
    276     // An invalid English word "bffly" containing a Latin ligature 'ffl'
    277     {L"b\xFB04y", false, 0, 3},
    278     // An invalid English word "ifmmp" (fullwidth version)
    279     {L"\xFF29\xFF46\xFF4D\xFF4D\xFF50", false, 0, 5},
    280     // An invalid English contraction
    281     {L"jtm'u", false, 0, 5},
    282     // An invalid English word enclosed with underscores.
    283     {L"_ifmmp_", false, 1, 5},
    284 
    285     // An invalid English word with a preceding whitespace
    286     {L" " L"ifmmp", false, 1, 5},
    287     // An invalid English word with a preceding no-break space
    288     {L"\xA0" L"ifmmp", false, 1, 5},
    289     // An invalid English word with a preceding ideographic space
    290     {L"\x3000" L"ifmmp", false, 1, 5},
    291     // An invalid English word with a preceding Chinese word
    292     {L"\x4F60\x597D" L"ifmmp", false, 2, 5},
    293     // [ROBUSTNESS] An invalid English word with a preceding Korean word
    294     {L"\xC548\xB155\xD558\xC138\xC694" L"ifmmp", false, 5, 5},
    295     // An invalid English word with a preceding Japanese word
    296     {L"\x3053\x3093\x306B\x3061\x306F" L"ifmmp", false, 5, 5},
    297     // [ROBUSTNESS] An invalid English word with a preceding Hindi word
    298     {L"\x0930\x093E\x091C\x0927\x093E\x0928" L"ifmmp", false, 6, 5},
    299     // [ROBUSTNESS] An invalid English word with two preceding Greek words
    300     {L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5"
    301      L"ifmmp", false, 8, 5},
    302     // [ROBUSTNESS] An invalid English word with a preceding Russian word
    303     {L"\x0437\x0434\x0440\x0430\x0432\x0441"
    304      L"\x0442\x0432\x0443\x0439\x0442\x0435" L"ifmmp", false, 12, 5},
    305 
    306     // An invalid English word with a following whitespace
    307     {L"ifmmp" L" ", false, 0, 5},
    308     // An invalid English word with a following no-break space
    309     {L"ifmmp" L"\xA0", false, 0, 5},
    310     // An invalid English word with a following ideographic space
    311     {L"ifmmp" L"\x3000", false, 0, 5},
    312     // An invalid English word with a following Chinese word
    313     {L"ifmmp" L"\x4F60\x597D", false, 0, 5},
    314     // [ROBUSTNESS] An invalid English word with a following Korean word
    315     {L"ifmmp" L"\xC548\xB155\xD558\xC138\xC694", false, 0, 5},
    316     // An invalid English word with a following Japanese word
    317     {L"ifmmp" L"\x3053\x3093\x306B\x3061\x306F", false, 0, 5},
    318     // [ROBUSTNESS] An invalid English word with a following Hindi word
    319     {L"ifmmp" L"\x0930\x093E\x091C\x0927\x093E\x0928", false, 0, 5},
    320     // [ROBUSTNESS] An invalid English word with two following Greek words
    321     {L"ifmmp"
    322      L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5", false, 0, 5},
    323     // [ROBUSTNESS] An invalid English word with a following Russian word
    324     {L"ifmmp" L"\x0437\x0434\x0440\x0430\x0432\x0441"
    325      L"\x0442\x0432\x0443\x0439\x0442\x0435", false, 0, 5},
    326 
    327     // Two invalid English words concatenated with a whitespace
    328     {L"ifmmp" L" " L"ifmmp", false, 0, 5},
    329     // Two invalid English words concatenated with a no-break space
    330     {L"ifmmp" L"\xA0" L"ifmmp", false, 0, 5},
    331     // Two invalid English words concatenated with an ideographic space
    332     {L"ifmmp" L"\x3000" L"ifmmp", false, 0, 5},
    333     // Two invalid English words concatenated with a Chinese word
    334     {L"ifmmp" L"\x4F60\x597D" L"ifmmp", false, 0, 5},
    335     // [ROBUSTNESS] Two invalid English words concatenated with a Korean word
    336     {L"ifmmp" L"\xC548\xB155\xD558\xC138\xC694" L"ifmmp", false, 0, 5},
    337     // Two invalid English words concatenated with a Japanese word
    338     {L"ifmmp" L"\x3053\x3093\x306B\x3061\x306F" L"ifmmp", false, 0, 5},
    339     // [ROBUSTNESS] Two invalid English words concatenated with a Hindi word
    340     {L"ifmmp" L"\x0930\x093E\x091C\x0927\x093E\x0928" L"ifmmp" , false, 0, 5},
    341     // [ROBUSTNESS] Two invalid English words concatenated with two Greek words
    342     {L"ifmmp" L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5"
    343      L"ifmmp", false, 0, 5},
    344     // [ROBUSTNESS] Two invalid English words concatenated with a Russian word
    345     {L"ifmmp" L"\x0437\x0434\x0440\x0430\x0432\x0441"
    346      L"\x0442\x0432\x0443\x0439\x0442\x0435" L"ifmmp", false, 0, 5},
    347     // [ROBUSTNESS] Two invalid English words concatenated with a contraction
    348     // character.
    349     {L"ifmmp:ifmmp", false, 0, 11},
    350 
    351     // [REGRESSION] Issue 13432: "Any word of 13 or 14 characters is not
    352     // spellcheck" <http://crbug.com/13432>.
    353     {L"qwertyuiopasd", false, 0, 13},
    354     {L"qwertyuiopasdf", false, 0, 14},
    355 
    356     // [REGRESSION] Issue 128896: "en_US hunspell dictionary includes
    357     // acknowledgement but not acknowledgements" <http://crbug.com/128896>
    358     {L"acknowledgement", true},
    359     {L"acknowledgements", true},
    360 
    361     // Issue 123290: "Spellchecker should treat numbers as word characters"
    362     {L"0th", true},
    363     {L"1st", true},
    364     {L"2nd", true},
    365     {L"3rd", true},
    366     {L"4th", true},
    367     {L"5th", true},
    368     {L"6th", true},
    369     {L"7th", true},
    370     {L"8th", true},
    371     {L"9th", true},
    372     {L"10th", true},
    373     {L"100th", true},
    374     {L"1000th", true},
    375     {L"25", true},
    376     {L"2012", true},
    377     {L"100,000,000", true},
    378     {L"3.141592653", true},
    379 
    380   };
    381 
    382   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
    383     size_t input_length = 0;
    384     if (kTestCases[i].input != NULL) {
    385       input_length = wcslen(kTestCases[i].input);
    386     }
    387     int misspelling_start;
    388     int misspelling_length;
    389     bool result = spell_check()->SpellCheckWord(
    390         base::WideToUTF16(kTestCases[i].input).c_str(),
    391         static_cast<int>(input_length),
    392         0,
    393         &misspelling_start,
    394         &misspelling_length, NULL);
    395 
    396     EXPECT_EQ(kTestCases[i].expected_result, result);
    397     EXPECT_EQ(kTestCases[i].misspelling_start, misspelling_start);
    398     EXPECT_EQ(kTestCases[i].misspelling_length, misspelling_length);
    399   }
    400 }
    401 
    402 TEST_F(SpellCheckTest, SpellCheckSuggestions_EN_US) {
    403   static const struct {
    404     // A string to be tested.
    405     const wchar_t* input;
    406     // An expected result for this test case.
    407     //   * true: the input string does not have any invalid words.
    408     //   * false: the input string has one or more invalid words.
    409     bool expected_result;
    410     // The position and the length of the first invalid word.
    411     int misspelling_start;
    412     int misspelling_length;
    413 
    414     // A suggested word that should occur.
    415     const wchar_t* suggested_word;
    416   } kTestCases[] = {
    417     {L"ello", false, 0, 0, L"hello"},
    418     {L"ello", false, 0, 0, L"cello"},
    419     {L"wate", false, 0, 0, L"water"},
    420     {L"wate", false, 0, 0, L"waste"},
    421     {L"wate", false, 0, 0, L"sate"},
    422     {L"wate", false, 0, 0, L"ate"},
    423     {L"jum", false, 0, 0, L"jump"},
    424     {L"jum", false, 0, 0, L"hum"},
    425     {L"jum", false, 0, 0, L"sum"},
    426     {L"jum", false, 0, 0, L"um"},
    427     // A regression test for Issue 36523.
    428     {L"privliged", false, 0, 0, L"privileged"},
    429     // TODO (Sidchat): add many more examples.
    430   };
    431 
    432   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
    433     std::vector<base::string16> suggestions;
    434     size_t input_length = 0;
    435     if (kTestCases[i].input != NULL) {
    436       input_length = wcslen(kTestCases[i].input);
    437     }
    438     int misspelling_start;
    439     int misspelling_length;
    440     bool result = spell_check()->SpellCheckWord(
    441         base::WideToUTF16(kTestCases[i].input).c_str(),
    442         static_cast<int>(input_length),
    443         0,
    444         &misspelling_start,
    445         &misspelling_length,
    446         &suggestions);
    447 
    448     // Check for spelling.
    449     EXPECT_EQ(kTestCases[i].expected_result, result);
    450 
    451     // Check if the suggested words occur.
    452     bool suggested_word_is_present = false;
    453     for (int j = 0; j < static_cast<int>(suggestions.size()); j++) {
    454       if (suggestions.at(j).compare(
    455               base::WideToUTF16(kTestCases[i].suggested_word)) == 0) {
    456         suggested_word_is_present = true;
    457         break;
    458       }
    459     }
    460 
    461     EXPECT_TRUE(suggested_word_is_present);
    462   }
    463 }
    464 
    465 // This test verifies our spellchecker can split a text into words and check
    466 // the spelling of each word in the text.
    467 #if defined(THREAD_SANITIZER)
    468 // SpellCheckTest.SpellCheckText fails under ThreadSanitizer v2.
    469 // See http://crbug.com/217909.
    470 #define MAYBE_SpellCheckText DISABLED_SpellCheckText
    471 #else
    472 #define MAYBE_SpellCheckText SpellCheckText
    473 #endif  // THREAD_SANITIZER
    474 TEST_F(SpellCheckTest, MAYBE_SpellCheckText) {
    475   static const struct {
    476     const char* language;
    477     const wchar_t* input;
    478   } kTestCases[] = {
    479     {
    480       // Afrikaans
    481       "af-ZA",
    482       L"Google se missie is om die w\x00EAreld se inligting te organiseer en "
    483       L"dit bruikbaar en toeganklik te maak."
    484     }, {
    485       // Catalan
    486       "ca-ES",
    487       L"La missi\x00F3 de Google \x00E9s organitzar la informaci\x00F3 "
    488       L"del m\x00F3n i fer que sigui \x00FAtil i accessible universalment."
    489     }, {
    490       // Czech
    491       "cs-CZ",
    492       L"Posl\x00E1n\x00EDm spole\x010Dnosti Google je "
    493       L"uspo\x0159\x00E1\x0064\x0061t informace z cel\x00E9ho sv\x011Bta "
    494       L"tak, aby byly v\x0161\x0065obecn\x011B p\x0159\x00EDstupn\x00E9 "
    495       L"a u\x017Eite\x010Dn\x00E9."
    496     }, {
    497       // Danish
    498       "da-DK",
    499       L"Googles "
    500       L"mission er at organisere verdens information og g\x00F8re den "
    501       L"almindeligt tilg\x00E6ngelig og nyttig."
    502     }, {
    503       // German
    504       "de-DE",
    505       L"Das Ziel von Google besteht darin, die auf der Welt vorhandenen "
    506       L"Informationen zu organisieren und allgemein zug\x00E4nglich und "
    507       L"nutzbar zu machen."
    508     }, {
    509       // Greek
    510       "el-GR",
    511       L"\x0391\x03C0\x03BF\x03C3\x03C4\x03BF\x03BB\x03AE "
    512       L"\x03C4\x03B7\x03C2 Google \x03B5\x03AF\x03BD\x03B1\x03B9 "
    513       L"\x03BD\x03B1 \x03BF\x03C1\x03B3\x03B1\x03BD\x03CE\x03BD\x03B5\x03B9 "
    514       L"\x03C4\x03B9\x03C2 "
    515       L"\x03C0\x03BB\x03B7\x03C1\x03BF\x03C6\x03BF\x03C1\x03AF\x03B5\x03C2 "
    516       L"\x03C4\x03BF\x03C5 \x03BA\x03CC\x03C3\x03BC\x03BF\x03C5 "
    517       L"\x03BA\x03B1\x03B9 \x03BD\x03B1 \x03C4\x03B9\x03C2 "
    518       L"\x03BA\x03B1\x03B8\x03B9\x03C3\x03C4\x03AC "
    519       L"\x03C0\x03C1\x03BF\x03C3\x03B2\x03AC\x03C3\x03B9\x03BC\x03B5\x03C2 "
    520       L"\x03BA\x03B1\x03B9 \x03C7\x03C1\x03AE\x03C3\x03B9\x03BC\x03B5\x03C2."
    521     }, {
    522       // English (Australia)
    523       "en-AU",
    524       L"Google's mission is to organise the world's information and make it "
    525       L"universally accessible and useful."
    526     }, {
    527       // English (Canada)
    528       "en-CA",
    529       L"Google's mission is to organize the world's information and make it "
    530       L"universally accessible and useful."
    531     }, {
    532       // English (United Kingdom)
    533       "en-GB",
    534       L"Google's mission is to organise the world's information and make it "
    535       L"universally accessible and useful."
    536     }, {
    537       // English (United States)
    538       "en-US",
    539       L"Google's mission is to organize the world's information and make it "
    540       L"universally accessible and useful."
    541     }, {
    542       // Bulgarian
    543       "bg-BG",
    544       L"\x041c\x0438\x0441\x0438\x044f\x0442\x0430 "
    545       L"\x043d\x0430 Google \x0435 \x0434\x0430 \x043e"
    546       L"\x0440\x0433\x0430\x043d\x0438\x0437\x0438\x0440"
    547       L"\x0430 \x0441\x0432\x0435\x0442\x043e\x0432"
    548       L"\x043d\x0430\x0442\x0430 \x0438\x043d\x0444"
    549       L"\x043e\x0440\x043c\x0430\x0446\x0438\x044f "
    550       L"\x0438 \x0434\x0430 \x044f \x043d"
    551       L"\x0430\x043f\x0440\x0430\x0432\x0438 \x0443"
    552       L"\x043d\x0438\x0432\x0435\x0440\x0441\x0430\x043b"
    553       L"\x043d\x043e \x0434\x043e\x0441\x0442\x044a"
    554       L"\x043f\x043d\x0430 \x0438 \x043f\x043e"
    555       L"\x043b\x0435\x0437\x043d\x0430."
    556     }, {
    557       // Spanish
    558       "es-ES",
    559       L"La misi\x00F3n de "
    560       // L"Google" - to be added.
    561       L" es organizar la informaci\x00F3n mundial "
    562       L"para que resulte universalmente accesible y \x00FAtil."
    563     }, {
    564       // Estonian
    565       "et-EE",
    566       // L"Google'ile " - to be added.
    567       L"\x00FClesanne on korraldada maailma teavet ja teeb selle "
    568       L"k\x00F5igile k\x00E4ttesaadavaks ja kasulikuks.",
    569     }, {
    570       // Faroese
    571       "fo-FO",
    572       L"Google er at samskipa alla vitan \x00ED heiminum og gera hana alment "
    573       L"atkomiliga og n\x00FDtiliga."
    574     }, {
    575       // French
    576       "fr-FR",
    577       L"Google a pour mission d'organiser les informations \x00E0 "
    578       L"l'\x00E9\x0063helle mondiale dans le but de les rendre accessibles "
    579       L"et utiles \x00E0 tous."
    580     }, {
    581       // Hebrew
    582       "he-IL",
    583       L"\x05D4\x05DE\x05E9\x05D9\x05DE\x05D4 \x05E9\x05DC Google "
    584       L"\x05D4\x05D9\x05D0 \x05DC\x05D0\x05E8\x05D2\x05DF "
    585       L"\x05D0\x05EA \x05D4\x05DE\x05D9\x05D3\x05E2 "
    586       L"\x05D4\x05E2\x05D5\x05DC\x05DE\x05D9 "
    587       L"\x05D5\x05DC\x05D4\x05E4\x05D5\x05DA \x05D0\x05D5\x05EA\x05D5 "
    588       L"\x05DC\x05D6\x05DE\x05D9\x05DF "
    589       L"\x05D5\x05E9\x05D9\x05DE\x05D5\x05E9\x05D9 \x05D1\x05DB\x05DC "
    590       L"\x05D4\x05E2\x05D5\x05DC\x05DD. "
    591       // Two words with ASCII double/single quoation marks.
    592       L"\x05DE\x05E0\x05DB\x0022\x05DC \x05E6\x0027\x05D9\x05E4\x05E1"
    593     }, {
    594       // Hindi
    595       "hi-IN",
    596       L"Google \x0915\x093E \x092E\x093F\x0936\x0928 "
    597       L"\x0926\x0941\x0928\x093F\x092F\x093E \x0915\x0940 "
    598       L"\x091C\x093E\x0928\x0915\x093E\x0930\x0940 \x0915\x094B "
    599       L"\x0935\x094D\x092F\x0935\x0938\x094D\x0925\x093F\x0924 "
    600       L"\x0915\x0930\x0928\x093E \x0914\x0930 \x0909\x0938\x0947 "
    601       L"\x0938\x093E\x0930\x094D\x0935\x092D\x094C\x092E\x093F\x0915 "
    602       L"\x0930\x0942\x092A \x0938\x0947 \x092A\x0939\x0941\x0901\x091A "
    603       L"\x092E\x0947\x0902 \x0914\x0930 \x0909\x092A\x092F\x094B\x0917\x0940 "
    604       L"\x092C\x0928\x093E\x0928\x093E \x0939\x0948."
    605     }, {
    606       // Hungarian
    607       "hu-HU",
    608       L"A Google azt a k\x00FCldet\x00E9st v\x00E1llalta mag\x00E1ra, "
    609       L"hogy a vil\x00E1gon fellelhet\x0151 inform\x00E1\x0063i\x00F3kat "
    610       L"rendszerezze \x00E9s \x00E1ltal\x00E1nosan el\x00E9rhet\x0151v\x00E9, "
    611       L"illetve haszn\x00E1lhat\x00F3v\x00E1 tegye."
    612     }, {
    613       // Croatian
    614       "hr-HR",
    615       // L"Googleova " - to be added.
    616       L"je misija organizirati svjetske informacije i u\x010Diniti ih "
    617       // L"univerzalno " - to be added.
    618       L"pristupa\x010Dnima i korisnima."
    619     }, {
    620       // Indonesian
    621       "id-ID",
    622       L"Misi Google adalah untuk mengelola informasi dunia dan membuatnya "
    623       L"dapat diakses dan bermanfaat secara universal."
    624     }, {
    625       // Italian
    626       "it-IT",
    627       L"La missione di Google \x00E8 organizzare le informazioni a livello "
    628       L"mondiale e renderle universalmente accessibili e fruibili."
    629     }, {
    630       // Lithuanian
    631       "lt-LT",
    632       L"\x201EGoogle\x201C tikslas \x2013 rinkti ir sisteminti pasaulio "
    633       L"informacij\x0105 bei padaryti j\x0105 prieinam\x0105 ir "
    634       L"nauding\x0105 visiems."
    635     }, {
    636       // Latvian
    637       "lv-LV",
    638       L"Google uzdevums ir k\x0101rtot pasaules inform\x0101"
    639       L"ciju un padar\x012Bt to univers\x0101li pieejamu un noder\x012Bgu."
    640     }, {
    641       // Norwegian
    642       "nb-NO",
    643       // L"Googles " - to be added.
    644       L"m\x00E5l er \x00E5 organisere informasjonen i verden og "
    645       L"gj\x00F8re den tilgjengelig og nyttig for alle."
    646     }, {
    647       // Dutch
    648       "nl-NL",
    649       L"Het doel van Google is om alle informatie wereldwijd toegankelijk "
    650       L"en bruikbaar te maken."
    651     }, {
    652       // Polish
    653       "pl-PL",
    654       L"Misj\x0105 Google jest uporz\x0105" L"dkowanie \x015Bwiatowych "
    655       L"zasob\x00F3w informacji, aby sta\x0142y si\x0119 one powszechnie "
    656       L"dost\x0119pne i u\x017Cyteczne."
    657     }, {
    658       // Portuguese (Brazil)
    659       "pt-BR",
    660       L"A miss\x00E3o do "
    661 #if !defined(OS_MACOSX)
    662       L"Google "
    663 #endif
    664       L"\x00E9 organizar as informa\x00E7\x00F5"
    665       L"es do mundo todo e "
    666 #if !defined(OS_MACOSX)
    667       L"torn\x00E1-las "
    668 #endif
    669       L"acess\x00EDveis e \x00FAteis em car\x00E1ter universal."
    670     }, {
    671       // Portuguese (Portugal)
    672       "pt-PT",
    673       L"O "
    674 #if !defined(OS_MACOSX)
    675       L"Google "
    676 #endif
    677       L"tem por miss\x00E3o organizar a informa\x00E7\x00E3o do "
    678       L"mundo e "
    679 #if !defined(OS_MACOSX)
    680       L"torn\x00E1-la "
    681 #endif
    682       L"universalmente acess\x00EDvel e \x00FAtil"
    683     }, {
    684       // Romanian
    685       "ro-RO",
    686       L"Misiunea Google este de a organiza informa\x021B3iile lumii \x0219i de "
    687       L"a le face accesibile \x0219i utile la nivel universal."
    688     }, {
    689       // Russian
    690       "ru-RU",
    691       L"\x041C\x0438\x0441\x0441\x0438\x044F Google "
    692       L"\x0441\x043E\x0441\x0442\x043E\x0438\x0442 \x0432 "
    693       L"\x043E\x0440\x0433\x0430\x043D\x0438\x0437\x0430\x0446\x0438\x0438 "
    694       L"\x043C\x0438\x0440\x043E\x0432\x043E\x0439 "
    695       L"\x0438\x043D\x0444\x043E\x0440\x043C\x0430\x0446\x0438\x0438, "
    696       L"\x043E\x0431\x0435\x0441\x043F\x0435\x0447\x0435\x043D\x0438\x0438 "
    697       L"\x0435\x0435 "
    698       L"\x0434\x043E\x0441\x0442\x0443\x043F\x043D\x043E\x0441\x0442\x0438 "
    699       L"\x0438 \x043F\x043E\x043B\x044C\x0437\x044B \x0434\x043B\x044F "
    700       L"\x0432\x0441\x0435\x0445."
    701       // A Russian word including U+0451. (Bug 15558 <http://crbug.com/15558>)
    702       L"\x0451\x043B\x043A\x0430"
    703     }, {
    704       // Serbo-Croatian (Serbian Latin)
    705       "sh",
    706       L"Google-ova misija je da organizuje sve informacije na svetu i "
    707       L"u\x010dini ih univerzal-no dostupnim i korisnim."
    708     }, {
    709       // Serbian
    710       "sr",
    711       L"\x0047\x006f\x006f\x0067\x006c\x0065\x002d\x043e\x0432\x0430 "
    712       L"\x043c\x0438\x0441\x0438\x0458\x0430 \x0458\x0435 \x0434\x0430 "
    713       L"\x043e\x0440\x0433\x0430\x043d\x0438\x0437\x0443\x0458\x0435 "
    714       L"\x0441\x0432\x0435 "
    715       L"\x0438\x043d\x0444\x043e\x0440\x043c\x0430\x0446\x0438\x0458\x0435 "
    716       L"\x043d\x0430 \x0441\x0432\x0435\x0442\x0443 \x0438 "
    717       L"\x0443\x0447\x0438\x043d\x0438 \x0438\x0445 "
    718       L"\x0443\x043d\x0438\x0432\x0435\x0440\x0437\x0430\x043b\x043d\x043e "
    719       L"\x0434\x043e\x0441\x0442\x0443\x043f\x043d\x0438\x043c \x0438 "
    720       L"\x043a\x043e\x0440\x0438\x0441\x043d\x0438\x043c."
    721     }, {
    722       // Slovak
    723       "sk-SK",
    724       L"Spolo\x010Dnos\x0165 Google si dala za \x00FAlohu usporiada\x0165 "
    725       L"inform\x00E1\x0063ie "
    726       L"z cel\x00E9ho sveta a zabezpe\x010Di\x0165, "
    727       L"aby boli v\x0161eobecne dostupn\x00E9 a u\x017Eito\x010Dn\x00E9."
    728     }, {
    729       // Slovenian
    730       "sl-SI",
    731       // L"Googlovo " - to be added.
    732       L"poslanstvo je organizirati svetovne informacije in "
    733       L"omogo\x010Diti njihovo dostopnost in s tem uporabnost za vse."
    734     }, {
    735       // Swedish
    736       "sv-SE",
    737       L"Googles m\x00E5ls\x00E4ttning \x00E4r att ordna v\x00E4rldens "
    738       L"samlade information och g\x00F6ra den tillg\x00E4nglig f\x00F6r alla."
    739     }, {
    740       // Turkish
    741       "tr-TR",
    742       // L"Google\x2019\x0131n " - to be added.
    743       L"misyonu, d\x00FCnyadaki t\x00FCm bilgileri "
    744       L"organize etmek ve evrensel olarak eri\x015Filebilir ve "
    745       L"kullan\x0131\x015Fl\x0131 k\x0131lmakt\x0131r."
    746     }, {
    747       // Ukranian
    748       "uk-UA",
    749       L"\x041c\x0456\x0441\x0456\x044f "
    750       L"\x043a\x043e\x043c\x043f\x0430\x043d\x0456\x0457 Google "
    751       L"\x043f\x043e\x043b\x044f\x0433\x0430\x0454 \x0432 "
    752       L"\x0442\x043e\x043c\x0443, \x0449\x043e\x0431 "
    753       L"\x0443\x043f\x043e\x0440\x044f\x0434\x043a\x0443\x0432\x0430\x0442"
    754       L"\x0438 \x0456\x043d\x0444\x043e\x0440\x043c\x0430\x0446\x0456\x044e "
    755       L"\x0437 \x0443\x0441\x044c\x043e\x0433\x043e "
    756       L"\x0441\x0432\x0456\x0442\x0443 \x0442\x0430 "
    757       L"\x0437\x0440\x043e\x0431\x0438\x0442\x0438 \x0457\x0457 "
    758       L"\x0443\x043d\x0456\x0432\x0435\x0440\x0441\x0430\x043b\x044c\x043d"
    759       L"\x043e \x0434\x043e\x0441\x0442\x0443\x043f\x043d\x043e\x044e "
    760       L"\x0442\x0430 \x043a\x043e\x0440\x0438\x0441\x043d\x043e\x044e."
    761     }, {
    762       // Vietnamese
    763       "vi-VN",
    764       L"Nhi\x1EC7m v\x1EE5 c\x1EE7\x0061 "
    765       L"Google la \x0111\x1EC3 t\x1ED5 ch\x1EE9\x0063 "
    766       L"c\x00E1\x0063 th\x00F4ng tin c\x1EE7\x0061 "
    767       L"th\x1EBF gi\x1EDBi va l\x00E0m cho n\x00F3 universal c\x00F3 "
    768       L"th\x1EC3 truy c\x1EADp va h\x1EEFu d\x1EE5ng h\x01A1n."
    769     }, {
    770       // Korean
    771       "ko",
    772       L"Google\xC758 \xBAA9\xD45C\xB294 \xC804\xC138\xACC4\xC758 "
    773       L"\xC815\xBCF4\xB97C \xCCB4\xACC4\xD654\xD558\xC5EC \xBAA8\xB450\xAC00 "
    774       L"\xD3B8\xB9AC\xD558\xAC8C \xC774\xC6A9\xD560 \xC218 "
    775       L"\xC788\xB3C4\xB85D \xD558\xB294 \xAC83\xC785\xB2C8\xB2E4."
    776     }, {
    777       // Albanian
    778       "sq",
    779       L"Misioni i Google \x00EBsht\x00EB q\x00EB t\x00EB organizoj\x00EB "
    780       L"informacionin e bot\x00EBs dhe t\x00EB b\x00EBjn\x00EB at\x00EB "
    781       L"universalisht t\x00EB arritshme dhe t\x00EB dobishme."
    782     }, {
    783       // Tamil
    784       "ta",
    785       L"Google \x0B87\x0BA9\x0BCD "
    786       L"\x0BA8\x0BC7\x0BBE\x0B95\x0BCD\x0B95\x0BAE\x0BCD "
    787       L"\x0B89\x0BB2\x0B95\x0BBF\x0BA9\x0BCD \x0BA4\x0B95\x0BB5\x0BB2\x0BCD "
    788       L"\x0B8F\x0BB1\x0BCD\x0BAA\x0BBE\x0B9F\x0BC1 \x0B87\x0BA4\x0BC1 "
    789       L"\x0B89\x0BB2\x0B95\x0BB3\x0BBE\x0BB5\x0BBF\x0BAF "
    790       L"\x0B85\x0BA3\x0BC1\x0B95\x0B95\x0BCD \x0B95\x0BC2\x0B9F\x0BBF\x0BAF "
    791       L"\x0BAE\x0BB1\x0BCD\x0BB1\x0BC1\x0BAE\x0BCD "
    792       L"\x0BAA\x0BAF\x0BA9\x0BC1\x0BB3\x0BCD\x0BB3 "
    793       L"\x0B9A\x0BC6\x0BAF\x0BCD\x0BAF \x0B89\x0BB3\x0BCD\x0BB3\x0BA4\x0BC1."
    794     }, {
    795       // Tajik
    796       "tg",
    797       L"\x041c\x0438\x0441\x0441\x0438\x044f\x0438 Google \x0438\x043d "
    798       L"\x043c\x0443\x0440\x0430\x0442\x0442\x0430\x0431 "
    799       L"\x0441\x043e\x0445\x0442\x0430\x043d\x0438 "
    800       L"\x043c\x0430\x044a\x043b\x0443\x043c\x043e\x0442\x04b3\x043e\x0438 "
    801       L"\x043c\x0430\x0432\x04b7\x0443\x0434\x0430, \x043e\x0441\x043e\x043d "
    802       L"\x043d\x0430\x043c\x0443\x0434\x0430\x043d\x0438 "
    803       L"\x0438\x0441\x0442\x0438\x0444\x043e\x0434\x0430\x0431\x0430\x0440"
    804       L"\x04e3 \x0432\x0430 \x0434\x0430\x0441\x0442\x0440\x0430\x0441\x0438 "
    805       L"\x0443\x043c\x0443\x043c "
    806       L"\x0433\x0430\x0440\x0434\x043e\x043d\x0438\x0434\x0430\x043d\x0438 "
    807       L"\x043e\x043d\x04b3\x043e \x0430\x0441\x0442."
    808     },
    809   };
    810 
    811   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
    812     ReinitializeSpellCheck(kTestCases[i].language);
    813     size_t input_length = 0;
    814     if (kTestCases[i].input != NULL)
    815       input_length = wcslen(kTestCases[i].input);
    816 
    817     int misspelling_start = 0;
    818     int misspelling_length = 0;
    819     bool result = spell_check()->SpellCheckWord(
    820         base::WideToUTF16(kTestCases[i].input).c_str(),
    821         static_cast<int>(input_length),
    822         0,
    823         &misspelling_start,
    824         &misspelling_length, NULL);
    825 
    826     EXPECT_TRUE(result)
    827         << "\""
    828         << std::wstring(kTestCases[i].input).substr(
    829                misspelling_start, misspelling_length)
    830         << "\" is misspelled in "
    831         << kTestCases[i].language
    832         << ".";
    833     EXPECT_EQ(0, misspelling_start);
    834     EXPECT_EQ(0, misspelling_length);
    835   }
    836 }
    837 
    838 TEST_F(SpellCheckTest, GetAutoCorrectionWord_EN_US) {
    839   static const struct {
    840     // A misspelled word.
    841     const char* input;
    842 
    843     // An expected result for this test case.
    844     // Should be an empty string if there are no suggestions for auto correct.
    845     const char* expected_result;
    846   } kTestCases[] = {
    847     {"teh", "the"},
    848     {"moer", "more"},
    849     {"watre", "water"},
    850     {"noen", ""},
    851     {"what", ""},
    852   };
    853 
    854   EnableAutoCorrect(true);
    855 
    856   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
    857     base::string16 misspelled_word(base::UTF8ToUTF16(kTestCases[i].input));
    858     base::string16 expected_autocorrect_word(
    859         base::UTF8ToUTF16(kTestCases[i].expected_result));
    860     base::string16 autocorrect_word = spell_check()->GetAutoCorrectionWord(
    861         misspelled_word, 0);
    862 
    863     // Check for spelling.
    864     EXPECT_EQ(expected_autocorrect_word, autocorrect_word);
    865   }
    866 }
    867 
    868 // Verify that our SpellCheck::SpellCheckWord() returns false when it checks
    869 // misspelled words.
    870 TEST_F(SpellCheckTest, MisspelledWords) {
    871   static const struct {
    872     const char* language;
    873     const wchar_t* input;
    874   } kTestCases[] = {
    875     {
    876       // A misspelled word for English
    877       "en-US",
    878       L"aaaaaaaaaa",
    879     }, {
    880       // A misspelled word for Greek.
    881       "el-GR",
    882       L"\x03B1\x03B1\x03B1\x03B1\x03B1\x03B1\x03B1\x03B1\x03B1\x03B1",
    883     }, {
    884       // A misspelled word for Hebrew
    885       "he-IL",
    886       L"\x05D0\x05D0\x05D0\x05D0\x05D0\x05D0\x05D0\x05D0\x05D0\x05D0",
    887     }, {
    888       // Hindi
    889       "hi-IN",
    890       L"\x0905\x0905\x0905\x0905\x0905\x0905\x0905\x0905\x0905\x0905",
    891     }, {
    892       // A misspelled word for Russian
    893       "ru-RU",
    894       L"\x0430\x0430\x0430\x0430\x0430\x0430\x0430\x0430\x0430\x0430",
    895     },
    896   };
    897 
    898   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
    899     ReinitializeSpellCheck(kTestCases[i].language);
    900 
    901     base::string16 word(base::WideToUTF16(kTestCases[i].input));
    902     int word_length = static_cast<int>(word.length());
    903     int misspelling_start = 0;
    904     int misspelling_length = 0;
    905     bool result = spell_check()->SpellCheckWord(word.c_str(),
    906                                                 word_length,
    907                                                 0,
    908                                                 &misspelling_start,
    909                                                 &misspelling_length,
    910                                                 NULL);
    911     EXPECT_FALSE(result);
    912     EXPECT_EQ(0, misspelling_start);
    913     EXPECT_EQ(word_length, misspelling_length);
    914   }
    915 }
    916 
    917 // Since SpellCheck::SpellCheckParagraph is not implemented on Mac,
    918 // we skip these SpellCheckParagraph tests on Mac.
    919 #if !defined(OS_MACOSX)
    920 
    921 // Make sure SpellCheckParagraph does not crash if the input is empty.
    922 TEST_F(SpellCheckTest, SpellCheckParagraphEmptyParagraph) {
    923   std::vector<SpellCheckResult> expected;
    924   TestSpellCheckParagraph(base::UTF8ToUTF16(""), expected);
    925 }
    926 
    927 // A simple test case having no misspellings.
    928 TEST_F(SpellCheckTest, SpellCheckParagraphNoMisspellings) {
    929   const base::string16 text = base::UTF8ToUTF16("apple");
    930   std::vector<SpellCheckResult> expected;
    931   TestSpellCheckParagraph(text, expected);
    932 }
    933 
    934 // A simple test case having one misspelling.
    935 TEST_F(SpellCheckTest, SpellCheckParagraphSingleMisspellings) {
    936   const base::string16 text = base::UTF8ToUTF16("zz");
    937   std::vector<SpellCheckResult> expected;
    938   expected.push_back(SpellCheckResult(
    939       SpellCheckResult::SPELLING, 0, 2));
    940 
    941   TestSpellCheckParagraph(text, expected);
    942 }
    943 
    944 // A simple test case having multiple misspellings.
    945 TEST_F(SpellCheckTest, SpellCheckParagraphMultipleMisspellings) {
    946   const base::string16 text = base::UTF8ToUTF16("zz, zz");
    947   std::vector<SpellCheckResult> expected;
    948   expected.push_back(SpellCheckResult(
    949       SpellCheckResult::SPELLING, 0, 2));
    950   expected.push_back(SpellCheckResult(
    951       SpellCheckResult::SPELLING, 4, 2));
    952 
    953   TestSpellCheckParagraph(text, expected);
    954 }
    955 
    956 // Make sure a relatively long (correct) sentence can be spellchecked.
    957 TEST_F(SpellCheckTest, SpellCheckParagraphLongSentence) {
    958   std::vector<SpellCheckResult> expected;
    959   // The text is taken from US constitution preamble.
    960   const base::string16 text = base::UTF8ToUTF16(
    961       "We the people of the United States, in order to form a more perfect "
    962       "union, establish justice, insure domestic tranquility, provide for "
    963       "the common defense, promote the general welfare, and secure the "
    964       "blessings of liberty to ourselves and our posterity, do ordain and "
    965       "establish this Constitution for the United States of America.");
    966 
    967   TestSpellCheckParagraph(text, expected);
    968 }
    969 
    970 // Make sure all misspellings can be found in a relatively long sentence.
    971 TEST_F(SpellCheckTest, SpellCheckParagraphLongSentenceMultipleMisspellings) {
    972   std::vector<SpellCheckResult> expected;
    973 
    974   // All 'the' are converted to 'hte' in US consitition preamble.
    975   const base::string16 text = base::UTF8ToUTF16(
    976       "We hte people of hte United States, in order to form a more perfect "
    977       "union, establish justice, insure domestic tranquility, provide for "
    978       "hte common defense, promote hte general welfare, and secure hte "
    979       "blessings of liberty to ourselves and our posterity, do ordain and "
    980       "establish this Constitution for hte United States of America.");
    981 
    982   expected.push_back(SpellCheckResult(
    983       SpellCheckResult::SPELLING, 3, 3));
    984   expected.push_back(SpellCheckResult(
    985       SpellCheckResult::SPELLING, 17, 3));
    986   expected.push_back(SpellCheckResult(
    987       SpellCheckResult::SPELLING, 135, 3));
    988   expected.push_back(SpellCheckResult(
    989       SpellCheckResult::SPELLING, 163, 3));
    990   expected.push_back(SpellCheckResult(
    991       SpellCheckResult::SPELLING, 195, 3));
    992   expected.push_back(SpellCheckResult(
    993       SpellCheckResult::SPELLING, 298, 3));
    994 
    995   TestSpellCheckParagraph(text, expected);
    996 }
    997 
    998 // We also skip RequestSpellCheck tests on Mac, because a system spellchecker
    999 // is used on Mac instead of SpellCheck::RequestTextChecking.
   1000 
   1001 // Make sure RequestTextChecking does not crash if input is empty.
   1002 TEST_F(SpellCheckTest, RequestSpellCheckWithEmptyString) {
   1003   MockTextCheckingCompletion completion;
   1004 
   1005   spell_check()->RequestTextChecking(base::string16(), &completion);
   1006 
   1007   base::MessageLoop::current()->RunUntilIdle();
   1008 
   1009   EXPECT_EQ(completion.completion_count_, 1U);
   1010 }
   1011 
   1012 // A simple test case having no misspellings.
   1013 TEST_F(SpellCheckTest, RequestSpellCheckWithoutMisspelling) {
   1014   MockTextCheckingCompletion completion;
   1015 
   1016   const base::string16 text = base::ASCIIToUTF16("hello");
   1017   spell_check()->RequestTextChecking(text, &completion);
   1018 
   1019   base::MessageLoop::current()->RunUntilIdle();
   1020 
   1021   EXPECT_EQ(completion.completion_count_, 1U);
   1022 }
   1023 
   1024 // A simple test case having one misspelling.
   1025 TEST_F(SpellCheckTest, RequestSpellCheckWithSingleMisspelling) {
   1026   MockTextCheckingCompletion completion;
   1027 
   1028   const base::string16 text = base::ASCIIToUTF16("apple, zz");
   1029   spell_check()->RequestTextChecking(text, &completion);
   1030 
   1031   base::MessageLoop::current()->RunUntilIdle();
   1032 
   1033   EXPECT_EQ(completion.completion_count_, 1U);
   1034   EXPECT_EQ(completion.last_results_.size(), 1U);
   1035   EXPECT_EQ(completion.last_results_[0].location, 7);
   1036   EXPECT_EQ(completion.last_results_[0].length, 2);
   1037 }
   1038 
   1039 // A simple test case having a few misspellings.
   1040 TEST_F(SpellCheckTest, RequestSpellCheckWithMisspellings) {
   1041   MockTextCheckingCompletion completion;
   1042 
   1043   const base::string16 text = base::ASCIIToUTF16("apple, zz, orange, zz");
   1044   spell_check()->RequestTextChecking(text, &completion);
   1045 
   1046   base::MessageLoop::current()->RunUntilIdle();
   1047 
   1048   EXPECT_EQ(completion.completion_count_, 1U);
   1049   EXPECT_EQ(completion.last_results_.size(), 2U);
   1050   EXPECT_EQ(completion.last_results_[0].location, 7);
   1051   EXPECT_EQ(completion.last_results_[0].length, 2);
   1052   EXPECT_EQ(completion.last_results_[1].location, 19);
   1053   EXPECT_EQ(completion.last_results_[1].length, 2);
   1054 }
   1055 
   1056 // A test case that multiple requests comes at once. Make sure all
   1057 // requests are processed.
   1058 TEST_F(SpellCheckTest, RequestSpellCheckWithMultipleRequests) {
   1059   MockTextCheckingCompletion completion[3];
   1060 
   1061   const base::string16 text[3] = {
   1062     base::ASCIIToUTF16("what, zz"),
   1063     base::ASCIIToUTF16("apple, zz"),
   1064     base::ASCIIToUTF16("orange, zz")
   1065   };
   1066 
   1067   for (int i = 0; i < 3; ++i)
   1068     spell_check()->RequestTextChecking(text[i], &completion[i]);
   1069 
   1070   base::MessageLoop::current()->RunUntilIdle();
   1071 
   1072   for (int i = 0; i < 3; ++i) {
   1073     EXPECT_EQ(completion[i].completion_count_, 1U);
   1074     EXPECT_EQ(completion[i].last_results_.size(), 1U);
   1075     EXPECT_EQ(completion[i].last_results_[0].location, 6 + i);
   1076     EXPECT_EQ(completion[i].last_results_[0].length, 2);
   1077   }
   1078 }
   1079 
   1080 // A test case that spellchecking is requested before initializing.
   1081 // In this case, we postpone to post a request.
   1082 TEST_F(SpellCheckTest, RequestSpellCheckWithoutInitialization) {
   1083   UninitializeSpellCheck();
   1084 
   1085   MockTextCheckingCompletion completion;
   1086   const base::string16 text = base::ASCIIToUTF16("zz");
   1087 
   1088   spell_check()->RequestTextChecking(text, &completion);
   1089 
   1090   // The task will not be posted yet.
   1091   base::MessageLoop::current()->RunUntilIdle();
   1092   EXPECT_EQ(completion.completion_count_, 0U);
   1093 }
   1094 
   1095 // Requests several spellchecking before initializing. Except the last one,
   1096 // posting requests is cancelled and text is rendered as correct one.
   1097 TEST_F(SpellCheckTest, RequestSpellCheckMultipleTimesWithoutInitialization) {
   1098   UninitializeSpellCheck();
   1099 
   1100   MockTextCheckingCompletion completion[3];
   1101   const base::string16 text[3] = {
   1102     base::ASCIIToUTF16("what, zz"),
   1103     base::ASCIIToUTF16("apple, zz"),
   1104     base::ASCIIToUTF16("orange, zz")
   1105   };
   1106 
   1107   // Calls RequestTextchecking a few times.
   1108   for (int i = 0; i < 3; ++i)
   1109     spell_check()->RequestTextChecking(text[i], &completion[i]);
   1110 
   1111   // The last task will be posted after initialization, however the other
   1112   // requests should be pressed without spellchecking.
   1113   base::MessageLoop::current()->RunUntilIdle();
   1114   for (int i = 0; i < 2; ++i)
   1115     EXPECT_EQ(completion[i].completion_count_, 1U);
   1116   EXPECT_EQ(completion[2].completion_count_, 0U);
   1117 
   1118   // Checks the last request is processed after initialization.
   1119   InitializeSpellCheck("en-US");
   1120 
   1121   // Calls PostDelayedSpellCheckTask instead of OnInit here for simplicity.
   1122   spell_check()->PostDelayedSpellCheckTask(
   1123       spell_check()->pending_request_param_.release());
   1124   base::MessageLoop::current()->RunUntilIdle();
   1125   for (int i = 0; i < 3; ++i)
   1126     EXPECT_EQ(completion[i].completion_count_, 1U);
   1127 }
   1128 
   1129 TEST_F(SpellCheckTest, CreateTextCheckingResults) {
   1130   // Verify that the SpellCheck class keeps the spelling marker added to a
   1131   // misspelled word "zz".
   1132   {
   1133     base::string16 text = base::ASCIIToUTF16("zz");
   1134     std::vector<SpellCheckResult> spellcheck_results;
   1135     spellcheck_results.push_back(SpellCheckResult(
   1136         SpellCheckResult::SPELLING, 0, 2, base::string16()));
   1137     blink::WebVector<blink::WebTextCheckingResult> textcheck_results;
   1138     spell_check()->CreateTextCheckingResults(SpellCheck::USE_NATIVE_CHECKER,
   1139                                              0,
   1140                                              text,
   1141                                              spellcheck_results,
   1142                                              &textcheck_results);
   1143     EXPECT_EQ(spellcheck_results.size(), textcheck_results.size());
   1144     EXPECT_EQ(blink::WebTextDecorationTypeSpelling,
   1145               textcheck_results[0].decoration);
   1146     EXPECT_EQ(spellcheck_results[0].location, textcheck_results[0].location);
   1147     EXPECT_EQ(spellcheck_results[0].length, textcheck_results[0].length);
   1148   }
   1149 
   1150   // Verify that the SpellCheck class replaces the spelling marker added to a
   1151   // contextually-misspelled word "bean" with a grammar marker.
   1152   {
   1153     base::string16 text = base::ASCIIToUTF16("I have bean to USA.");
   1154     std::vector<SpellCheckResult> spellcheck_results;
   1155     spellcheck_results.push_back(SpellCheckResult(
   1156         SpellCheckResult::SPELLING, 7, 4, base::string16()));
   1157     blink::WebVector<blink::WebTextCheckingResult> textcheck_results;
   1158     spell_check()->CreateTextCheckingResults(SpellCheck::USE_NATIVE_CHECKER,
   1159                                              0,
   1160                                              text,
   1161                                              spellcheck_results,
   1162                                              &textcheck_results);
   1163     EXPECT_EQ(spellcheck_results.size(), textcheck_results.size());
   1164     EXPECT_EQ(blink::WebTextDecorationTypeGrammar,
   1165               textcheck_results[0].decoration);
   1166     EXPECT_EQ(spellcheck_results[0].location, textcheck_results[0].location);
   1167     EXPECT_EQ(spellcheck_results[0].length, textcheck_results[0].length);
   1168   }
   1169 }
   1170 
   1171 #endif
   1172 
   1173 // Checks some words that should be present in all English dictionaries.
   1174 TEST_F(SpellCheckTest, EnglishWords) {
   1175   static const struct {
   1176     const char* input;
   1177     bool should_pass;
   1178   } kTestCases[] = {
   1179     // Issue 146093: "Chromebook" and "Chromebox" not included in spell-checking
   1180     // dictionary.
   1181     {"Chromebook", true},
   1182     {"Chromebooks", true},
   1183     {"Chromebox", true},
   1184     {"Chromeboxes", true},
   1185     {"Chromeblade", true},
   1186     {"Chromeblades", true},
   1187     {"Chromebase", true},
   1188     {"Chromebases", true},
   1189     // Issue 94708: Spell-checker incorrectly reports whisky as misspelled.
   1190     {"whisky", true},
   1191     {"whiskey", true},
   1192     {"whiskies", true},
   1193     // Issue 98678: "Recency" should be included in client-side dictionary.
   1194     {"recency", true},
   1195     {"recencies", false},
   1196     // Issue 140486
   1197     {"movie", true},
   1198     {"movies", true},
   1199   };
   1200 
   1201   static const char* kLocales[] = { "en-GB", "en-US", "en-CA", "en-AU" };
   1202 
   1203   for (size_t j = 0; j < arraysize(kLocales); ++j) {
   1204     ReinitializeSpellCheck(kLocales[j]);
   1205     for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
   1206       size_t input_length = 0;
   1207       if (kTestCases[i].input != NULL)
   1208         input_length = strlen(kTestCases[i].input);
   1209 
   1210       int misspelling_start = 0;
   1211       int misspelling_length = 0;
   1212       bool result = spell_check()->SpellCheckWord(
   1213           base::ASCIIToUTF16(kTestCases[i].input).c_str(),
   1214           static_cast<int>(input_length),
   1215           0,
   1216           &misspelling_start,
   1217           &misspelling_length, NULL);
   1218 
   1219       EXPECT_EQ(kTestCases[i].should_pass, result) << kTestCases[i].input <<
   1220           " in " << kLocales[j];
   1221     }
   1222   }
   1223 }
   1224 
   1225 // Checks that NOSUGGEST works in English dictionaries.
   1226 TEST_F(SpellCheckTest, NoSuggest) {
   1227   static const struct {
   1228     const char* input;
   1229     const char* suggestion;
   1230     const char* locale;
   1231     bool should_pass;
   1232   } kTestCases[] = {
   1233     {"suckerbert", "cocksucker",  "en-GB", true},
   1234     {"suckerbert", "cocksucker",  "en-US", true},
   1235     {"suckerbert", "cocksucker",  "en-CA", true},
   1236     {"suckerbert", "cocksucker",  "en-AU", true},
   1237     {"suckerbert", "cocksuckers", "en-GB", true},
   1238     {"suckerbert", "cocksuckers", "en-US", true},
   1239     {"suckerbert", "cocksuckers", "en-CA", true},
   1240     {"suckerbert", "cocksuckers", "en-AU", true},
   1241     {"Batasunaa",  "Batasuna",    "ca-ES", true},
   1242     {"pornoo",     "porno",       "it-IT", true},
   1243     {"catass",     "catas",       "lt-LT", true},
   1244     {"kuracc",     "kurac",       "sl-SI", true},
   1245     {"pittt",      "pitt",        "sv-SE", true},
   1246   };
   1247 
   1248   size_t test_cases_size = ARRAYSIZE_UNSAFE(kTestCases);
   1249   for (size_t i = 0; i < test_cases_size; ++i) {
   1250     ReinitializeSpellCheck(kTestCases[i].locale);
   1251     size_t suggestion_length = 0;
   1252     if (kTestCases[i].suggestion != NULL)
   1253       suggestion_length = strlen(kTestCases[i].suggestion);
   1254 
   1255     // First check that the NOSUGGEST flag didn't mark this word as not being in
   1256     // the dictionary.
   1257     int misspelling_start = 0;
   1258     int misspelling_length = 0;
   1259     bool result = spell_check()->SpellCheckWord(
   1260         base::ASCIIToUTF16(kTestCases[i].suggestion).c_str(),
   1261         static_cast<int>(suggestion_length),
   1262         0,
   1263         &misspelling_start,
   1264         &misspelling_length, NULL);
   1265 
   1266     EXPECT_EQ(kTestCases[i].should_pass, result) << kTestCases[i].suggestion <<
   1267         " in " << kTestCases[i].locale;
   1268 
   1269     // Now verify that this test case does not show up as a suggestion.
   1270     std::vector<base::string16> suggestions;
   1271     size_t input_length = 0;
   1272     if (kTestCases[i].input != NULL)
   1273       input_length = strlen(kTestCases[i].input);
   1274     result = spell_check()->SpellCheckWord(
   1275         base::ASCIIToUTF16(kTestCases[i].input).c_str(),
   1276         static_cast<int>(input_length),
   1277         0,
   1278         &misspelling_start,
   1279         &misspelling_length,
   1280         &suggestions);
   1281     // Input word should be a misspelling.
   1282     EXPECT_FALSE(result) << kTestCases[i].input
   1283                          << " is not a misspelling in "
   1284                          << kTestCases[i].locale;
   1285     // Check if the suggested words occur.
   1286     for (int j = 0; j < static_cast<int>(suggestions.size()); j++) {
   1287       for (size_t t = 0; t < test_cases_size; t++) {
   1288         int compare_result = suggestions.at(j).compare(
   1289             base::ASCIIToUTF16(kTestCases[t].suggestion));
   1290         EXPECT_FALSE(compare_result == 0) << kTestCases[t].suggestion <<
   1291             " in " << kTestCases[i].locale;
   1292       }
   1293     }
   1294   }
   1295 }
   1296 
   1297 // Check that the correct dictionary files are checked in.
   1298 TEST_F(SpellCheckTest, DictionaryFiles) {
   1299   std::vector<std::string> spellcheck_languages;
   1300   chrome::spellcheck_common::SpellCheckLanguages(&spellcheck_languages);
   1301   EXPECT_FALSE(spellcheck_languages.empty());
   1302 
   1303   base::FilePath hunspell = GetHunspellDirectory();
   1304   for (size_t i = 0; i < spellcheck_languages.size(); ++i) {
   1305     base::FilePath dict = chrome::spellcheck_common::GetVersionedFileName(
   1306         spellcheck_languages[i], hunspell);
   1307     EXPECT_TRUE(base::PathExists(dict)) << dict.value() << " not found";
   1308   }
   1309 }
   1310 
   1311 // TODO(groby): Add a test for hunspell itself, when MAXWORDLEN is exceeded.
   1312 TEST_F(SpellCheckTest, SpellingEngine_CheckSpelling) {
   1313   static const struct {
   1314     const char* word;
   1315     bool expected_result;
   1316   } kTestCases[] = {
   1317     { "", true },
   1318     { "automatic", true },
   1319     { "hello", true },
   1320     { "forglobantic", false },
   1321     { "xfdssfsdfaasds", false },
   1322     {  // 64 chars are the longest word to check - this should fail checking.
   1323       "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl",
   1324       false
   1325     },
   1326     {  // Any word longer than 64 chars should be exempt from checking.
   1327       "reallylongwordthatabsolutelyexceedsthespecifiedcharacterlimitabit",
   1328       true
   1329     }
   1330   };
   1331 
   1332   // Initialization magic - call InitializeIfNeeded twice. The first one simply
   1333   // flags internal state that a dictionary was requested. The second one will
   1334   // take the passed-in file and initialize hunspell with it. (The file was
   1335   // passed to hunspell in the ctor for the test fixture).
   1336   // This needs to be done since we need to ensure the SpellingEngine object
   1337   // contained in |spellcheck_| from the test fixture does get initialized.
   1338   // TODO(groby): Clean up this mess.
   1339   InitializeIfNeeded();
   1340   ASSERT_FALSE(InitializeIfNeeded());
   1341 
   1342   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
   1343     bool result = CheckSpelling(kTestCases[i].word, 0);
   1344     EXPECT_EQ(kTestCases[i].expected_result, result) <<
   1345         "Failed test for " << kTestCases[i].word;
   1346   }
   1347 }
   1348 
   1349 // Chrome should not suggest "Othello" for "hellllo" or "identically" for
   1350 // "accidently".
   1351 TEST_F(SpellCheckTest, LogicalSuggestions) {
   1352   static const struct {
   1353     const char* misspelled;
   1354     const char* suggestion;
   1355   } kTestCases[] = {
   1356     { "hellllo", "hello" },
   1357     { "accidently", "accidentally" }
   1358   };
   1359 
   1360   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
   1361     int misspelling_start = 0;
   1362     int misspelling_length = 0;
   1363     std::vector<base::string16> suggestions;
   1364     EXPECT_FALSE(spell_check()->SpellCheckWord(
   1365         base::ASCIIToUTF16(kTestCases[i].misspelled).c_str(),
   1366         strlen(kTestCases[i].misspelled),
   1367         0,
   1368         &misspelling_start,
   1369         &misspelling_length,
   1370         &suggestions));
   1371     EXPECT_GE(suggestions.size(), static_cast<size_t>(1));
   1372     if (suggestions.size() > 0)
   1373       EXPECT_EQ(suggestions[0], base::ASCIIToUTF16(kTestCases[i].suggestion));
   1374   }
   1375 }
   1376