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 "chrome/browser/spellchecker/spellcheck_custom_dictionary.h"
6
7 #include <stddef.h>
8
9 #include <memory>
10 #include <utility>
11 #include <vector>
12
13 #include "base/bind.h"
14 #include "base/files/file_util.h"
15 #include "base/macros.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/metrics/histogram_samples.h"
18 #include "base/metrics/statistics_recorder.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "build/build_config.h"
21 #include "chrome/browser/spellchecker/spellcheck_factory.h"
22 #include "chrome/browser/spellchecker/spellcheck_service.h"
23 #include "chrome/common/chrome_constants.h"
24 #include "chrome/test/base/testing_profile.h"
25 #include "components/spellcheck/browser/spellcheck_host_metrics.h"
26 #include "components/spellcheck/common/spellcheck_common.h"
27 #include "components/sync/model/sync_change.h"
28 #include "components/sync/model/sync_change_processor_wrapper_for_test.h"
29 #include "components/sync/model/sync_data.h"
30 #include "components/sync/model/sync_error_factory.h"
31 #include "components/sync/model/sync_error_factory_mock.h"
32 #include "components/sync/protocol/sync.pb.h"
33 #include "content/public/test/browser_task_environment.h"
34 #include "content/public/test/test_utils.h"
35 #include "net/test/embedded_test_server/default_handlers.h"
36 #include "net/test/embedded_test_server/embedded_test_server.h"
37 #include "net/test/embedded_test_server/http_request.h"
38 #include "net/test/embedded_test_server/http_response.h"
39 #include "testing/gmock/include/gmock/gmock.h"
40 #include "testing/gtest/include/gtest/gtest.h"
41
42 using base::HistogramBase;
43 using base::HistogramSamples;
44 using base::StatisticsRecorder;
45
46 namespace {
47
48 // Get all sync data for the custom dictionary without limiting to maximum
49 // number of syncable words.
GetAllSyncDataNoLimit(const SpellcheckCustomDictionary * dictionary)50 syncer::SyncDataList GetAllSyncDataNoLimit(
51 const SpellcheckCustomDictionary* dictionary) {
52 syncer::SyncDataList data;
53 for (const std::string& word : dictionary->GetWords()) {
54 sync_pb::EntitySpecifics specifics;
55 specifics.mutable_dictionary()->set_word(word);
56 data.push_back(syncer::SyncData::CreateLocalData(word, word, specifics));
57 }
58 return data;
59 }
60
61 } // namespace
62
BuildSpellcheckService(content::BrowserContext * profile)63 static std::unique_ptr<KeyedService> BuildSpellcheckService(
64 content::BrowserContext* profile) {
65 return std::make_unique<SpellcheckService>(static_cast<Profile*>(profile));
66 }
67
68 class SpellcheckCustomDictionaryTest : public testing::Test {
69 public:
SpellcheckCustomDictionaryTest()70 SpellcheckCustomDictionaryTest()
71 : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP) {}
72
73 protected:
SetUp()74 void SetUp() override {
75 // Use SetTestingFactoryAndUse to force creation and initialization.
76 SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
77 &profile_, base::BindRepeating(&BuildSpellcheckService));
78 }
79
80 // A wrapper around SpellcheckCustomDictionary::LoadDictionaryFile private
81 // function to avoid a large number of FRIEND_TEST declarations in
82 // SpellcheckCustomDictionary.
83 std::unique_ptr<SpellcheckCustomDictionary::LoadFileResult>
LoadDictionaryFile(const base::FilePath & path)84 LoadDictionaryFile(const base::FilePath& path) {
85 return SpellcheckCustomDictionary::LoadDictionaryFile(path);
86 }
87
88 // A wrapper around SpellcheckCustomDictionary::UpdateDictionaryFile private
89 // function to avoid a large number of FRIEND_TEST declarations in
90 // SpellcheckCustomDictionary.
UpdateDictionaryFile(std::unique_ptr<SpellcheckCustomDictionary::Change> dictionary_change,const base::FilePath & path)91 void UpdateDictionaryFile(
92 std::unique_ptr<SpellcheckCustomDictionary::Change> dictionary_change,
93 const base::FilePath& path) {
94 SpellcheckCustomDictionary::UpdateDictionaryFile(
95 std::move(dictionary_change), path);
96 }
97
98 // A wrapper around SpellcheckCustomDictionary::OnLoaded private method to
99 // avoid a large number of FRIEND_TEST declarations in
100 // SpellcheckCustomDictionary.
OnLoaded(SpellcheckCustomDictionary & dictionary,std::unique_ptr<std::set<std::string>> words)101 void OnLoaded(SpellcheckCustomDictionary& dictionary,
102 std::unique_ptr<std::set<std::string>> words) {
103 std::unique_ptr<SpellcheckCustomDictionary::LoadFileResult> result(
104 new SpellcheckCustomDictionary::LoadFileResult);
105 result->is_valid_file = true;
106 result->words = *words;
107 dictionary.OnLoaded(std::move(result));
108 }
109
110 // A wrapper around SpellcheckCustomDictionary::Apply private method to avoid
111 // a large number of FRIEND_TEST declarations in SpellcheckCustomDictionary.
Apply(SpellcheckCustomDictionary & dictionary,const SpellcheckCustomDictionary::Change & change)112 void Apply(
113 SpellcheckCustomDictionary& dictionary,
114 const SpellcheckCustomDictionary::Change& change) {
115 return dictionary.Apply(change);
116 }
117
118 content::BrowserTaskEnvironment task_environment_;
119
120 TestingProfile profile_;
121 };
122
123 // An implementation of SyncErrorFactory that does not upload the error message
124 // and updates an outside error counter. This lets us know the number of error
125 // messages in an instance of this class after that instance is deleted.
126 class SyncErrorFactoryStub : public syncer::SyncErrorFactory {
127 public:
SyncErrorFactoryStub(int * error_counter)128 explicit SyncErrorFactoryStub(int* error_counter)
129 : error_counter_(error_counter) {}
~SyncErrorFactoryStub()130 ~SyncErrorFactoryStub() override {}
131
132 // Overridden from syncer::SyncErrorFactory:
CreateAndUploadError(const base::Location & location,const std::string & message)133 syncer::SyncError CreateAndUploadError(const base::Location& location,
134 const std::string& message) override {
135 (*error_counter_)++;
136 return syncer::SyncError(location,
137 syncer::SyncError::DATATYPE_ERROR,
138 message,
139 syncer::DICTIONARY);
140 }
141
142 private:
143 int* error_counter_;
144 DISALLOW_COPY_AND_ASSIGN(SyncErrorFactoryStub);
145 };
146
147 // Counts the number of notifications for dictionary load and change.
148 class DictionaryObserverCounter : public SpellcheckCustomDictionary::Observer {
149 public:
DictionaryObserverCounter()150 DictionaryObserverCounter() : loads_(0), changes_(0) {}
~DictionaryObserverCounter()151 virtual ~DictionaryObserverCounter() {}
152
loads() const153 int loads() const { return loads_; }
changes() const154 int changes() const { return changes_; }
155
156 // Overridden from SpellcheckCustomDictionary::Observer:
OnCustomDictionaryLoaded()157 void OnCustomDictionaryLoaded() override { loads_++; }
OnCustomDictionaryChanged(const SpellcheckCustomDictionary::Change & change)158 void OnCustomDictionaryChanged(
159 const SpellcheckCustomDictionary::Change& change) override {
160 changes_++;
161 }
162
163 private:
164 int loads_;
165 int changes_;
166 DISALLOW_COPY_AND_ASSIGN(DictionaryObserverCounter);
167 };
168
TEST_F(SpellcheckCustomDictionaryTest,SaveAndLoad)169 TEST_F(SpellcheckCustomDictionaryTest, SaveAndLoad) {
170 base::FilePath path =
171 profile_.GetPath().Append(chrome::kCustomDictionaryFileName);
172
173 // The custom word list should be empty now.
174 EXPECT_TRUE(LoadDictionaryFile(path)->words.empty());
175
176 std::unique_ptr<SpellcheckCustomDictionary::Change> change(
177 new SpellcheckCustomDictionary::Change);
178 change->AddWord("bar");
179 change->AddWord("foo");
180
181 UpdateDictionaryFile(std::move(change), path);
182 std::set<std::string> expected;
183 expected.insert("bar");
184 expected.insert("foo");
185
186 // The custom word list should include written words.
187 EXPECT_EQ(expected, LoadDictionaryFile(path)->words);
188
189 std::unique_ptr<SpellcheckCustomDictionary::Change> change2(
190 new SpellcheckCustomDictionary::Change);
191 change2->RemoveWord("bar");
192 change2->RemoveWord("foo");
193 UpdateDictionaryFile(std::move(change2), path);
194 EXPECT_TRUE(LoadDictionaryFile(path)->words.empty());
195 }
196
TEST_F(SpellcheckCustomDictionaryTest,MultiProfile)197 TEST_F(SpellcheckCustomDictionaryTest, MultiProfile) {
198 SpellcheckService* spellcheck_service =
199 SpellcheckServiceFactory::GetForContext(&profile_);
200 SpellcheckCustomDictionary* custom_dictionary =
201 spellcheck_service->GetCustomDictionary();
202 TestingProfile profile2;
203 SpellcheckService* spellcheck_service2 = static_cast<SpellcheckService*>(
204 SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
205 &profile2, base::BindRepeating(&BuildSpellcheckService)));
206 SpellcheckCustomDictionary* custom_dictionary2 =
207 spellcheck_service2->GetCustomDictionary();
208
209 std::set<std::string> expected1;
210 std::set<std::string> expected2;
211
212 custom_dictionary->AddWord("foo");
213 custom_dictionary->AddWord("bar");
214 expected1.insert("foo");
215 expected1.insert("bar");
216
217 custom_dictionary2->AddWord("hoge");
218 custom_dictionary2->AddWord("fuga");
219 expected2.insert("hoge");
220 expected2.insert("fuga");
221
222 std::set<std::string> actual1 = custom_dictionary->GetWords();
223 EXPECT_EQ(actual1, expected1);
224
225 std::set<std::string> actual2 = custom_dictionary2->GetWords();
226 EXPECT_EQ(actual2, expected2);
227 }
228
229 // Legacy empty dictionary should be converted to new format empty dictionary.
TEST_F(SpellcheckCustomDictionaryTest,LegacyEmptyDictionaryShouldBeConverted)230 TEST_F(SpellcheckCustomDictionaryTest, LegacyEmptyDictionaryShouldBeConverted) {
231 base::FilePath path =
232 profile_.GetPath().Append(chrome::kCustomDictionaryFileName);
233
234 std::string content;
235 base::WriteFile(path, content.c_str(), content.length());
236 EXPECT_TRUE(LoadDictionaryFile(path)->words.empty());
237 }
238
239 // Legacy dictionary with two words should be converted to new format dictionary
240 // with two words.
TEST_F(SpellcheckCustomDictionaryTest,LegacyDictionaryWithTwoWordsShouldBeConverted)241 TEST_F(SpellcheckCustomDictionaryTest,
242 LegacyDictionaryWithTwoWordsShouldBeConverted) {
243 base::FilePath path =
244 profile_.GetPath().Append(chrome::kCustomDictionaryFileName);
245
246 std::string content = "foo\nbar\nfoo\n";
247 base::WriteFile(path, content.c_str(), content.length());
248 std::set<std::string> expected;
249 expected.insert("bar");
250 expected.insert("foo");
251 EXPECT_EQ(expected, LoadDictionaryFile(path)->words);
252 }
253
254 // Illegal words should be removed. Leading and trailing whitespace should be
255 // trimmed.
TEST_F(SpellcheckCustomDictionaryTest,IllegalWordsShouldBeRemovedFromDictionary)256 TEST_F(SpellcheckCustomDictionaryTest,
257 IllegalWordsShouldBeRemovedFromDictionary) {
258 base::FilePath path =
259 profile_.GetPath().Append(chrome::kCustomDictionaryFileName);
260
261 std::string content = "foo\n foo bar \n\n \nbar\n"
262 "01234567890123456789012345678901234567890123456789"
263 "01234567890123456789012345678901234567890123456789";
264 base::WriteFile(path, content.c_str(), content.length());
265 std::set<std::string> expected;
266 expected.insert("bar");
267 expected.insert("foo");
268 expected.insert("foo bar");
269 EXPECT_EQ(expected, LoadDictionaryFile(path)->words);
270 }
271
272 // Write to dictionary should backup previous version and write the word to the
273 // end of the dictionary. If the dictionary file is corrupted on disk, the
274 // previous version should be reloaded.
TEST_F(SpellcheckCustomDictionaryTest,CorruptedWriteShouldBeRecovered)275 TEST_F(SpellcheckCustomDictionaryTest, CorruptedWriteShouldBeRecovered) {
276 base::FilePath path =
277 profile_.GetPath().Append(chrome::kCustomDictionaryFileName);
278
279 std::string content = "foo\nbar";
280 base::WriteFile(path, content.c_str(), content.length());
281 std::set<std::string> expected;
282 expected.insert("bar");
283 expected.insert("foo");
284 EXPECT_EQ(expected, LoadDictionaryFile(path)->words);
285
286 std::unique_ptr<SpellcheckCustomDictionary::Change> change(
287 new SpellcheckCustomDictionary::Change);
288 change->AddWord("baz");
289 UpdateDictionaryFile(std::move(change), path);
290 content.clear();
291 base::ReadFileToString(path, &content);
292 content.append("corruption");
293 base::WriteFile(path, content.c_str(), content.length());
294 EXPECT_EQ(expected, LoadDictionaryFile(path)->words);
295 }
296
TEST_F(SpellcheckCustomDictionaryTest,GetAllSyncDataAccuratelyReflectsDictionaryState)297 TEST_F(SpellcheckCustomDictionaryTest,
298 GetAllSyncDataAccuratelyReflectsDictionaryState) {
299 SpellcheckCustomDictionary* dictionary =
300 SpellcheckServiceFactory::GetForContext(
301 &profile_)->GetCustomDictionary();
302
303 syncer::SyncDataList data =
304 dictionary->GetAllSyncDataForTesting(syncer::DICTIONARY);
305 EXPECT_TRUE(data.empty());
306
307 EXPECT_TRUE(dictionary->AddWord("bar"));
308 EXPECT_TRUE(dictionary->AddWord("foo"));
309
310 data = dictionary->GetAllSyncDataForTesting(syncer::DICTIONARY);
311 EXPECT_EQ(2UL, data.size());
312 std::vector<std::string> words;
313 words.push_back("bar");
314 words.push_back("foo");
315 for (size_t i = 0; i < data.size(); i++) {
316 EXPECT_TRUE(data[i].GetSpecifics().has_dictionary());
317 EXPECT_EQ(syncer::DICTIONARY, data[i].GetDataType());
318 EXPECT_EQ(words[i], syncer::SyncDataLocal(data[i]).GetTag());
319 EXPECT_EQ(words[i], data[i].GetSpecifics().dictionary().word());
320 }
321
322 EXPECT_TRUE(dictionary->RemoveWord("bar"));
323 EXPECT_TRUE(dictionary->RemoveWord("foo"));
324
325 data = dictionary->GetAllSyncDataForTesting(syncer::DICTIONARY);
326 EXPECT_TRUE(data.empty());
327 }
328
TEST_F(SpellcheckCustomDictionaryTest,GetAllSyncDataHasLimit)329 TEST_F(SpellcheckCustomDictionaryTest, GetAllSyncDataHasLimit) {
330 SpellcheckCustomDictionary* dictionary =
331 SpellcheckServiceFactory::GetForContext(
332 &profile_)->GetCustomDictionary();
333
334 SpellcheckCustomDictionary::Change change;
335 for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords - 1; i++) {
336 change.AddWord("foo" + base::NumberToString(i));
337 }
338 Apply(*dictionary, change);
339 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords - 1,
340 dictionary->GetWords().size());
341 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords - 1,
342 dictionary->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
343
344 dictionary->AddWord("baz");
345 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
346 dictionary->GetWords().size());
347 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
348 dictionary->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
349
350 dictionary->AddWord("bar");
351 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
352 dictionary->GetWords().size());
353 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
354 dictionary->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
355
356 dictionary->AddWord("snafoo");
357 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 2,
358 dictionary->GetWords().size());
359 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
360 dictionary->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
361 }
362
TEST_F(SpellcheckCustomDictionaryTest,ProcessSyncChanges)363 TEST_F(SpellcheckCustomDictionaryTest, ProcessSyncChanges) {
364 SpellcheckService* spellcheck_service =
365 SpellcheckServiceFactory::GetForContext(&profile_);
366 SpellcheckCustomDictionary* dictionary =
367 spellcheck_service->GetCustomDictionary();
368
369 dictionary->AddWord("foo");
370 dictionary->AddWord("bar");
371
372 syncer::SyncChangeList changes;
373 {
374 // Add existing word.
375 std::string word = "foo";
376 sync_pb::EntitySpecifics specifics;
377 specifics.mutable_dictionary()->set_word(word);
378 changes.push_back(syncer::SyncChange(
379 FROM_HERE,
380 syncer::SyncChange::ACTION_ADD,
381 syncer::SyncData::CreateLocalData(word, word, specifics)));
382 }
383 {
384 // Add invalid word. This word is too long.
385 std::string word = "01234567890123456789012345678901234567890123456789"
386 "01234567890123456789012345678901234567890123456789";
387 sync_pb::EntitySpecifics specifics;
388 specifics.mutable_dictionary()->set_word(word);
389 changes.push_back(syncer::SyncChange(
390 FROM_HERE,
391 syncer::SyncChange::ACTION_ADD,
392 syncer::SyncData::CreateLocalData(word, word, specifics)));
393 }
394 {
395 // Add valid word.
396 std::string word = "baz";
397 sync_pb::EntitySpecifics specifics;
398 specifics.mutable_dictionary()->set_word(word);
399 changes.push_back(syncer::SyncChange(
400 FROM_HERE,
401 syncer::SyncChange::ACTION_ADD,
402 syncer::SyncData::CreateLocalData(word, word, specifics)));
403 }
404 {
405 // Remove missing word.
406 std::string word = "snafoo";
407 sync_pb::EntitySpecifics specifics;
408 specifics.mutable_dictionary()->set_word(word);
409 changes.push_back(syncer::SyncChange(
410 FROM_HERE,
411 syncer::SyncChange::ACTION_DELETE,
412 syncer::SyncData::CreateLocalData(word, word, specifics)));
413 }
414 {
415 // Remove existing word.
416 std::string word = "bar";
417 sync_pb::EntitySpecifics specifics;
418 specifics.mutable_dictionary()->set_word(word);
419 changes.push_back(syncer::SyncChange(
420 FROM_HERE,
421 syncer::SyncChange::ACTION_DELETE,
422 syncer::SyncData::CreateLocalData(word, word, specifics)));
423 }
424
425 EXPECT_FALSE(dictionary->ProcessSyncChanges(FROM_HERE, changes).IsSet());
426
427 const std::set<std::string>& words = dictionary->GetWords();
428 EXPECT_EQ(2UL, words.size());
429 EXPECT_EQ(0UL, words.count("bar"));
430 EXPECT_EQ(1UL, words.count("foo"));
431 EXPECT_EQ(1UL, words.count("baz"));
432 }
433
TEST_F(SpellcheckCustomDictionaryTest,MergeDataAndStartSyncing)434 TEST_F(SpellcheckCustomDictionaryTest, MergeDataAndStartSyncing) {
435 SpellcheckService* spellcheck_service =
436 SpellcheckServiceFactory::GetForContext(&profile_);
437 SpellcheckCustomDictionary* custom_dictionary =
438 spellcheck_service->GetCustomDictionary();
439 TestingProfile profile2;
440 SpellcheckService* spellcheck_service2 = static_cast<SpellcheckService*>(
441 SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
442 &profile2, base::BindRepeating(&BuildSpellcheckService)));
443 SpellcheckCustomDictionary* custom_dictionary2 =
444 spellcheck_service2->GetCustomDictionary();
445
446 SpellcheckCustomDictionary::Change change;
447 for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords / 2; ++i) {
448 change.AddWord("foo" + base::NumberToString(i));
449 }
450 Apply(*custom_dictionary, change);
451
452 SpellcheckCustomDictionary::Change change2;
453 for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords / 2; ++i) {
454 change2.AddWord("bar" + base::NumberToString(i));
455 }
456 Apply(*custom_dictionary2, change2);
457
458 int error_counter = 0;
459 EXPECT_FALSE(
460 custom_dictionary
461 ->MergeDataAndStartSyncing(
462 syncer::DICTIONARY,
463 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY),
464 std::unique_ptr<syncer::SyncChangeProcessor>(
465 new syncer::SyncChangeProcessorWrapperForTest(
466 custom_dictionary2)),
467 std::unique_ptr<syncer::SyncErrorFactory>(
468 new SyncErrorFactoryStub(&error_counter)))
469 .error()
470 .IsSet());
471 EXPECT_EQ(0, error_counter);
472 EXPECT_TRUE(custom_dictionary->IsSyncing());
473
474 std::set<std::string> words = custom_dictionary->GetWords();
475 std::set<std::string> words2 = custom_dictionary2->GetWords();
476 EXPECT_EQ(words.size(), words2.size());
477 EXPECT_EQ(words, words2);
478 }
479
TEST_F(SpellcheckCustomDictionaryTest,SyncBeforeLoadDoesNotDuplicateWords)480 TEST_F(SpellcheckCustomDictionaryTest, SyncBeforeLoadDoesNotDuplicateWords) {
481 // Test triggers network requests since it indirectly instantiates
482 // SpellcheckHunspellDictionary's.
483 // Install a mock server to avoid sending random network request to
484 // localhost.
485 net::EmbeddedTestServer embedded_test_server;
486 embedded_test_server.RegisterRequestHandler(
487 base::BindRepeating([](const net::test_server::HttpRequest& request) {
488 // Mock implementation to hang the request.
489 std::unique_ptr<net::test_server::HttpResponse> response;
490 return response;
491 }));
492 net::test_server::RegisterDefaultHandlers(&embedded_test_server);
493 ASSERT_TRUE(embedded_test_server.Start());
494
495 // Forcibly set a hanging URL.
496 GURL url = embedded_test_server.GetURL("/hang");
497 SpellcheckHunspellDictionary::SetDownloadURLForTesting(url);
498
499 SpellcheckCustomDictionary* custom_dictionary =
500 SpellcheckServiceFactory::GetForContext(&profile_)->GetCustomDictionary();
501
502 TestingProfile profile2;
503 SpellcheckCustomDictionary* custom_dictionary2 =
504 static_cast<SpellcheckService*>(
505 SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
506 &profile2, base::BindRepeating(&BuildSpellcheckService)))
507 ->GetCustomDictionary();
508
509 std::unique_ptr<SpellcheckCustomDictionary::Change> change(
510 new SpellcheckCustomDictionary::Change);
511 change->AddWord("foo");
512 Apply(*custom_dictionary2, *change);
513
514 base::FilePath path =
515 profile_.GetPath().Append(chrome::kCustomDictionaryFileName);
516 UpdateDictionaryFile(std::move(change), path);
517 EXPECT_TRUE(custom_dictionary->GetWords().empty());
518
519 int error_counter = 0;
520 EXPECT_FALSE(
521 custom_dictionary
522 ->MergeDataAndStartSyncing(
523 syncer::DICTIONARY,
524 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY),
525 std::unique_ptr<syncer::SyncChangeProcessor>(
526 new syncer::SyncChangeProcessorWrapperForTest(
527 custom_dictionary2)),
528 std::unique_ptr<syncer::SyncErrorFactory>(
529 new SyncErrorFactoryStub(&error_counter)))
530 .error()
531 .IsSet());
532 EXPECT_EQ(0, error_counter);
533 EXPECT_TRUE(custom_dictionary->IsSyncing());
534
535 std::set<std::string> expected_words_in_memory;
536 expected_words_in_memory.insert("foo");
537 EXPECT_EQ(expected_words_in_memory, custom_dictionary->GetWords());
538
539 // Finish all writes to disk.
540 content::RunAllTasksUntilIdle();
541
542 std::string actual_contents_on_disk;
543 base::ReadFileToString(path, &actual_contents_on_disk);
544 static const char kExpectedContentsPrefix[] = "foo\nchecksum_v1 = ";
545 EXPECT_EQ(
546 kExpectedContentsPrefix,
547 actual_contents_on_disk.substr(0, sizeof kExpectedContentsPrefix - 1));
548 }
549
TEST_F(SpellcheckCustomDictionaryTest,DictionaryTooBigBeforeSyncing)550 TEST_F(SpellcheckCustomDictionaryTest, DictionaryTooBigBeforeSyncing) {
551 SpellcheckService* spellcheck_service =
552 SpellcheckServiceFactory::GetForContext(&profile_);
553 SpellcheckCustomDictionary* custom_dictionary =
554 spellcheck_service->GetCustomDictionary();
555 TestingProfile profile2;
556 SpellcheckService* spellcheck_service2 = static_cast<SpellcheckService*>(
557 SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
558 &profile2, base::BindRepeating(&BuildSpellcheckService)));
559 SpellcheckCustomDictionary* custom_dictionary2 =
560 spellcheck_service2->GetCustomDictionary();
561
562 SpellcheckCustomDictionary::Change change;
563 for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords + 1; ++i) {
564 change.AddWord("foo" + base::NumberToString(i));
565 }
566 Apply(*custom_dictionary, change);
567
568 int error_counter = 0;
569 EXPECT_FALSE(
570 custom_dictionary
571 ->MergeDataAndStartSyncing(
572 syncer::DICTIONARY,
573 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY),
574 std::unique_ptr<syncer::SyncChangeProcessor>(
575 new syncer::SyncChangeProcessorWrapperForTest(
576 custom_dictionary2)),
577 std::unique_ptr<syncer::SyncErrorFactory>(
578 new SyncErrorFactoryStub(&error_counter)))
579 .error()
580 .IsSet());
581 EXPECT_EQ(0, error_counter);
582 EXPECT_FALSE(custom_dictionary->IsSyncing());
583
584 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
585 custom_dictionary->GetWords().size());
586 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
587 custom_dictionary2->GetWords().size());
588
589 EXPECT_EQ(
590 spellcheck::kMaxSyncableDictionaryWords,
591 custom_dictionary->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
592 EXPECT_EQ(
593 spellcheck::kMaxSyncableDictionaryWords,
594 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
595 }
596
TEST_F(SpellcheckCustomDictionaryTest,DictionaryTooBigAndServerFull)597 TEST_F(SpellcheckCustomDictionaryTest, DictionaryTooBigAndServerFull) {
598 SpellcheckService* spellcheck_service =
599 SpellcheckServiceFactory::GetForContext(&profile_);
600 SpellcheckCustomDictionary* custom_dictionary =
601 spellcheck_service->GetCustomDictionary();
602 TestingProfile profile2;
603 SpellcheckService* spellcheck_service2 = static_cast<SpellcheckService*>(
604 SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
605 &profile2, base::BindRepeating(&BuildSpellcheckService)));
606 SpellcheckCustomDictionary* custom_dictionary2 =
607 spellcheck_service2->GetCustomDictionary();
608
609 SpellcheckCustomDictionary::Change change;
610 SpellcheckCustomDictionary::Change change2;
611 for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords; ++i) {
612 change.AddWord("foo" + base::NumberToString(i));
613 change2.AddWord("bar" + base::NumberToString(i));
614 }
615 change.AddWord("foo");
616 Apply(*custom_dictionary, change);
617 Apply(*custom_dictionary2, change2);
618
619 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
620 custom_dictionary->GetWords().size());
621 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
622 custom_dictionary2->GetWords().size());
623
624 int error_counter = 0;
625 EXPECT_FALSE(
626 custom_dictionary
627 ->MergeDataAndStartSyncing(
628 syncer::DICTIONARY,
629 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY),
630 std::unique_ptr<syncer::SyncChangeProcessor>(
631 new syncer::SyncChangeProcessorWrapperForTest(
632 custom_dictionary2)),
633 std::unique_ptr<syncer::SyncErrorFactory>(
634 new SyncErrorFactoryStub(&error_counter)))
635 .error()
636 .IsSet());
637 EXPECT_EQ(0, error_counter);
638 EXPECT_FALSE(custom_dictionary->IsSyncing());
639
640 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords * 2 + 1,
641 custom_dictionary->GetWords().size());
642 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
643 custom_dictionary2->GetWords().size());
644
645 EXPECT_EQ(
646 spellcheck::kMaxSyncableDictionaryWords,
647 custom_dictionary->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
648 EXPECT_EQ(
649 spellcheck::kMaxSyncableDictionaryWords,
650 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
651 }
652
TEST_F(SpellcheckCustomDictionaryTest,ServerTooBig)653 TEST_F(SpellcheckCustomDictionaryTest, ServerTooBig) {
654 SpellcheckService* spellcheck_service =
655 SpellcheckServiceFactory::GetForContext(&profile_);
656 SpellcheckCustomDictionary* custom_dictionary =
657 spellcheck_service->GetCustomDictionary();
658 TestingProfile profile2;
659 SpellcheckService* spellcheck_service2 = static_cast<SpellcheckService*>(
660 SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
661 &profile2, base::BindRepeating(&BuildSpellcheckService)));
662 SpellcheckCustomDictionary* custom_dictionary2 =
663 spellcheck_service2->GetCustomDictionary();
664
665 SpellcheckCustomDictionary::Change change;
666 SpellcheckCustomDictionary::Change change2;
667 for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords + 1; ++i) {
668 change.AddWord("foo" + base::NumberToString(i));
669 change2.AddWord("bar" + base::NumberToString(i));
670 }
671 Apply(*custom_dictionary, change);
672 Apply(*custom_dictionary2, change2);
673
674 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
675 custom_dictionary->GetWords().size());
676 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
677 custom_dictionary2->GetWords().size());
678
679 int error_counter = 0;
680 EXPECT_FALSE(custom_dictionary
681 ->MergeDataAndStartSyncing(
682 syncer::DICTIONARY,
683 GetAllSyncDataNoLimit(custom_dictionary2),
684 std::unique_ptr<syncer::SyncChangeProcessor>(
685 new syncer::SyncChangeProcessorWrapperForTest(
686 custom_dictionary2)),
687 std::unique_ptr<syncer::SyncErrorFactory>(
688 new SyncErrorFactoryStub(&error_counter)))
689 .error()
690 .IsSet());
691 EXPECT_EQ(0, error_counter);
692 EXPECT_FALSE(custom_dictionary->IsSyncing());
693
694 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords * 2 + 2,
695 custom_dictionary->GetWords().size());
696 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
697 custom_dictionary2->GetWords().size());
698
699 EXPECT_EQ(
700 spellcheck::kMaxSyncableDictionaryWords,
701 custom_dictionary->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
702 EXPECT_EQ(
703 spellcheck::kMaxSyncableDictionaryWords,
704 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
705 }
706
TEST_F(SpellcheckCustomDictionaryTest,DictionaryTooBigToStartSyncing)707 TEST_F(SpellcheckCustomDictionaryTest, DictionaryTooBigToStartSyncing) {
708 SpellcheckService* spellcheck_service =
709 SpellcheckServiceFactory::GetForContext(&profile_);
710 SpellcheckCustomDictionary* custom_dictionary =
711 spellcheck_service->GetCustomDictionary();
712 TestingProfile profile2;
713 SpellcheckService* spellcheck_service2 = static_cast<SpellcheckService*>(
714 SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
715 &profile2, base::BindRepeating(&BuildSpellcheckService)));
716 SpellcheckCustomDictionary* custom_dictionary2 =
717 spellcheck_service2->GetCustomDictionary();
718
719 SpellcheckCustomDictionary::Change change;
720 for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords - 1; ++i) {
721 change.AddWord("foo" + base::NumberToString(i));
722 }
723 Apply(*custom_dictionary, change);
724
725 custom_dictionary2->AddWord("bar");
726 custom_dictionary2->AddWord("baz");
727
728 int error_counter = 0;
729 EXPECT_FALSE(
730 custom_dictionary
731 ->MergeDataAndStartSyncing(
732 syncer::DICTIONARY,
733 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY),
734 std::unique_ptr<syncer::SyncChangeProcessor>(
735 new syncer::SyncChangeProcessorWrapperForTest(
736 custom_dictionary2)),
737 std::unique_ptr<syncer::SyncErrorFactory>(
738 new SyncErrorFactoryStub(&error_counter)))
739 .error()
740 .IsSet());
741 EXPECT_EQ(0, error_counter);
742 EXPECT_FALSE(custom_dictionary->IsSyncing());
743
744 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
745 custom_dictionary->GetWords().size());
746 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
747 custom_dictionary2->GetWords().size());
748
749 EXPECT_EQ(
750 spellcheck::kMaxSyncableDictionaryWords,
751 custom_dictionary->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
752 EXPECT_EQ(
753 spellcheck::kMaxSyncableDictionaryWords,
754 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
755 }
756
TEST_F(SpellcheckCustomDictionaryTest,DictionaryTooBigToContiueSyncing)757 TEST_F(SpellcheckCustomDictionaryTest, DictionaryTooBigToContiueSyncing) {
758 SpellcheckService* spellcheck_service =
759 SpellcheckServiceFactory::GetForContext(&profile_);
760 SpellcheckCustomDictionary* custom_dictionary =
761 spellcheck_service->GetCustomDictionary();
762 TestingProfile profile2;
763 SpellcheckService* spellcheck_service2 = static_cast<SpellcheckService*>(
764 SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
765 &profile2, base::BindRepeating(&BuildSpellcheckService)));
766 SpellcheckCustomDictionary* custom_dictionary2 =
767 spellcheck_service2->GetCustomDictionary();
768
769 SpellcheckCustomDictionary::Change change;
770 for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords - 1; ++i) {
771 change.AddWord("foo" + base::NumberToString(i));
772 }
773 Apply(*custom_dictionary, change);
774
775 int error_counter = 0;
776 EXPECT_FALSE(
777 custom_dictionary
778 ->MergeDataAndStartSyncing(
779 syncer::DICTIONARY,
780 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY),
781 std::unique_ptr<syncer::SyncChangeProcessor>(
782 new syncer::SyncChangeProcessorWrapperForTest(
783 custom_dictionary2)),
784 std::unique_ptr<syncer::SyncErrorFactory>(
785 new SyncErrorFactoryStub(&error_counter)))
786 .error()
787 .IsSet());
788 EXPECT_EQ(0, error_counter);
789 EXPECT_TRUE(custom_dictionary->IsSyncing());
790
791 custom_dictionary->AddWord("bar");
792 EXPECT_EQ(0, error_counter);
793 EXPECT_TRUE(custom_dictionary->IsSyncing());
794
795 custom_dictionary->AddWord("baz");
796 EXPECT_EQ(0, error_counter);
797 EXPECT_FALSE(custom_dictionary->IsSyncing());
798
799 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
800 custom_dictionary->GetWords().size());
801 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
802 custom_dictionary2->GetWords().size());
803
804 EXPECT_EQ(
805 spellcheck::kMaxSyncableDictionaryWords,
806 custom_dictionary->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
807 EXPECT_EQ(
808 spellcheck::kMaxSyncableDictionaryWords,
809 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
810 }
811
TEST_F(SpellcheckCustomDictionaryTest,LoadAfterSyncStart)812 TEST_F(SpellcheckCustomDictionaryTest, LoadAfterSyncStart) {
813 SpellcheckService* spellcheck_service =
814 SpellcheckServiceFactory::GetForContext(&profile_);
815 SpellcheckCustomDictionary* custom_dictionary =
816 spellcheck_service->GetCustomDictionary();
817 TestingProfile profile2;
818 SpellcheckService* spellcheck_service2 = static_cast<SpellcheckService*>(
819 SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
820 &profile2, base::BindRepeating(&BuildSpellcheckService)));
821 SpellcheckCustomDictionary* custom_dictionary2 =
822 spellcheck_service2->GetCustomDictionary();
823
824 custom_dictionary->AddWord("foo");
825
826 int error_counter = 0;
827 EXPECT_FALSE(
828 custom_dictionary
829 ->MergeDataAndStartSyncing(
830 syncer::DICTIONARY,
831 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY),
832 std::unique_ptr<syncer::SyncChangeProcessor>(
833 new syncer::SyncChangeProcessorWrapperForTest(
834 custom_dictionary2)),
835 std::unique_ptr<syncer::SyncErrorFactory>(
836 new SyncErrorFactoryStub(&error_counter)))
837 .error()
838 .IsSet());
839 EXPECT_EQ(0, error_counter);
840 EXPECT_TRUE(custom_dictionary->IsSyncing());
841
842 std::unique_ptr<std::set<std::string>> custom_words(
843 new std::set<std::string>);
844 custom_words->insert("bar");
845 OnLoaded(*custom_dictionary, std::move(custom_words));
846 EXPECT_TRUE(custom_dictionary->IsSyncing());
847
848 EXPECT_EQ(2UL, custom_dictionary->GetWords().size());
849 EXPECT_EQ(2UL, custom_dictionary2->GetWords().size());
850
851 EXPECT_EQ(
852 2UL,
853 custom_dictionary->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
854 EXPECT_EQ(
855 2UL,
856 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
857 }
858
TEST_F(SpellcheckCustomDictionaryTest,LoadAfterSyncStartTooBigToSync)859 TEST_F(SpellcheckCustomDictionaryTest, LoadAfterSyncStartTooBigToSync) {
860 SpellcheckService* spellcheck_service =
861 SpellcheckServiceFactory::GetForContext(&profile_);
862 SpellcheckCustomDictionary* custom_dictionary =
863 spellcheck_service->GetCustomDictionary();
864 TestingProfile profile2;
865 SpellcheckService* spellcheck_service2 = static_cast<SpellcheckService*>(
866 SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
867 &profile2, base::BindRepeating(&BuildSpellcheckService)));
868 SpellcheckCustomDictionary* custom_dictionary2 =
869 spellcheck_service2->GetCustomDictionary();
870
871 custom_dictionary->AddWord("foo");
872
873 int error_counter = 0;
874 EXPECT_FALSE(
875 custom_dictionary
876 ->MergeDataAndStartSyncing(
877 syncer::DICTIONARY,
878 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY),
879 std::unique_ptr<syncer::SyncChangeProcessor>(
880 new syncer::SyncChangeProcessorWrapperForTest(
881 custom_dictionary2)),
882 std::unique_ptr<syncer::SyncErrorFactory>(
883 new SyncErrorFactoryStub(&error_counter)))
884 .error()
885 .IsSet());
886 EXPECT_EQ(0, error_counter);
887 EXPECT_TRUE(custom_dictionary->IsSyncing());
888
889 std::unique_ptr<std::set<std::string>> custom_words(
890 new std::set<std::string>);
891 for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords; ++i) {
892 custom_words->insert(custom_words->end(), "foo" + base::NumberToString(i));
893 }
894 OnLoaded(*custom_dictionary, std::move(custom_words));
895 EXPECT_EQ(0, error_counter);
896 EXPECT_FALSE(custom_dictionary->IsSyncing());
897
898 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords + 1,
899 custom_dictionary->GetWords().size());
900 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
901 custom_dictionary2->GetWords().size());
902
903 EXPECT_EQ(
904 spellcheck::kMaxSyncableDictionaryWords,
905 custom_dictionary->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
906 EXPECT_EQ(
907 spellcheck::kMaxSyncableDictionaryWords,
908 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
909 }
910
TEST_F(SpellcheckCustomDictionaryTest,LoadDuplicatesAfterSync)911 TEST_F(SpellcheckCustomDictionaryTest, LoadDuplicatesAfterSync) {
912 SpellcheckService* spellcheck_service =
913 SpellcheckServiceFactory::GetForContext(&profile_);
914 SpellcheckCustomDictionary* custom_dictionary =
915 spellcheck_service->GetCustomDictionary();
916 TestingProfile profile2;
917 SpellcheckService* spellcheck_service2 = static_cast<SpellcheckService*>(
918 SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
919 &profile2, base::BindRepeating(&BuildSpellcheckService)));
920 SpellcheckCustomDictionary* custom_dictionary2 =
921 spellcheck_service2->GetCustomDictionary();
922
923 SpellcheckCustomDictionary::Change change;
924 for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords / 2; ++i) {
925 change.AddWord("foo" + base::NumberToString(i));
926 }
927 Apply(*custom_dictionary, change);
928
929 int error_counter = 0;
930 EXPECT_FALSE(
931 custom_dictionary
932 ->MergeDataAndStartSyncing(
933 syncer::DICTIONARY,
934 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY),
935 std::unique_ptr<syncer::SyncChangeProcessor>(
936 new syncer::SyncChangeProcessorWrapperForTest(
937 custom_dictionary2)),
938 std::unique_ptr<syncer::SyncErrorFactory>(
939 new SyncErrorFactoryStub(&error_counter)))
940 .error()
941 .IsSet());
942 EXPECT_EQ(0, error_counter);
943 EXPECT_TRUE(custom_dictionary->IsSyncing());
944
945 OnLoaded(*custom_dictionary,
946 std::make_unique<std::set<std::string>>(change.to_add()));
947 EXPECT_EQ(0, error_counter);
948 EXPECT_TRUE(custom_dictionary->IsSyncing());
949
950 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords / 2,
951 custom_dictionary->GetWords().size());
952 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords / 2,
953 custom_dictionary2->GetWords().size());
954
955 EXPECT_EQ(
956 spellcheck::kMaxSyncableDictionaryWords / 2,
957 custom_dictionary->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
958 EXPECT_EQ(
959 spellcheck::kMaxSyncableDictionaryWords / 2,
960 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY).size());
961 }
962
TEST_F(SpellcheckCustomDictionaryTest,DictionaryLoadNotification)963 TEST_F(SpellcheckCustomDictionaryTest, DictionaryLoadNotification) {
964 SpellcheckService* spellcheck_service =
965 SpellcheckServiceFactory::GetForContext(&profile_);
966 SpellcheckCustomDictionary* custom_dictionary =
967 spellcheck_service->GetCustomDictionary();
968
969 DictionaryObserverCounter observer;
970 custom_dictionary->AddObserver(&observer);
971
972 std::unique_ptr<std::set<std::string>> custom_words(
973 new std::set<std::string>);
974 custom_words->insert("foo");
975 custom_words->insert("bar");
976 OnLoaded(*custom_dictionary, std::move(custom_words));
977
978 EXPECT_GE(observer.loads(), 1);
979 EXPECT_LE(observer.loads(), 2);
980 EXPECT_EQ(0, observer.changes());
981
982 custom_dictionary->RemoveObserver(&observer);
983 }
984
TEST_F(SpellcheckCustomDictionaryTest,DictionaryAddWordNotification)985 TEST_F(SpellcheckCustomDictionaryTest, DictionaryAddWordNotification) {
986 SpellcheckService* spellcheck_service =
987 SpellcheckServiceFactory::GetForContext(&profile_);
988 SpellcheckCustomDictionary* custom_dictionary =
989 spellcheck_service->GetCustomDictionary();
990
991 OnLoaded(*custom_dictionary, base::WrapUnique(new std::set<std::string>));
992
993 DictionaryObserverCounter observer;
994 custom_dictionary->AddObserver(&observer);
995
996 EXPECT_TRUE(custom_dictionary->AddWord("foo"));
997 EXPECT_TRUE(custom_dictionary->AddWord("bar"));
998 EXPECT_FALSE(custom_dictionary->AddWord("bar"));
999
1000 EXPECT_EQ(2, observer.changes());
1001
1002 custom_dictionary->RemoveObserver(&observer);
1003 }
1004
TEST_F(SpellcheckCustomDictionaryTest,DictionaryRemoveWordNotification)1005 TEST_F(SpellcheckCustomDictionaryTest, DictionaryRemoveWordNotification) {
1006 SpellcheckService* spellcheck_service =
1007 SpellcheckServiceFactory::GetForContext(&profile_);
1008 SpellcheckCustomDictionary* custom_dictionary =
1009 spellcheck_service->GetCustomDictionary();
1010
1011 OnLoaded(*custom_dictionary, base::WrapUnique(new std::set<std::string>));
1012
1013 EXPECT_TRUE(custom_dictionary->AddWord("foo"));
1014 EXPECT_TRUE(custom_dictionary->AddWord("bar"));
1015
1016 DictionaryObserverCounter observer;
1017 custom_dictionary->AddObserver(&observer);
1018
1019 EXPECT_TRUE(custom_dictionary->RemoveWord("foo"));
1020 EXPECT_TRUE(custom_dictionary->RemoveWord("bar"));
1021 EXPECT_FALSE(custom_dictionary->RemoveWord("baz"));
1022
1023 EXPECT_EQ(2, observer.changes());
1024
1025 custom_dictionary->RemoveObserver(&observer);
1026 }
1027
TEST_F(SpellcheckCustomDictionaryTest,DictionarySyncNotification)1028 TEST_F(SpellcheckCustomDictionaryTest, DictionarySyncNotification) {
1029 SpellcheckService* spellcheck_service =
1030 SpellcheckServiceFactory::GetForContext(&profile_);
1031 SpellcheckCustomDictionary* custom_dictionary =
1032 spellcheck_service->GetCustomDictionary();
1033 TestingProfile profile2;
1034 SpellcheckService* spellcheck_service2 = static_cast<SpellcheckService*>(
1035 SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
1036 &profile2, base::BindRepeating(&BuildSpellcheckService)));
1037 SpellcheckCustomDictionary* custom_dictionary2 =
1038 spellcheck_service2->GetCustomDictionary();
1039
1040 OnLoaded(*custom_dictionary, base::WrapUnique(new std::set<std::string>));
1041 OnLoaded(*custom_dictionary2, base::WrapUnique(new std::set<std::string>));
1042
1043 custom_dictionary->AddWord("foo");
1044 custom_dictionary->AddWord("bar");
1045 custom_dictionary2->AddWord("foo");
1046 custom_dictionary2->AddWord("baz");
1047
1048 DictionaryObserverCounter observer;
1049 custom_dictionary->AddObserver(&observer);
1050
1051 DictionaryObserverCounter observer2;
1052 custom_dictionary2->AddObserver(&observer2);
1053
1054 int error_counter = 0;
1055 EXPECT_FALSE(
1056 custom_dictionary
1057 ->MergeDataAndStartSyncing(
1058 syncer::DICTIONARY,
1059 custom_dictionary2->GetAllSyncDataForTesting(syncer::DICTIONARY),
1060 std::unique_ptr<syncer::SyncChangeProcessor>(
1061 new syncer::SyncChangeProcessorWrapperForTest(
1062 custom_dictionary2)),
1063 std::unique_ptr<syncer::SyncErrorFactory>(
1064 new SyncErrorFactoryStub(&error_counter)))
1065 .error()
1066 .IsSet());
1067 EXPECT_EQ(0, error_counter);
1068 EXPECT_TRUE(custom_dictionary->IsSyncing());
1069
1070 EXPECT_EQ(1, observer.changes());
1071 EXPECT_EQ(1, observer2.changes());
1072
1073 custom_dictionary->RemoveObserver(&observer);
1074 custom_dictionary2->RemoveObserver(&observer2);
1075 }
1076
1077 // The server has maximum number of words and the client has maximum number of
1078 // different words before association time. No new words should be pushed to the
1079 // sync server upon association. The client should accept words from the sync
1080 // server, however.
TEST_F(SpellcheckCustomDictionaryTest,DictionarySyncLimit)1081 TEST_F(SpellcheckCustomDictionaryTest, DictionarySyncLimit) {
1082 TestingProfile server_profile;
1083 SpellcheckService* server_spellcheck_service =
1084 static_cast<SpellcheckService*>(
1085 SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
1086 &server_profile, base::BindRepeating(&BuildSpellcheckService)));
1087
1088 // Here, |server_custom_dictionary| plays the role of the sync server.
1089 SpellcheckCustomDictionary* server_custom_dictionary =
1090 server_spellcheck_service->GetCustomDictionary();
1091
1092 // Upload the maximum number of words to the sync server.
1093 {
1094 SpellcheckService* spellcheck_service =
1095 SpellcheckServiceFactory::GetForContext(&profile_);
1096 SpellcheckCustomDictionary* custom_dictionary =
1097 spellcheck_service->GetCustomDictionary();
1098
1099 SpellcheckCustomDictionary::Change change;
1100 for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords; ++i) {
1101 change.AddWord("foo" + base::NumberToString(i));
1102 }
1103 Apply(*custom_dictionary, change);
1104
1105 int error_counter = 0;
1106 EXPECT_FALSE(custom_dictionary
1107 ->MergeDataAndStartSyncing(
1108 syncer::DICTIONARY,
1109 server_custom_dictionary->GetAllSyncDataForTesting(
1110 syncer::DICTIONARY),
1111 std::unique_ptr<syncer::SyncChangeProcessor>(
1112 new syncer::SyncChangeProcessorWrapperForTest(
1113 server_custom_dictionary)),
1114 std::unique_ptr<syncer::SyncErrorFactory>(
1115 new SyncErrorFactoryStub(&error_counter)))
1116 .error()
1117 .IsSet());
1118 EXPECT_EQ(0, error_counter);
1119 EXPECT_TRUE(custom_dictionary->IsSyncing());
1120 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
1121 custom_dictionary->GetWords().size());
1122 }
1123
1124 // The sync server now has the maximum number of words.
1125 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
1126 server_custom_dictionary->GetWords().size());
1127
1128 // Associate the sync server with a client that also has the maximum number of
1129 // words, but all of these words are different from the ones on the sync
1130 // server.
1131 {
1132 TestingProfile client_profile;
1133 SpellcheckService* client_spellcheck_service =
1134 static_cast<SpellcheckService*>(
1135 SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
1136 &client_profile, base::BindRepeating(&BuildSpellcheckService)));
1137
1138 // Here, |client_custom_dictionary| plays the role of the client.
1139 SpellcheckCustomDictionary* client_custom_dictionary =
1140 client_spellcheck_service->GetCustomDictionary();
1141
1142 // Add the maximum number of words to the client. These words are all
1143 // different from those on the server.
1144 SpellcheckCustomDictionary::Change change;
1145 for (size_t i = 0; i < spellcheck::kMaxSyncableDictionaryWords; ++i) {
1146 change.AddWord("bar" + base::NumberToString(i));
1147 }
1148 Apply(*client_custom_dictionary, change);
1149
1150 // Associate the server and the client.
1151 int error_counter = 0;
1152 EXPECT_FALSE(client_custom_dictionary
1153 ->MergeDataAndStartSyncing(
1154 syncer::DICTIONARY,
1155 server_custom_dictionary->GetAllSyncDataForTesting(
1156 syncer::DICTIONARY),
1157 std::unique_ptr<syncer::SyncChangeProcessor>(
1158 new syncer::SyncChangeProcessorWrapperForTest(
1159 server_custom_dictionary)),
1160 std::unique_ptr<syncer::SyncErrorFactory>(
1161 new SyncErrorFactoryStub(&error_counter)))
1162 .error()
1163 .IsSet());
1164 EXPECT_EQ(0, error_counter);
1165 EXPECT_FALSE(client_custom_dictionary->IsSyncing());
1166 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords * 2,
1167 client_custom_dictionary->GetWords().size());
1168 }
1169
1170 // The sync server should not receive more words, because it has the maximum
1171 // number of words already.
1172 EXPECT_EQ(spellcheck::kMaxSyncableDictionaryWords,
1173 server_custom_dictionary->GetWords().size());
1174 }
1175
1176 #if defined(OS_WIN)
1177 // Failing consistently on Win7+. See crbug.com/230534.
1178 #define MAYBE_RecordSizeStatsCorrectly DISABLED_RecordSizeStatsCorrectly
1179 #else
1180 #define MAYBE_RecordSizeStatsCorrectly RecordSizeStatsCorrectly
1181 #endif
1182
TEST_F(SpellcheckCustomDictionaryTest,MAYBE_RecordSizeStatsCorrectly)1183 TEST_F(SpellcheckCustomDictionaryTest, MAYBE_RecordSizeStatsCorrectly) {
1184 // Record a baseline.
1185 SpellCheckHostMetrics::RecordCustomWordCountStats(123);
1186
1187 HistogramBase* histogram =
1188 StatisticsRecorder::FindHistogram("SpellCheck.CustomWords");
1189 ASSERT_TRUE(histogram != NULL);
1190 std::unique_ptr<HistogramSamples> baseline = histogram->SnapshotSamples();
1191
1192 // Load the dictionary which should be empty.
1193 base::FilePath path =
1194 profile_.GetPath().Append(chrome::kCustomDictionaryFileName);
1195 EXPECT_TRUE(LoadDictionaryFile(path)->words.empty());
1196
1197 // We expect there to be an entry with 0.
1198 histogram =
1199 StatisticsRecorder::FindHistogram("SpellCheck.CustomWords");
1200 ASSERT_TRUE(histogram != NULL);
1201 std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
1202
1203 samples->Subtract(*baseline);
1204 EXPECT_EQ(0,samples->sum());
1205
1206 std::unique_ptr<SpellcheckCustomDictionary::Change> change(
1207 new SpellcheckCustomDictionary::Change);
1208 change->AddWord("bar");
1209 change->AddWord("foo");
1210 UpdateDictionaryFile(std::move(change), path);
1211
1212 // Load the dictionary again and it should have 2 entries.
1213 EXPECT_EQ(2u, LoadDictionaryFile(path)->words.size());
1214
1215 histogram =
1216 StatisticsRecorder::FindHistogram("SpellCheck.CustomWords");
1217 ASSERT_TRUE(histogram != NULL);
1218 std::unique_ptr<HistogramSamples> samples2 = histogram->SnapshotSamples();
1219
1220 samples2->Subtract(*baseline);
1221 EXPECT_EQ(2,samples2->sum());
1222 }
1223
TEST_F(SpellcheckCustomDictionaryTest,HasWord)1224 TEST_F(SpellcheckCustomDictionaryTest, HasWord) {
1225 SpellcheckService* spellcheck_service =
1226 SpellcheckServiceFactory::GetForContext(&profile_);
1227 SpellcheckCustomDictionary* custom_dictionary =
1228 spellcheck_service->GetCustomDictionary();
1229 OnLoaded(*custom_dictionary, base::WrapUnique(new std::set<std::string>));
1230 EXPECT_FALSE(custom_dictionary->HasWord("foo"));
1231 EXPECT_FALSE(custom_dictionary->HasWord("bar"));
1232 custom_dictionary->AddWord("foo");
1233 EXPECT_TRUE(custom_dictionary->HasWord("foo"));
1234 EXPECT_FALSE(custom_dictionary->HasWord("bar"));
1235 }
1236