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