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