• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // Unit tests for |FeedbackSender| object.
6 
7 #include "chrome/browser/spellchecker/feedback_sender.h"
8 
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/json/json_reader.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/metrics/field_trial.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/common/spellcheck_common.h"
19 #include "chrome/common/spellcheck_marker.h"
20 #include "chrome/common/spellcheck_result.h"
21 #include "chrome/test/base/testing_profile.h"
22 #include "components/variations/entropy_provider.h"
23 #include "content/public/test/test_browser_thread.h"
24 #include "net/url_request/test_url_fetcher_factory.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 
27 namespace spellcheck {
28 
29 namespace {
30 
31 const char kCountry[] = "USA";
32 const char kLanguage[] = "en";
33 const char kText[] = "Helllo world.";
34 const int kMisspellingLength = 6;
35 const int kMisspellingStart = 0;
36 const int kRendererProcessId = 0;
37 const int kUrlFetcherId = 0;
38 
39 // Builds a simple spellcheck result.
BuildSpellCheckResult()40 SpellCheckResult BuildSpellCheckResult() {
41   return SpellCheckResult(SpellCheckResult::SPELLING,
42                           kMisspellingStart,
43                           kMisspellingLength,
44                           base::UTF8ToUTF16("Hello"));
45 }
46 
47 // Returns the number of times that |needle| appears in |haystack| without
48 // overlaps. For example, CountOccurences("bananana", "nana") returns 1.
CountOccurences(const std::string & haystack,const std::string & needle)49 int CountOccurences(const std::string& haystack, const std::string& needle) {
50   int number_of_occurrences = 0;
51   for (size_t pos = haystack.find(needle);
52        pos != std::string::npos;
53        pos = haystack.find(needle, pos + needle.length())) {
54     ++number_of_occurrences;
55   }
56   return number_of_occurrences;
57 }
58 
59 }  // namespace
60 
61 // A test fixture to help keep tests simple.
62 class FeedbackSenderTest : public testing::Test {
63  public:
FeedbackSenderTest()64   FeedbackSenderTest() : ui_thread_(content::BrowserThread::UI, &loop_) {
65     feedback_.reset(new FeedbackSender(NULL, kLanguage, kCountry));
66     feedback_->StartFeedbackCollection();
67   }
68 
~FeedbackSenderTest()69   virtual ~FeedbackSenderTest() {}
70 
71  protected:
72   // Appends the "--enable-spelling-service-feedback" switch to the
73   // command-line.
AppendCommandLineSwitch()74   void AppendCommandLineSwitch() {
75     // The command-line switch is temporary.
76     // TODO(rouslan): Remove the command-line switch. http://crbug.com/247726
77     CommandLine::ForCurrentProcess()->AppendSwitch(
78         switches::kEnableSpellingFeedbackFieldTrial);
79     feedback_.reset(new FeedbackSender(NULL, kLanguage, kCountry));
80     feedback_->StartFeedbackCollection();
81   }
82 
83   // Enables the "SpellingServiceFeedback.Enabled" field trial.
EnableFieldTrial()84   void EnableFieldTrial() {
85     // The field trial is temporary.
86     // TODO(rouslan): Remove the field trial. http://crbug.com/247726
87     field_trial_list_.reset(
88         new base::FieldTrialList(new metrics::SHA1EntropyProvider("foo")));
89     field_trial_ = base::FieldTrialList::CreateFieldTrial(
90         kFeedbackFieldTrialName, kFeedbackFieldTrialEnabledGroupName);
91     field_trial_->group();
92     feedback_.reset(new FeedbackSender(NULL, kLanguage, kCountry));
93     feedback_->StartFeedbackCollection();
94   }
95 
AddPendingFeedback()96   uint32 AddPendingFeedback() {
97     std::vector<SpellCheckResult> results(1, BuildSpellCheckResult());
98     feedback_->OnSpellcheckResults(kRendererProcessId,
99                                    base::UTF8ToUTF16(kText),
100                                    std::vector<SpellCheckMarker>(),
101                                    &results);
102     return results[0].hash;
103   }
104 
ExpireSession()105   void ExpireSession() {
106     feedback_->session_start_ =
107         base::Time::Now() -
108         base::TimeDelta::FromHours(chrome::spellcheck_common::kSessionHours);
109   }
110 
UploadDataContains(const std::string & data) const111   bool UploadDataContains(const std::string& data) const {
112     const net::TestURLFetcher* fetcher =
113         fetchers_.GetFetcherByID(kUrlFetcherId);
114     return fetcher && CountOccurences(fetcher->upload_data(), data) > 0;
115   }
116 
UploadDataContains(const std::string & data,int number_of_occurrences) const117   bool UploadDataContains(const std::string& data,
118                           int number_of_occurrences) const {
119     const net::TestURLFetcher* fetcher =
120         fetchers_.GetFetcherByID(kUrlFetcherId);
121     return fetcher && CountOccurences(fetcher->upload_data(), data) ==
122                           number_of_occurrences;
123   }
124 
125   // Returns true if the feedback sender would be uploading data now. The test
126   // does not open network connections.
IsUploadingData() const127   bool IsUploadingData() const {
128     return !!fetchers_.GetFetcherByID(kUrlFetcherId);
129   }
130 
ClearUploadData()131   void ClearUploadData() {
132     fetchers_.RemoveFetcherFromMap(kUrlFetcherId);
133   }
134 
GetUploadData() const135   std::string GetUploadData() const {
136     const net::TestURLFetcher* fetcher =
137         fetchers_.GetFetcherByID(kUrlFetcherId);
138     return fetcher ? fetcher->upload_data() : std::string();
139   }
140 
141   scoped_ptr<spellcheck::FeedbackSender> feedback_;
142 
143  private:
144   TestingProfile profile_;
145   base::MessageLoop loop_;
146   content::TestBrowserThread ui_thread_;
147   scoped_ptr<base::FieldTrialList> field_trial_list_;
148   scoped_refptr<base::FieldTrial> field_trial_;
149   net::TestURLFetcherFactory fetchers_;
150 };
151 
152 // Do not send data if there's no feedback.
TEST_F(FeedbackSenderTest,NoFeedback)153 TEST_F(FeedbackSenderTest, NoFeedback) {
154   EXPECT_FALSE(IsUploadingData());
155   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
156                                       std::vector<uint32>());
157   EXPECT_FALSE(IsUploadingData());
158 }
159 
160 // Do not send data if not aware of which markers are still in the document.
TEST_F(FeedbackSenderTest,NoDocumentMarkersReceived)161 TEST_F(FeedbackSenderTest, NoDocumentMarkersReceived) {
162   EXPECT_FALSE(IsUploadingData());
163   uint32 hash = AddPendingFeedback();
164   EXPECT_FALSE(IsUploadingData());
165   static const int kSuggestionIndex = 1;
166   feedback_->SelectedSuggestion(hash, kSuggestionIndex);
167   EXPECT_FALSE(IsUploadingData());
168 }
169 
170 // Send PENDING feedback message if the marker is still in the document, and the
171 // user has not performed any action on it.
TEST_F(FeedbackSenderTest,PendingFeedback)172 TEST_F(FeedbackSenderTest, PendingFeedback) {
173   uint32 hash = AddPendingFeedback();
174   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
175                                       std::vector<uint32>(1, hash));
176   EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
177 }
178 
179 // Send NO_ACTION feedback message if the marker has been removed from the
180 // document.
TEST_F(FeedbackSenderTest,NoActionFeedback)181 TEST_F(FeedbackSenderTest, NoActionFeedback) {
182   AddPendingFeedback();
183   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
184                                       std::vector<uint32>());
185   EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
186 }
187 
188 // Send SELECT feedback message if the user has selected a spelling suggestion.
TEST_F(FeedbackSenderTest,SelectFeedback)189 TEST_F(FeedbackSenderTest, SelectFeedback) {
190   uint32 hash = AddPendingFeedback();
191   static const int kSuggestionIndex = 0;
192   feedback_->SelectedSuggestion(hash, kSuggestionIndex);
193   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
194                                       std::vector<uint32>());
195   EXPECT_TRUE(UploadDataContains("\"actionType\":\"SELECT\""));
196   EXPECT_TRUE(UploadDataContains("\"actionTargetIndex\":" + kSuggestionIndex));
197 }
198 
199 // Send ADD_TO_DICT feedback message if the user has added the misspelled word
200 // to the custom dictionary.
TEST_F(FeedbackSenderTest,AddToDictFeedback)201 TEST_F(FeedbackSenderTest, AddToDictFeedback) {
202   uint32 hash = AddPendingFeedback();
203   feedback_->AddedToDictionary(hash);
204   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
205                                       std::vector<uint32>());
206   EXPECT_TRUE(UploadDataContains("\"actionType\":\"ADD_TO_DICT\""));
207 }
208 
209 // Send IN_DICTIONARY feedback message if the user has the misspelled word in
210 // the custom dictionary.
TEST_F(FeedbackSenderTest,InDictionaryFeedback)211 TEST_F(FeedbackSenderTest, InDictionaryFeedback) {
212   uint32 hash = AddPendingFeedback();
213   feedback_->RecordInDictionary(hash);
214   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
215                                       std::vector<uint32>());
216   EXPECT_TRUE(UploadDataContains("\"actionType\":\"IN_DICTIONARY\""));
217 }
218 
219 // Send PENDING feedback message if the user saw the spelling suggestion, but
220 // decided to not select it, and the marker is still in the document.
TEST_F(FeedbackSenderTest,IgnoreFeedbackMarkerInDocument)221 TEST_F(FeedbackSenderTest, IgnoreFeedbackMarkerInDocument) {
222   uint32 hash = AddPendingFeedback();
223   feedback_->IgnoredSuggestions(hash);
224   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
225                                       std::vector<uint32>(1, hash));
226   EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
227 }
228 
229 // Send IGNORE feedback message if the user saw the spelling suggestion, but
230 // decided to not select it, and the marker is no longer in the document.
TEST_F(FeedbackSenderTest,IgnoreFeedbackMarkerNotInDocument)231 TEST_F(FeedbackSenderTest, IgnoreFeedbackMarkerNotInDocument) {
232   uint32 hash = AddPendingFeedback();
233   feedback_->IgnoredSuggestions(hash);
234   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
235                                       std::vector<uint32>());
236   EXPECT_TRUE(UploadDataContains("\"actionType\":\"IGNORE\""));
237 }
238 
239 // Send MANUALLY_CORRECTED feedback message if the user manually corrected the
240 // misspelled word.
TEST_F(FeedbackSenderTest,ManuallyCorrectedFeedback)241 TEST_F(FeedbackSenderTest, ManuallyCorrectedFeedback) {
242   uint32 hash = AddPendingFeedback();
243   static const std::string kManualCorrection = "Howdy";
244   feedback_->ManuallyCorrected(hash, base::ASCIIToUTF16(kManualCorrection));
245   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
246                                       std::vector<uint32>());
247   EXPECT_TRUE(UploadDataContains("\"actionType\":\"MANUALLY_CORRECTED\""));
248   EXPECT_TRUE(UploadDataContains("\"actionTargetValue\":\"" +
249                                  kManualCorrection + "\""));
250 }
251 
252 // Send feedback messages in batch.
TEST_F(FeedbackSenderTest,BatchFeedback)253 TEST_F(FeedbackSenderTest, BatchFeedback) {
254   std::vector<SpellCheckResult> results;
255   results.push_back(SpellCheckResult(SpellCheckResult::SPELLING,
256                                      kMisspellingStart,
257                                      kMisspellingLength,
258                                      base::ASCIIToUTF16("Hello")));
259   static const int kSecondMisspellingStart = 7;
260   static const int kSecondMisspellingLength = 5;
261   results.push_back(SpellCheckResult(SpellCheckResult::SPELLING,
262                                      kSecondMisspellingStart,
263                                      kSecondMisspellingLength,
264                                      base::ASCIIToUTF16("world")));
265   feedback_->OnSpellcheckResults(kRendererProcessId,
266                                  base::UTF8ToUTF16(kText),
267                                  std::vector<SpellCheckMarker>(),
268                                  &results);
269   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
270                                       std::vector<uint32>());
271   EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\"", 2));
272 }
273 
274 // Send a series of PENDING feedback messages and one final NO_ACTION feedback
275 // message with the same hash identifier for a single misspelling.
TEST_F(FeedbackSenderTest,SameHashFeedback)276 TEST_F(FeedbackSenderTest, SameHashFeedback) {
277   uint32 hash = AddPendingFeedback();
278   std::vector<uint32> remaining_markers(1, hash);
279 
280   feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
281   EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
282   std::string hash_string = base::StringPrintf("\"suggestionId\":\"%u\"", hash);
283   EXPECT_TRUE(UploadDataContains(hash_string));
284   ClearUploadData();
285 
286   feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
287   EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
288   EXPECT_TRUE(UploadDataContains(hash_string));
289   ClearUploadData();
290 
291   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
292                                       std::vector<uint32>());
293   EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
294   EXPECT_TRUE(UploadDataContains(hash_string));
295   ClearUploadData();
296 
297   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
298                                       std::vector<uint32>());
299   EXPECT_FALSE(IsUploadingData());
300 }
301 
302 // When a session expires:
303 // 1) Pending feedback is finalized and sent to the server in the last message
304 //    batch in the session.
305 // 2) No feedback is sent until a spellcheck request happens.
306 // 3) Existing markers get new hash identifiers.
TEST_F(FeedbackSenderTest,SessionExpirationFeedback)307 TEST_F(FeedbackSenderTest, SessionExpirationFeedback) {
308   std::vector<SpellCheckResult> results(
309       1,
310       SpellCheckResult(SpellCheckResult::SPELLING,
311                        kMisspellingStart,
312                        kMisspellingLength,
313                        base::ASCIIToUTF16("Hello")));
314   feedback_->OnSpellcheckResults(kRendererProcessId,
315                                  base::UTF8ToUTF16(kText),
316                                  std::vector<SpellCheckMarker>(),
317                                  &results);
318   uint32 original_hash = results[0].hash;
319   std::vector<uint32> remaining_markers(1, original_hash);
320 
321   feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
322   EXPECT_FALSE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
323   EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
324   std::string original_hash_string =
325       base::StringPrintf("\"suggestionId\":\"%u\"", original_hash);
326   EXPECT_TRUE(UploadDataContains(original_hash_string));
327   ClearUploadData();
328 
329   ExpireSession();
330 
331   // Last message batch in the current session has only finalized messages.
332   feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
333   EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
334   EXPECT_FALSE(UploadDataContains("\"actionType\":\"PENDING\""));
335   EXPECT_TRUE(UploadDataContains(original_hash_string));
336   ClearUploadData();
337 
338   // The next session starts on the next spellchecker request. Until then,
339   // there's no more feedback sent.
340   feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
341   EXPECT_FALSE(IsUploadingData());
342 
343   // The first spellcheck request after session expiration creates different
344   // document marker hash identifiers.
345   std::vector<SpellCheckMarker> original_markers(
346       1, SpellCheckMarker(results[0].hash, results[0].location));
347   results[0] = SpellCheckResult(SpellCheckResult::SPELLING,
348                                 kMisspellingStart,
349                                 kMisspellingLength,
350                                 base::ASCIIToUTF16("Hello"));
351   feedback_->OnSpellcheckResults(
352       kRendererProcessId, base::UTF8ToUTF16(kText), original_markers, &results);
353   uint32 updated_hash = results[0].hash;
354   EXPECT_NE(updated_hash, original_hash);
355   remaining_markers[0] = updated_hash;
356 
357   // The first feedback message batch in session |i + 1| has the new document
358   // marker hash identifiers.
359   feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
360   EXPECT_FALSE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
361   EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
362   EXPECT_FALSE(UploadDataContains(original_hash_string));
363   std::string updated_hash_string =
364       base::StringPrintf("\"suggestionId\":\"%u\"", updated_hash);
365   EXPECT_TRUE(UploadDataContains(updated_hash_string));
366 }
367 
368 // First message in session has an indicator.
TEST_F(FeedbackSenderTest,FirstMessageInSessionIndicator)369 TEST_F(FeedbackSenderTest, FirstMessageInSessionIndicator) {
370   // Session 1, message 1
371   AddPendingFeedback();
372   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
373                                       std::vector<uint32>());
374   EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":true"));
375 
376   // Session 1, message 2
377   AddPendingFeedback();
378   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
379                                       std::vector<uint32>());
380   EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":false"));
381 
382   ExpireSession();
383 
384   // Session 1, message 3 (last)
385   AddPendingFeedback();
386   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
387                                       std::vector<uint32>());
388   EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":false"));
389 
390   // Session 2, message 1
391   AddPendingFeedback();
392   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
393                                       std::vector<uint32>());
394   EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":true"));
395 
396   // Session 2, message 2
397   AddPendingFeedback();
398   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
399                                       std::vector<uint32>());
400   EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":false"));
401 }
402 
403 // Flush all feedback when the spellcheck language and country change.
TEST_F(FeedbackSenderTest,OnLanguageCountryChange)404 TEST_F(FeedbackSenderTest, OnLanguageCountryChange) {
405   AddPendingFeedback();
406   feedback_->OnLanguageCountryChange("pt", "BR");
407   EXPECT_TRUE(UploadDataContains("\"language\":\"en\""));
408   AddPendingFeedback();
409   feedback_->OnLanguageCountryChange("en", "US");
410   EXPECT_TRUE(UploadDataContains("\"language\":\"pt\""));
411 }
412 
413 // The field names and types should correspond to the API.
TEST_F(FeedbackSenderTest,FeedbackAPI)414 TEST_F(FeedbackSenderTest, FeedbackAPI) {
415   AddPendingFeedback();
416   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
417                                       std::vector<uint32>());
418   std::string actual_data = GetUploadData();
419   scoped_ptr<base::DictionaryValue> actual(
420       static_cast<base::DictionaryValue*>(base::JSONReader::Read(actual_data)));
421   actual->SetString("params.key", "TestDummyKey");
422   base::ListValue* suggestions = NULL;
423   actual->GetList("params.suggestionInfo", &suggestions);
424   base::DictionaryValue* suggestion = NULL;
425   suggestions->GetDictionary(0, &suggestion);
426   suggestion->SetString("suggestionId", "42");
427   suggestion->SetString("timestamp", "9001");
428   static const std::string expected_data =
429       "{\"apiVersion\":\"v2\","
430       "\"method\":\"spelling.feedback\","
431       "\"params\":"
432       "{\"clientName\":\"Chrome\","
433       "\"originCountry\":\"USA\","
434       "\"key\":\"TestDummyKey\","
435       "\"language\":\"en\","
436       "\"suggestionInfo\":[{"
437       "\"isAutoCorrection\":false,"
438       "\"isFirstInSession\":true,"
439       "\"misspelledLength\":6,"
440       "\"misspelledStart\":0,"
441       "\"originalText\":\"Helllo world\","
442       "\"suggestionId\":\"42\","
443       "\"suggestions\":[\"Hello\"],"
444       "\"timestamp\":\"9001\","
445       "\"userActions\":[{\"actionType\":\"NO_ACTION\"}]}]}}";
446   scoped_ptr<base::Value> expected(base::JSONReader::Read(expected_data));
447   EXPECT_TRUE(expected->Equals(actual.get()))
448       << "Expected data: " << expected_data
449       << "\nActual data:   " << actual_data;
450 }
451 
452 // The default API version is "v2".
TEST_F(FeedbackSenderTest,DefaultApiVersion)453 TEST_F(FeedbackSenderTest, DefaultApiVersion) {
454   AddPendingFeedback();
455   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
456                                       std::vector<uint32>());
457   EXPECT_TRUE(UploadDataContains("\"apiVersion\":\"v2\""));
458   EXPECT_FALSE(UploadDataContains("\"apiVersion\":\"v2-internal\""));
459 }
460 
461 // The API version should not change for field-trial participants that do not
462 // append the command-line switch.
TEST_F(FeedbackSenderTest,FieldTrialAloneHasSameApiVersion)463 TEST_F(FeedbackSenderTest, FieldTrialAloneHasSameApiVersion) {
464   EnableFieldTrial();
465 
466   AddPendingFeedback();
467   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
468                                       std::vector<uint32>());
469 
470   EXPECT_TRUE(UploadDataContains("\"apiVersion\":\"v2\""));
471   EXPECT_FALSE(UploadDataContains("\"apiVersion\":\"v2-internal\""));
472 }
473 
474 // The API version should not change if the command-line switch is appended, but
475 // the user is not participating in the field-trial.
TEST_F(FeedbackSenderTest,CommandLineSwitchAloneHasSameApiVersion)476 TEST_F(FeedbackSenderTest, CommandLineSwitchAloneHasSameApiVersion) {
477   AppendCommandLineSwitch();
478 
479   AddPendingFeedback();
480   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
481                                       std::vector<uint32>());
482 
483   EXPECT_TRUE(UploadDataContains("\"apiVersion\":\"v2\""));
484   EXPECT_FALSE(UploadDataContains("\"apiVersion\":\"v2-internal\""));
485 }
486 
487 // The API version should be different for field-trial participants that also
488 // append the command-line switch.
TEST_F(FeedbackSenderTest,InternalApiVersion)489 TEST_F(FeedbackSenderTest, InternalApiVersion) {
490   AppendCommandLineSwitch();
491   EnableFieldTrial();
492 
493   AddPendingFeedback();
494   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
495                                       std::vector<uint32>());
496 
497   EXPECT_FALSE(UploadDataContains("\"apiVersion\":\"v2\""));
498   EXPECT_TRUE(UploadDataContains("\"apiVersion\":\"v2-internal\""));
499 }
500 
501 // Duplicate spellcheck results should be matched to the existing markers.
TEST_F(FeedbackSenderTest,MatchDupliateResultsWithExistingMarkers)502 TEST_F(FeedbackSenderTest, MatchDupliateResultsWithExistingMarkers) {
503   uint32 hash = AddPendingFeedback();
504   std::vector<SpellCheckResult> results(
505       1,
506       SpellCheckResult(SpellCheckResult::SPELLING,
507                        kMisspellingStart,
508                        kMisspellingLength,
509                        base::ASCIIToUTF16("Hello")));
510   std::vector<SpellCheckMarker> markers(
511       1, SpellCheckMarker(hash, results[0].location));
512   EXPECT_EQ(static_cast<uint32>(0), results[0].hash);
513   feedback_->OnSpellcheckResults(
514       kRendererProcessId, base::UTF8ToUTF16(kText), markers, &results);
515   EXPECT_EQ(hash, results[0].hash);
516 }
517 
518 // Adding a word to dictionary should trigger ADD_TO_DICT feedback for every
519 // occurrence of that word.
TEST_F(FeedbackSenderTest,MultipleAddToDictFeedback)520 TEST_F(FeedbackSenderTest, MultipleAddToDictFeedback) {
521   std::vector<SpellCheckResult> results;
522   static const int kSentenceLength = 14;
523   static const int kNumberOfSentences = 2;
524   static const base::string16 kTextWithDuplicates =
525       base::ASCIIToUTF16("Helllo world. Helllo world.");
526   for (int i = 0; i < kNumberOfSentences; ++i) {
527     results.push_back(SpellCheckResult(SpellCheckResult::SPELLING,
528                                        kMisspellingStart + i * kSentenceLength,
529                                        kMisspellingLength,
530                                        base::ASCIIToUTF16("Hello")));
531   }
532   static const int kNumberOfRenderers = 2;
533   int last_renderer_process_id = -1;
534   for (int i = 0; i < kNumberOfRenderers; ++i) {
535     feedback_->OnSpellcheckResults(kRendererProcessId + i,
536                                    kTextWithDuplicates,
537                                    std::vector<SpellCheckMarker>(),
538                                    &results);
539     last_renderer_process_id = kRendererProcessId + i;
540   }
541   std::vector<uint32> remaining_markers;
542   for (size_t i = 0; i < results.size(); ++i)
543     remaining_markers.push_back(results[i].hash);
544   feedback_->OnReceiveDocumentMarkers(last_renderer_process_id,
545                                       remaining_markers);
546   EXPECT_TRUE(UploadDataContains("PENDING", 2));
547   EXPECT_FALSE(UploadDataContains("ADD_TO_DICT"));
548 
549   feedback_->AddedToDictionary(results[0].hash);
550   feedback_->OnReceiveDocumentMarkers(last_renderer_process_id,
551                                       remaining_markers);
552   EXPECT_FALSE(UploadDataContains("PENDING"));
553   EXPECT_TRUE(UploadDataContains("ADD_TO_DICT", 2));
554 }
555 
556 // ADD_TO_DICT feedback for multiple occurrences of a word should trigger only
557 // for pending feedback.
TEST_F(FeedbackSenderTest,AddToDictOnlyPending)558 TEST_F(FeedbackSenderTest, AddToDictOnlyPending) {
559   AddPendingFeedback();
560   uint32 add_to_dict_hash = AddPendingFeedback();
561   uint32 select_hash = AddPendingFeedback();
562   feedback_->SelectedSuggestion(select_hash, 0);
563   feedback_->AddedToDictionary(add_to_dict_hash);
564   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
565                                       std::vector<uint32>());
566   EXPECT_TRUE(UploadDataContains("SELECT", 1));
567   EXPECT_TRUE(UploadDataContains("ADD_TO_DICT", 2));
568 }
569 
570 // Spellcheck results that are out-of-bounds are not added to feedback.
TEST_F(FeedbackSenderTest,IgnoreOutOfBounds)571 TEST_F(FeedbackSenderTest, IgnoreOutOfBounds) {
572   std::vector<SpellCheckResult> results;
573   results.push_back(SpellCheckResult(
574       SpellCheckResult::SPELLING, 0, 100, base::UTF8ToUTF16("Hello")));
575   results.push_back(SpellCheckResult(
576       SpellCheckResult::SPELLING, 100, 3, base::UTF8ToUTF16("world")));
577   results.push_back(SpellCheckResult(
578       SpellCheckResult::SPELLING, -1, 3, base::UTF8ToUTF16("how")));
579   results.push_back(SpellCheckResult(
580       SpellCheckResult::SPELLING, 0, 0, base::UTF8ToUTF16("are")));
581   results.push_back(SpellCheckResult(
582       SpellCheckResult::SPELLING, 2, -1, base::UTF8ToUTF16("you")));
583   feedback_->OnSpellcheckResults(kRendererProcessId,
584                                  base::UTF8ToUTF16(kText),
585                                  std::vector<SpellCheckMarker>(),
586                                  &results);
587   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
588                                       std::vector<uint32>());
589   EXPECT_FALSE(IsUploadingData());
590 }
591 
592 // FeedbackSender does not collect and upload feedback when instructed to stop.
TEST_F(FeedbackSenderTest,CanStopFeedbackCollection)593 TEST_F(FeedbackSenderTest, CanStopFeedbackCollection) {
594   feedback_->StopFeedbackCollection();
595   AddPendingFeedback();
596   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
597                                       std::vector<uint32>());
598   EXPECT_FALSE(IsUploadingData());
599 }
600 
601 // FeedbackSender resumes collecting and uploading feedback when instructed to
602 // start after stopping.
TEST_F(FeedbackSenderTest,CanResumeFeedbackCollection)603 TEST_F(FeedbackSenderTest, CanResumeFeedbackCollection) {
604   feedback_->StopFeedbackCollection();
605   feedback_->StartFeedbackCollection();
606   AddPendingFeedback();
607   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
608                                       std::vector<uint32>());
609   EXPECT_TRUE(IsUploadingData());
610 }
611 
612 // FeedbackSender does not collect data while being stopped and upload it later.
TEST_F(FeedbackSenderTest,NoFeedbackCollectionWhenStopped)613 TEST_F(FeedbackSenderTest, NoFeedbackCollectionWhenStopped) {
614   feedback_->StopFeedbackCollection();
615   AddPendingFeedback();
616   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
617                                       std::vector<uint32>());
618   feedback_->StartFeedbackCollection();
619   feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
620                                       std::vector<uint32>());
621   EXPECT_FALSE(IsUploadingData());
622 }
623 
624 }  // namespace spellcheck
625