1// Copyright (c) 2011 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/bind.h" 6#include "base/message_loop/message_loop.h" 7#include "base/strings/string_util.h" 8#include "base/strings/utf_string_conversions.h" 9#include "chrome/browser/spellchecker/spellcheck_platform_mac.h" 10#include "chrome/common/spellcheck_result.h" 11#include "content/public/test/test_utils.h" 12#include "testing/gtest/include/gtest/gtest.h" 13 14namespace { 15 16class SpellcheckMacTest: public testing::Test { 17 public: 18 SpellcheckMacTest() 19 : callback_(base::Bind(&SpellcheckMacTest::CompletionCallback, 20 base::Unretained(this))), 21 callback_finished_(false), 22 message_loop_(base::MessageLoop::TYPE_UI) {} 23 24 void WaitForCallback() { 25 content::RunMessageLoop(); 26 } 27 28 std::vector<SpellCheckResult> results_; 29 spellcheck_mac::TextCheckCompleteCallback callback_; 30 bool callback_finished_; 31 32 private: 33 void QuitMessageLoop() { 34 CHECK(base::MessageLoop::current() == &message_loop_); 35 base::MessageLoop::current()->Quit(); 36 } 37 38 void CompletionCallback(const std::vector<SpellCheckResult>& results) { 39 results_ = results; 40 callback_finished_ = true; 41 message_loop_.PostTask(FROM_HERE, 42 base::Bind(&SpellcheckMacTest::QuitMessageLoop, 43 base::Unretained(this))); 44 } 45 46 base::MessageLoop message_loop_; 47 spellcheck_mac::ScopedEnglishLanguageForTest scoped_language_; 48}; 49 50// Tests that words are properly ignored. Currently only enabled on OS X as it 51// is the only platform to support ignoring words. Note that in this test, we 52// supply a non-zero doc_tag, in order to test that ignored words are matched to 53// the correct document. 54TEST_F(SpellcheckMacTest, IgnoreWords_EN_US) { 55 const char* kTestCases[] = { 56 "teh", 57 "morblier", 58 "watre", 59 "noooen", 60 }; 61 62 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) { 63 const string16 word(ASCIIToUTF16(kTestCases[i])); 64 const int doc_tag = spellcheck_mac::GetDocumentTag(); 65 66 // The word should show up as misspelled. 67 EXPECT_FALSE(spellcheck_mac::CheckSpelling(word, doc_tag)) << word; 68 69 // Ignore the word. 70 spellcheck_mac::IgnoreWord(word); 71 72 // The word should now show up as correctly spelled. 73 EXPECT_TRUE(spellcheck_mac::CheckSpelling(word, doc_tag)) << word; 74 75 // Close the docuemnt. Any words that we had previously ignored should no 76 // longer be ignored and thus should show up as misspelled. 77 spellcheck_mac::CloseDocumentWithTag(doc_tag); 78 79 // The word should now show be spelled wrong again 80 EXPECT_FALSE(spellcheck_mac::CheckSpelling(word, doc_tag)) << word; 81 } 82} // Test IgnoreWords_EN_US 83 84TEST_F(SpellcheckMacTest, SpellCheckSuggestions_EN_US) { 85 static const struct { 86 const char* input; // A string to be tested. 87 const char* suggested_word; // A suggested word that should occur. 88 } kTestCases[] = { 89 // We need to have separate test cases here, since hunspell and the OS X 90 // spellchecking service occasionally differ on what they consider a valid 91 // suggestion for a given word, although these lists could likely be 92 // integrated somewhat. The test cases for non-Mac are in 93 // chrome/renderer/spellcheck_unittest.cc 94 // These words come from the wikipedia page of the most commonly 95 // misspelled words in english. 96 // (http://en.wikipedia.org/wiki/Commonly_misspelled_words). 97 // However, 10.6 loads multiple dictionaries and enables many non-English 98 // dictionaries by default. As a result, we have removed from the list any 99 // word that is marked as correct because it is correct in another 100 // language. 101 {"absense", "absence"}, 102 {"acceptible", "acceptable"}, 103 {"accidentaly", "accidentally"}, 104 {"acheive", "achieve"}, 105 {"acknowlege", "acknowledge"}, 106 {"acquaintence", "acquaintance"}, 107 {"aquire", "acquire"}, 108 {"aquit", "acquit"}, 109 {"acrage", "acreage"}, 110 {"adultary", "adultery"}, 111 {"advertize", "advertise"}, 112 {"adviseable", "advisable"}, 113 {"alchohol", "alcohol"}, 114 {"alege", "allege"}, 115 {"allegaince", "allegiance"}, 116 {"allmost", "almost"}, 117 // Ideally, this test should pass. It works in firefox, but not in hunspell 118 // or OS X. 119 // {"alot", "a lot"}, 120 {"amatuer", "amateur"}, 121 {"ammend", "amend"}, 122 {"amung", "among"}, 123 {"anually", "annually"}, 124 {"apparant", "apparent"}, 125 {"artic", "arctic"}, 126 {"arguement", "argument"}, 127 {"athiest", "atheist"}, 128 {"athelete", "athlete"}, 129 {"avrage", "average"}, 130 {"awfull", "awful"}, 131 {"ballance", "balance"}, 132 {"basicly", "basically"}, 133 {"becuase", "because"}, 134 {"becomeing", "becoming"}, 135 {"befor", "before"}, 136 {"begining", "beginning"}, 137 {"beleive", "believe"}, 138 {"bellweather", "bellwether"}, 139 {"benifit", "benefit"}, 140 {"bouy", "buoy"}, 141 {"briliant", "brilliant"}, 142 {"burgler", "burglar"}, 143 {"camoflage", "camouflage"}, 144 {"carefull", "careful"}, 145 {"Carribean", "Caribbean"}, 146 {"catagory", "category"}, 147 {"cauhgt", "caught"}, 148 {"cieling", "ceiling"}, 149 {"cemetary", "cemetery"}, 150 {"certin", "certain"}, 151 {"changable", "changeable"}, 152 {"cheif", "chief"}, 153 {"citezen", "citizen"}, 154 {"collaegue", "colleague"}, 155 {"colum", "column"}, 156 {"comming", "coming"}, 157 {"commited", "committed"}, 158 {"compitition", "competition"}, 159 {"conceed", "concede"}, 160 {"congradulate", "congratulate"}, 161 {"consciencious", "conscientious"}, 162 {"concious", "conscious"}, 163 {"concensus", "consensus"}, 164 {"contraversy", "controversy"}, 165 {"conveniance", "convenience"}, 166 {"critecize", "criticize"}, 167 {"dacquiri", "daiquiri"}, 168 {"decieve", "deceive"}, 169 {"dicide", "decide"}, 170 {"definate", "definite"}, 171 {"definitly", "definitely"}, 172 {"desparate", "desperate"}, 173 {"develope", "develop"}, 174 {"diffrence", "difference"}, 175 {"disapear", "disappear"}, 176 {"disapoint", "disappoint"}, 177 {"disasterous", "disastrous"}, 178 {"disipline", "discipline"}, 179 {"drunkeness", "drunkenness"}, 180 {"dumbell", "dumbbell"}, 181 {"easely", "easily"}, 182 {"eigth", "eight"}, 183 {"embarass", "embarrass"}, 184 {"enviroment", "environment"}, 185 {"equiped", "equipped"}, 186 {"equiptment", "equipment"}, 187 {"exagerate", "exaggerate"}, 188 {"exellent", "excellent"}, 189 {"exsept", "except"}, 190 {"exercize", "exercise"}, 191 {"exilerate", "exhilarate"}, 192 {"existance", "existence"}, 193 {"experiance", "experience"}, 194 {"experament", "experiment"}, 195 {"explaination", "explanation"}, 196 {"facinating", "fascinating"}, 197 {"firey", "fiery"}, 198 {"finaly", "finally"}, 199 {"flourescent", "fluorescent"}, 200 {"foriegn", "foreign"}, 201 {"fourty", "forty"}, 202 {"foreward", "forward"}, 203 {"freind", "friend"}, 204 {"fundemental", "fundamental"}, 205 {"guage", "gauge"}, 206 {"generaly", "generally"}, 207 {"goverment", "government"}, 208 {"gratefull", "grateful"}, 209 {"garantee", "guarantee"}, 210 {"guidence", "guidance"}, 211 {"happyness", "happiness"}, 212 {"harrass", "harass"}, 213 {"heighth", "height"}, 214 {"heirarchy", "hierarchy"}, 215 {"humerous", "humorous"}, 216 {"hygene", "hygiene"}, 217 {"hipocrit", "hypocrite"}, 218 {"idenity", "identity"}, 219 {"ignorence", "ignorance"}, 220 {"imaginery", "imaginary"}, 221 {"immitate", "imitate"}, 222 {"immitation", "imitation"}, 223 {"imediately", "immediately"}, 224 {"incidently", "incidentally"}, 225 {"independant", "independent"}, 226 {"indispensible", "indispensable"}, 227 {"innoculate", "inoculate"}, 228 {"inteligence", "intelligence"}, 229 {"intresting", "interesting"}, 230 {"interuption", "interruption"}, 231 {"irrelevent", "irrelevant"}, 232 {"irritible", "irritable"}, 233 {"jellous", "jealous"}, 234 {"knowlege", "knowledge"}, 235 {"labratory", "laboratory"}, 236 {"lenght", "length"}, 237 {"liason", "liaison"}, 238 {"libary", "library"}, 239 {"lisence", "license"}, 240 {"lonelyness", "loneliness"}, 241 {"lieing", "lying"}, 242 {"maintenence", "maintenance"}, 243 {"manuever", "maneuver"}, 244 {"marrige", "marriage"}, 245 {"mathmatics", "mathematics"}, 246 {"medcine", "medicine"}, 247 {"miniture", "miniature"}, 248 {"minite", "minute"}, 249 {"mischevous", "mischievous"}, 250 {"mispell", "misspell"}, 251 // Maybe this one should pass, as it works in hunspell, but not in firefox. 252 // {"misterius", "mysterious"}, 253 {"naturaly", "naturally"}, 254 {"neccessary", "necessary"}, 255 {"neice", "niece"}, 256 {"nieghbor", "neighbor"}, 257 {"nieghbour", "neighbor"}, 258 {"niether", "neither"}, 259 {"noticable", "noticeable"}, 260 {"occassion", "occasion"}, 261 {"occasionaly", "occasionally"}, 262 {"occurrance", "occurrence"}, 263 {"occured", "occurred"}, 264 {"ommision", "omission"}, 265 {"oppurtunity", "opportunity"}, 266 {"outragous", "outrageous"}, 267 {"parrallel", "parallel"}, 268 {"parliment", "parliament"}, 269 {"particurly", "particularly"}, 270 {"passtime", "pastime"}, 271 {"peculier", "peculiar"}, 272 {"percieve", "perceive"}, 273 {"pernament", "permanent"}, 274 {"perseverence", "perseverance"}, 275 {"personaly", "personally"}, 276 {"persaude", "persuade"}, 277 {"pichure", "picture"}, 278 {"peice", "piece"}, 279 {"plagerize", "plagiarize"}, 280 {"playright", "playwright"}, 281 {"plesant", "pleasant"}, 282 {"pollitical", "political"}, 283 {"posession", "possession"}, 284 {"potatos", "potatoes"}, 285 {"practicle", "practical"}, 286 {"preceed", "precede"}, 287 {"predjudice", "prejudice"}, 288 {"presance", "presence"}, 289 {"privelege", "privilege"}, 290 // This one should probably work. It does in FF and Hunspell. 291 // {"probly", "probably"}, 292 {"proffesional", "professional"}, 293 {"promiss", "promise"}, 294 {"pronounciation", "pronunciation"}, 295 {"prufe", "proof"}, 296 {"psycology", "psychology"}, 297 {"publically", "publicly"}, 298 {"quanity", "quantity"}, 299 {"quarentine", "quarantine"}, 300 {"questionaire", "questionnaire"}, 301 {"readible", "readable"}, 302 {"realy", "really"}, 303 {"recieve", "receive"}, 304 {"reciept", "receipt"}, 305 {"reconize", "recognize"}, 306 {"recomend", "recommend"}, 307 {"refered", "referred"}, 308 {"referance", "reference"}, 309 {"relevent", "relevant"}, 310 {"religous", "religious"}, 311 {"repitition", "repetition"}, 312 {"restarant", "restaurant"}, 313 {"rythm", "rhythm"}, 314 {"rediculous", "ridiculous"}, 315 {"sacrefice", "sacrifice"}, 316 {"saftey", "safety"}, 317 {"sissors", "scissors"}, 318 {"secratary", "secretary"}, 319 {"seperate", "separate"}, 320 {"sargent", "sergeant"}, 321 {"shineing", "shining"}, 322 {"similer", "similar"}, 323 {"sinceerly", "sincerely"}, 324 {"speach", "speech"}, 325 {"strenght", "strength"}, 326 {"succesful", "successful"}, 327 {"supercede", "supersede"}, 328 {"surelly", "surely"}, 329 {"suprise", "surprise"}, 330 {"temperture", "temperature"}, 331 {"temprary", "temporary"}, 332 {"tommorrow", "tomorrow"}, 333 {"tounge", "tongue"}, 334 {"truely", "truly"}, 335 {"twelth", "twelfth"}, 336 {"tyrany", "tyranny"}, 337 {"underate", "underrate"}, 338 {"untill", "until"}, 339 {"unuseual", "unusual"}, 340 {"upholstry", "upholstery"}, 341 {"usible", "usable"}, 342 {"useing", "using"}, 343 {"usualy", "usually"}, 344 {"vaccuum", "vacuum"}, 345 {"vegatarian", "vegetarian"}, 346 {"vehical", "vehicle"}, 347 {"visious", "vicious"}, 348 {"villege", "village"}, 349 {"wierd", "weird"}, 350 {"wellcome", "welcome"}, 351 {"wellfare", "welfare"}, 352 {"wilfull", "willful"}, 353 {"withold", "withhold"}, 354 {"writting", "writing"}, 355 }; 356 357 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) { 358 const string16 word(ASCIIToUTF16(kTestCases[i].input)); 359 EXPECT_FALSE(spellcheck_mac::CheckSpelling(word, 0)) << word; 360 361 // Check if the suggested words occur. 362 std::vector<string16> suggestions; 363 spellcheck_mac::FillSuggestionList(word, &suggestions); 364 bool suggested_word_is_present = false; 365 const string16 suggested_word(ASCIIToUTF16(kTestCases[i].suggested_word)); 366 for (size_t j = 0; j < suggestions.size(); j++) { 367 if (suggestions[j].compare(suggested_word) == 0) { 368 suggested_word_is_present = true; 369 break; 370 } 371 } 372 EXPECT_TRUE(suggested_word_is_present) << suggested_word; 373 } 374} 375 376// The OSX spellchecker returns non-spellcheck results when invoked on a 377// sentence, specifically an NSTextCheckingTypeOrthography result indicating 378// the language used in that sentence. Test that it is filtered out from 379// RequestTextCheck results. 380TEST_F(SpellcheckMacTest, SpellCheckIgnoresOrthography) { 381 string16 test_string(ASCIIToUTF16("Icland is awesome.")); 382 spellcheck_mac::RequestTextCheck(0, test_string, callback_); 383 WaitForCallback(); 384 EXPECT_TRUE(callback_finished_); 385 EXPECT_EQ(1U, results_.size()); 386} 387 388} // namespace 389