| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 // | 4 // |
| 5 // Unit tests for |FeedbackSender| object. | 5 // Unit tests for |FeedbackSender| object. |
| 6 | 6 |
| 7 #include "chrome/browser/spellchecker/feedback_sender.h" | 7 #include "chrome/browser/spellchecker/feedback_sender.h" |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 23 matching lines...) Expand all Loading... |
| 34 const int kMisspellingLength = 6; | 34 const int kMisspellingLength = 6; |
| 35 const int kMisspellingStart = 0; | 35 const int kMisspellingStart = 0; |
| 36 const int kRendererProcessId = 0; | 36 const int kRendererProcessId = 0; |
| 37 const int kUrlFetcherId = 0; | 37 const int kUrlFetcherId = 0; |
| 38 | 38 |
| 39 // Builds a simple spellcheck result. | 39 // Builds a simple spellcheck result. |
| 40 SpellCheckResult BuildSpellCheckResult() { | 40 SpellCheckResult BuildSpellCheckResult() { |
| 41 return SpellCheckResult(SpellCheckResult::SPELLING, | 41 return SpellCheckResult(SpellCheckResult::SPELLING, |
| 42 kMisspellingStart, | 42 kMisspellingStart, |
| 43 kMisspellingLength, | 43 kMisspellingLength, |
| 44 UTF8ToUTF16("Hello")); | 44 base::UTF8ToUTF16("Hello")); |
| 45 } | 45 } |
| 46 | 46 |
| 47 // Returns the number of times that |needle| appears in |haystack| without | 47 // Returns the number of times that |needle| appears in |haystack| without |
| 48 // overlaps. For example, CountOccurences("bananana", "nana") returns 1. | 48 // overlaps. For example, CountOccurences("bananana", "nana") returns 1. |
| 49 int CountOccurences(const std::string& haystack, const std::string& needle) { | 49 int CountOccurences(const std::string& haystack, const std::string& needle) { |
| 50 int number_of_occurrences = 0; | 50 int number_of_occurrences = 0; |
| 51 for (size_t pos = haystack.find(needle); | 51 for (size_t pos = haystack.find(needle); |
| 52 pos != std::string::npos; | 52 pos != std::string::npos; |
| 53 pos = haystack.find(needle, pos + needle.length())) { | 53 pos = haystack.find(needle, pos + needle.length())) { |
| 54 ++number_of_occurrences; | 54 ++number_of_occurrences; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 89 field_trial_ = base::FieldTrialList::CreateFieldTrial( | 89 field_trial_ = base::FieldTrialList::CreateFieldTrial( |
| 90 kFeedbackFieldTrialName, kFeedbackFieldTrialEnabledGroupName); | 90 kFeedbackFieldTrialName, kFeedbackFieldTrialEnabledGroupName); |
| 91 field_trial_->group(); | 91 field_trial_->group(); |
| 92 feedback_.reset(new FeedbackSender(NULL, kLanguage, kCountry)); | 92 feedback_.reset(new FeedbackSender(NULL, kLanguage, kCountry)); |
| 93 feedback_->StartFeedbackCollection(); | 93 feedback_->StartFeedbackCollection(); |
| 94 } | 94 } |
| 95 | 95 |
| 96 uint32 AddPendingFeedback() { | 96 uint32 AddPendingFeedback() { |
| 97 std::vector<SpellCheckResult> results(1, BuildSpellCheckResult()); | 97 std::vector<SpellCheckResult> results(1, BuildSpellCheckResult()); |
| 98 feedback_->OnSpellcheckResults(kRendererProcessId, | 98 feedback_->OnSpellcheckResults(kRendererProcessId, |
| 99 UTF8ToUTF16(kText), | 99 base::UTF8ToUTF16(kText), |
| 100 std::vector<SpellCheckMarker>(), | 100 std::vector<SpellCheckMarker>(), |
| 101 &results); | 101 &results); |
| 102 return results[0].hash; | 102 return results[0].hash; |
| 103 } | 103 } |
| 104 | 104 |
| 105 void ExpireSession() { | 105 void ExpireSession() { |
| 106 feedback_->session_start_ = | 106 feedback_->session_start_ = |
| 107 base::Time::Now() - | 107 base::Time::Now() - |
| 108 base::TimeDelta::FromHours(chrome::spellcheck_common::kSessionHours); | 108 base::TimeDelta::FromHours(chrome::spellcheck_common::kSessionHours); |
| 109 } | 109 } |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 234 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, | 234 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, |
| 235 std::vector<uint32>()); | 235 std::vector<uint32>()); |
| 236 EXPECT_TRUE(UploadDataContains("\"actionType\":\"IGNORE\"")); | 236 EXPECT_TRUE(UploadDataContains("\"actionType\":\"IGNORE\"")); |
| 237 } | 237 } |
| 238 | 238 |
| 239 // Send MANUALLY_CORRECTED feedback message if the user manually corrected the | 239 // Send MANUALLY_CORRECTED feedback message if the user manually corrected the |
| 240 // misspelled word. | 240 // misspelled word. |
| 241 TEST_F(FeedbackSenderTest, ManuallyCorrectedFeedback) { | 241 TEST_F(FeedbackSenderTest, ManuallyCorrectedFeedback) { |
| 242 uint32 hash = AddPendingFeedback(); | 242 uint32 hash = AddPendingFeedback(); |
| 243 static const std::string kManualCorrection = "Howdy"; | 243 static const std::string kManualCorrection = "Howdy"; |
| 244 feedback_->ManuallyCorrected(hash, ASCIIToUTF16(kManualCorrection)); | 244 feedback_->ManuallyCorrected(hash, base::ASCIIToUTF16(kManualCorrection)); |
| 245 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, | 245 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, |
| 246 std::vector<uint32>()); | 246 std::vector<uint32>()); |
| 247 EXPECT_TRUE(UploadDataContains("\"actionType\":\"MANUALLY_CORRECTED\"")); | 247 EXPECT_TRUE(UploadDataContains("\"actionType\":\"MANUALLY_CORRECTED\"")); |
| 248 EXPECT_TRUE(UploadDataContains("\"actionTargetValue\":\"" + | 248 EXPECT_TRUE(UploadDataContains("\"actionTargetValue\":\"" + |
| 249 kManualCorrection + "\"")); | 249 kManualCorrection + "\"")); |
| 250 } | 250 } |
| 251 | 251 |
| 252 // Send feedback messages in batch. | 252 // Send feedback messages in batch. |
| 253 TEST_F(FeedbackSenderTest, BatchFeedback) { | 253 TEST_F(FeedbackSenderTest, BatchFeedback) { |
| 254 std::vector<SpellCheckResult> results; | 254 std::vector<SpellCheckResult> results; |
| 255 results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, | 255 results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, |
| 256 kMisspellingStart, | 256 kMisspellingStart, |
| 257 kMisspellingLength, | 257 kMisspellingLength, |
| 258 ASCIIToUTF16("Hello"))); | 258 base::ASCIIToUTF16("Hello"))); |
| 259 static const int kSecondMisspellingStart = 7; | 259 static const int kSecondMisspellingStart = 7; |
| 260 static const int kSecondMisspellingLength = 5; | 260 static const int kSecondMisspellingLength = 5; |
| 261 results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, | 261 results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, |
| 262 kSecondMisspellingStart, | 262 kSecondMisspellingStart, |
| 263 kSecondMisspellingLength, | 263 kSecondMisspellingLength, |
| 264 ASCIIToUTF16("world"))); | 264 base::ASCIIToUTF16("world"))); |
| 265 feedback_->OnSpellcheckResults(kRendererProcessId, | 265 feedback_->OnSpellcheckResults(kRendererProcessId, |
| 266 UTF8ToUTF16(kText), | 266 base::UTF8ToUTF16(kText), |
| 267 std::vector<SpellCheckMarker>(), | 267 std::vector<SpellCheckMarker>(), |
| 268 &results); | 268 &results); |
| 269 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, | 269 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, |
| 270 std::vector<uint32>()); | 270 std::vector<uint32>()); |
| 271 EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\"", 2)); | 271 EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\"", 2)); |
| 272 } | 272 } |
| 273 | 273 |
| 274 // Send a series of PENDING feedback messages and one final NO_ACTION feedback | 274 // Send a series of PENDING feedback messages and one final NO_ACTION feedback |
| 275 // message with the same hash identifier for a single misspelling. | 275 // message with the same hash identifier for a single misspelling. |
| 276 TEST_F(FeedbackSenderTest, SameHashFeedback) { | 276 TEST_F(FeedbackSenderTest, SameHashFeedback) { |
| (...skipping 26 matching lines...) Expand all Loading... |
| 303 // 1) Pending feedback is finalized and sent to the server in the last message | 303 // 1) Pending feedback is finalized and sent to the server in the last message |
| 304 // batch in the session. | 304 // batch in the session. |
| 305 // 2) No feedback is sent until a spellcheck request happens. | 305 // 2) No feedback is sent until a spellcheck request happens. |
| 306 // 3) Existing markers get new hash identifiers. | 306 // 3) Existing markers get new hash identifiers. |
| 307 TEST_F(FeedbackSenderTest, SessionExpirationFeedback) { | 307 TEST_F(FeedbackSenderTest, SessionExpirationFeedback) { |
| 308 std::vector<SpellCheckResult> results( | 308 std::vector<SpellCheckResult> results( |
| 309 1, | 309 1, |
| 310 SpellCheckResult(SpellCheckResult::SPELLING, | 310 SpellCheckResult(SpellCheckResult::SPELLING, |
| 311 kMisspellingStart, | 311 kMisspellingStart, |
| 312 kMisspellingLength, | 312 kMisspellingLength, |
| 313 ASCIIToUTF16("Hello"))); | 313 base::ASCIIToUTF16("Hello"))); |
| 314 feedback_->OnSpellcheckResults(kRendererProcessId, | 314 feedback_->OnSpellcheckResults(kRendererProcessId, |
| 315 UTF8ToUTF16(kText), | 315 base::UTF8ToUTF16(kText), |
| 316 std::vector<SpellCheckMarker>(), | 316 std::vector<SpellCheckMarker>(), |
| 317 &results); | 317 &results); |
| 318 uint32 original_hash = results[0].hash; | 318 uint32 original_hash = results[0].hash; |
| 319 std::vector<uint32> remaining_markers(1, original_hash); | 319 std::vector<uint32> remaining_markers(1, original_hash); |
| 320 | 320 |
| 321 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers); | 321 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers); |
| 322 EXPECT_FALSE(UploadDataContains("\"actionType\":\"NO_ACTION\"")); | 322 EXPECT_FALSE(UploadDataContains("\"actionType\":\"NO_ACTION\"")); |
| 323 EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\"")); | 323 EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\"")); |
| 324 std::string original_hash_string = | 324 std::string original_hash_string = |
| 325 base::StringPrintf("\"suggestionId\":\"%u\"", original_hash); | 325 base::StringPrintf("\"suggestionId\":\"%u\"", original_hash); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 340 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers); | 340 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers); |
| 341 EXPECT_FALSE(IsUploadingData()); | 341 EXPECT_FALSE(IsUploadingData()); |
| 342 | 342 |
| 343 // The first spellcheck request after session expiration creates different | 343 // The first spellcheck request after session expiration creates different |
| 344 // document marker hash identifiers. | 344 // document marker hash identifiers. |
| 345 std::vector<SpellCheckMarker> original_markers( | 345 std::vector<SpellCheckMarker> original_markers( |
| 346 1, SpellCheckMarker(results[0].hash, results[0].location)); | 346 1, SpellCheckMarker(results[0].hash, results[0].location)); |
| 347 results[0] = SpellCheckResult(SpellCheckResult::SPELLING, | 347 results[0] = SpellCheckResult(SpellCheckResult::SPELLING, |
| 348 kMisspellingStart, | 348 kMisspellingStart, |
| 349 kMisspellingLength, | 349 kMisspellingLength, |
| 350 ASCIIToUTF16("Hello")); | 350 base::ASCIIToUTF16("Hello")); |
| 351 feedback_->OnSpellcheckResults( | 351 feedback_->OnSpellcheckResults( |
| 352 kRendererProcessId, UTF8ToUTF16(kText), original_markers, &results); | 352 kRendererProcessId, base::UTF8ToUTF16(kText), original_markers, &results); |
| 353 uint32 updated_hash = results[0].hash; | 353 uint32 updated_hash = results[0].hash; |
| 354 EXPECT_NE(updated_hash, original_hash); | 354 EXPECT_NE(updated_hash, original_hash); |
| 355 remaining_markers[0] = updated_hash; | 355 remaining_markers[0] = updated_hash; |
| 356 | 356 |
| 357 // The first feedback message batch in session |i + 1| has the new document | 357 // The first feedback message batch in session |i + 1| has the new document |
| 358 // marker hash identifiers. | 358 // marker hash identifiers. |
| 359 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers); | 359 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers); |
| 360 EXPECT_FALSE(UploadDataContains("\"actionType\":\"NO_ACTION\"")); | 360 EXPECT_FALSE(UploadDataContains("\"actionType\":\"NO_ACTION\"")); |
| 361 EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\"")); | 361 EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\"")); |
| 362 EXPECT_FALSE(UploadDataContains(original_hash_string)); | 362 EXPECT_FALSE(UploadDataContains(original_hash_string)); |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 499 } | 499 } |
| 500 | 500 |
| 501 // Duplicate spellcheck results should be matched to the existing markers. | 501 // Duplicate spellcheck results should be matched to the existing markers. |
| 502 TEST_F(FeedbackSenderTest, MatchDupliateResultsWithExistingMarkers) { | 502 TEST_F(FeedbackSenderTest, MatchDupliateResultsWithExistingMarkers) { |
| 503 uint32 hash = AddPendingFeedback(); | 503 uint32 hash = AddPendingFeedback(); |
| 504 std::vector<SpellCheckResult> results( | 504 std::vector<SpellCheckResult> results( |
| 505 1, | 505 1, |
| 506 SpellCheckResult(SpellCheckResult::SPELLING, | 506 SpellCheckResult(SpellCheckResult::SPELLING, |
| 507 kMisspellingStart, | 507 kMisspellingStart, |
| 508 kMisspellingLength, | 508 kMisspellingLength, |
| 509 ASCIIToUTF16("Hello"))); | 509 base::ASCIIToUTF16("Hello"))); |
| 510 std::vector<SpellCheckMarker> markers( | 510 std::vector<SpellCheckMarker> markers( |
| 511 1, SpellCheckMarker(hash, results[0].location)); | 511 1, SpellCheckMarker(hash, results[0].location)); |
| 512 EXPECT_EQ(static_cast<uint32>(0), results[0].hash); | 512 EXPECT_EQ(static_cast<uint32>(0), results[0].hash); |
| 513 feedback_->OnSpellcheckResults( | 513 feedback_->OnSpellcheckResults( |
| 514 kRendererProcessId, UTF8ToUTF16(kText), markers, &results); | 514 kRendererProcessId, base::UTF8ToUTF16(kText), markers, &results); |
| 515 EXPECT_EQ(hash, results[0].hash); | 515 EXPECT_EQ(hash, results[0].hash); |
| 516 } | 516 } |
| 517 | 517 |
| 518 // Adding a word to dictionary should trigger ADD_TO_DICT feedback for every | 518 // Adding a word to dictionary should trigger ADD_TO_DICT feedback for every |
| 519 // occurrence of that word. | 519 // occurrence of that word. |
| 520 TEST_F(FeedbackSenderTest, MultipleAddToDictFeedback) { | 520 TEST_F(FeedbackSenderTest, MultipleAddToDictFeedback) { |
| 521 std::vector<SpellCheckResult> results; | 521 std::vector<SpellCheckResult> results; |
| 522 static const int kSentenceLength = 14; | 522 static const int kSentenceLength = 14; |
| 523 static const int kNumberOfSentences = 2; | 523 static const int kNumberOfSentences = 2; |
| 524 static const base::string16 kTextWithDuplicates = | 524 static const base::string16 kTextWithDuplicates = |
| 525 ASCIIToUTF16("Helllo world. Helllo world."); | 525 base::ASCIIToUTF16("Helllo world. Helllo world."); |
| 526 for (int i = 0; i < kNumberOfSentences; ++i) { | 526 for (int i = 0; i < kNumberOfSentences; ++i) { |
| 527 results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, | 527 results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, |
| 528 kMisspellingStart + i * kSentenceLength, | 528 kMisspellingStart + i * kSentenceLength, |
| 529 kMisspellingLength, | 529 kMisspellingLength, |
| 530 ASCIIToUTF16("Hello"))); | 530 base::ASCIIToUTF16("Hello"))); |
| 531 } | 531 } |
| 532 static const int kNumberOfRenderers = 2; | 532 static const int kNumberOfRenderers = 2; |
| 533 int last_renderer_process_id = -1; | 533 int last_renderer_process_id = -1; |
| 534 for (int i = 0; i < kNumberOfRenderers; ++i) { | 534 for (int i = 0; i < kNumberOfRenderers; ++i) { |
| 535 feedback_->OnSpellcheckResults(kRendererProcessId + i, | 535 feedback_->OnSpellcheckResults(kRendererProcessId + i, |
| 536 kTextWithDuplicates, | 536 kTextWithDuplicates, |
| 537 std::vector<SpellCheckMarker>(), | 537 std::vector<SpellCheckMarker>(), |
| 538 &results); | 538 &results); |
| 539 last_renderer_process_id = kRendererProcessId + i; | 539 last_renderer_process_id = kRendererProcessId + i; |
| 540 } | 540 } |
| (...skipping 23 matching lines...) Expand all Loading... |
| 564 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, | 564 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, |
| 565 std::vector<uint32>()); | 565 std::vector<uint32>()); |
| 566 EXPECT_TRUE(UploadDataContains("SELECT", 1)); | 566 EXPECT_TRUE(UploadDataContains("SELECT", 1)); |
| 567 EXPECT_TRUE(UploadDataContains("ADD_TO_DICT", 2)); | 567 EXPECT_TRUE(UploadDataContains("ADD_TO_DICT", 2)); |
| 568 } | 568 } |
| 569 | 569 |
| 570 // Spellcheck results that are out-of-bounds are not added to feedback. | 570 // Spellcheck results that are out-of-bounds are not added to feedback. |
| 571 TEST_F(FeedbackSenderTest, IgnoreOutOfBounds) { | 571 TEST_F(FeedbackSenderTest, IgnoreOutOfBounds) { |
| 572 std::vector<SpellCheckResult> results; | 572 std::vector<SpellCheckResult> results; |
| 573 results.push_back(SpellCheckResult( | 573 results.push_back(SpellCheckResult( |
| 574 SpellCheckResult::SPELLING, 0, 100, UTF8ToUTF16("Hello"))); | 574 SpellCheckResult::SPELLING, 0, 100, base::UTF8ToUTF16("Hello"))); |
| 575 results.push_back(SpellCheckResult( | 575 results.push_back(SpellCheckResult( |
| 576 SpellCheckResult::SPELLING, 100, 3, UTF8ToUTF16("world"))); | 576 SpellCheckResult::SPELLING, 100, 3, base::UTF8ToUTF16("world"))); |
| 577 results.push_back( | 577 results.push_back(SpellCheckResult( |
| 578 SpellCheckResult(SpellCheckResult::SPELLING, -1, 3, UTF8ToUTF16("how"))); | 578 SpellCheckResult::SPELLING, -1, 3, base::UTF8ToUTF16("how"))); |
| 579 results.push_back( | 579 results.push_back(SpellCheckResult( |
| 580 SpellCheckResult(SpellCheckResult::SPELLING, 0, 0, UTF8ToUTF16("are"))); | 580 SpellCheckResult::SPELLING, 0, 0, base::UTF8ToUTF16("are"))); |
| 581 results.push_back( | 581 results.push_back(SpellCheckResult( |
| 582 SpellCheckResult(SpellCheckResult::SPELLING, 2, -1, UTF8ToUTF16("you"))); | 582 SpellCheckResult::SPELLING, 2, -1, base::UTF8ToUTF16("you"))); |
| 583 feedback_->OnSpellcheckResults(kRendererProcessId, | 583 feedback_->OnSpellcheckResults(kRendererProcessId, |
| 584 UTF8ToUTF16(kText), | 584 base::UTF8ToUTF16(kText), |
| 585 std::vector<SpellCheckMarker>(), | 585 std::vector<SpellCheckMarker>(), |
| 586 &results); | 586 &results); |
| 587 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, | 587 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, |
| 588 std::vector<uint32>()); | 588 std::vector<uint32>()); |
| 589 EXPECT_FALSE(IsUploadingData()); | 589 EXPECT_FALSE(IsUploadingData()); |
| 590 } | 590 } |
| 591 | 591 |
| 592 // FeedbackSender does not collect and upload feedback when instructed to stop. | 592 // FeedbackSender does not collect and upload feedback when instructed to stop. |
| 593 TEST_F(FeedbackSenderTest, CanStopFeedbackCollection) { | 593 TEST_F(FeedbackSenderTest, CanStopFeedbackCollection) { |
| 594 feedback_->StopFeedbackCollection(); | 594 feedback_->StopFeedbackCollection(); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 615 AddPendingFeedback(); | 615 AddPendingFeedback(); |
| 616 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, | 616 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, |
| 617 std::vector<uint32>()); | 617 std::vector<uint32>()); |
| 618 feedback_->StartFeedbackCollection(); | 618 feedback_->StartFeedbackCollection(); |
| 619 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, | 619 feedback_->OnReceiveDocumentMarkers(kRendererProcessId, |
| 620 std::vector<uint32>()); | 620 std::vector<uint32>()); |
| 621 EXPECT_FALSE(IsUploadingData()); | 621 EXPECT_FALSE(IsUploadingData()); |
| 622 } | 622 } |
| 623 | 623 |
| 624 } // namespace spellcheck | 624 } // namespace spellcheck |
| OLD | NEW |