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.
5 // Unit tests for |FeedbackSender| object.
7 #include "chrome/browser/spellchecker/feedback_sender.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"
27 namespace spellcheck
{
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;
39 // Builds a simple spellcheck result.
40 SpellCheckResult
BuildSpellCheckResult() {
41 return SpellCheckResult(SpellCheckResult::SPELLING
,
44 base::UTF8ToUTF16("Hello"));
47 // Returns the number of times that |needle| appears in |haystack| without
48 // overlaps. For example, CountOccurences("bananana", "nana") returns 1.
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
;
56 return number_of_occurrences
;
61 // A test fixture to help keep tests simple.
62 class FeedbackSenderTest
: public testing::Test
{
64 FeedbackSenderTest() : ui_thread_(content::BrowserThread::UI
, &loop_
) {
65 feedback_
.reset(new FeedbackSender(NULL
, kLanguage
, kCountry
));
66 feedback_
->StartFeedbackCollection();
69 ~FeedbackSenderTest() override
{}
72 // Appends the "--enable-spelling-service-feedback" switch to the
74 void AppendCommandLineSwitch() {
75 // The command-line switch is temporary.
76 // TODO(rouslan): Remove the command-line switch. http://crbug.com/247726
77 base::CommandLine::ForCurrentProcess()->AppendSwitch(
78 switches::kEnableSpellingFeedbackFieldTrial
);
79 feedback_
.reset(new FeedbackSender(NULL
, kLanguage
, kCountry
));
80 feedback_
->StartFeedbackCollection();
83 // Enables the "SpellingServiceFeedback.Enabled" field trial.
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();
96 uint32
AddPendingFeedback() {
97 std::vector
<SpellCheckResult
> results(1, BuildSpellCheckResult());
98 feedback_
->OnSpellcheckResults(kRendererProcessId
,
99 base::UTF8ToUTF16(kText
),
100 std::vector
<SpellCheckMarker
>(),
102 return results
[0].hash
;
105 void ExpireSession() {
106 feedback_
->session_start_
=
108 base::TimeDelta::FromHours(chrome::spellcheck_common::kSessionHours
);
111 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;
117 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
;
125 // Returns true if the feedback sender would be uploading data now. The test
126 // does not open network connections.
127 bool IsUploadingData() const {
128 return !!fetchers_
.GetFetcherByID(kUrlFetcherId
);
131 void ClearUploadData() {
132 fetchers_
.RemoveFetcherFromMap(kUrlFetcherId
);
135 std::string
GetUploadData() const {
136 const net::TestURLFetcher
* fetcher
=
137 fetchers_
.GetFetcherByID(kUrlFetcherId
);
138 return fetcher
? fetcher
->upload_data() : std::string();
141 scoped_ptr
<spellcheck::FeedbackSender
> feedback_
;
144 base::MessageLoop loop_
;
145 TestingProfile profile_
;
146 content::TestBrowserThread ui_thread_
;
147 scoped_ptr
<base::FieldTrialList
> field_trial_list_
;
148 scoped_refptr
<base::FieldTrial
> field_trial_
;
149 net::TestURLFetcherFactory fetchers_
;
152 // Do not send data if there's no feedback.
153 TEST_F(FeedbackSenderTest
, NoFeedback
) {
154 EXPECT_FALSE(IsUploadingData());
155 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
,
156 std::vector
<uint32
>());
157 EXPECT_FALSE(IsUploadingData());
160 // Do not send data if not aware of which markers are still in the document.
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());
170 // Send PENDING feedback message if the marker is still in the document, and the
171 // user has not performed any action on it.
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\""));
179 // Send NO_ACTION feedback message if the marker has been removed from the
181 TEST_F(FeedbackSenderTest
, NoActionFeedback
) {
182 AddPendingFeedback();
183 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
,
184 std::vector
<uint32
>());
185 EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
188 // Send SELECT feedback message if the user has selected a spelling suggestion.
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
));
199 // Send ADD_TO_DICT feedback message if the user has added the misspelled word
200 // to the custom dictionary.
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\""));
209 // Send IN_DICTIONARY feedback message if the user has the misspelled word in
210 // the custom dictionary.
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\""));
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.
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\""));
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.
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\""));
239 // Send MANUALLY_CORRECTED feedback message if the user manually corrected the
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
+ "\""));
252 // Send feedback messages in batch.
253 TEST_F(FeedbackSenderTest
, BatchFeedback
) {
254 std::vector
<SpellCheckResult
> results
;
255 results
.push_back(SpellCheckResult(SpellCheckResult::SPELLING
,
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
>(),
269 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
,
270 std::vector
<uint32
>());
271 EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\"", 2));
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.
276 TEST_F(FeedbackSenderTest
, SameHashFeedback
) {
277 uint32 hash
= AddPendingFeedback();
278 std::vector
<uint32
> remaining_markers(1, hash
);
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
));
286 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
, remaining_markers
);
287 EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
288 EXPECT_TRUE(UploadDataContains(hash_string
));
291 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
,
292 std::vector
<uint32
>());
293 EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
294 EXPECT_TRUE(UploadDataContains(hash_string
));
297 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
,
298 std::vector
<uint32
>());
299 EXPECT_FALSE(IsUploadingData());
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.
307 TEST_F(FeedbackSenderTest
, SessionExpirationFeedback
) {
308 std::vector
<SpellCheckResult
> results(
310 SpellCheckResult(SpellCheckResult::SPELLING
,
313 base::ASCIIToUTF16("Hello")));
314 feedback_
->OnSpellcheckResults(kRendererProcessId
,
315 base::UTF8ToUTF16(kText
),
316 std::vector
<SpellCheckMarker
>(),
318 uint32 original_hash
= results
[0].hash
;
319 std::vector
<uint32
> remaining_markers(1, original_hash
);
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
));
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
));
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());
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
,
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
;
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
));
368 // First message in session has an indicator.
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"));
376 // Session 1, message 2
377 AddPendingFeedback();
378 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
,
379 std::vector
<uint32
>());
380 EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":false"));
384 // Session 1, message 3 (last)
385 AddPendingFeedback();
386 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
,
387 std::vector
<uint32
>());
388 EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":false"));
390 // Session 2, message 1
391 AddPendingFeedback();
392 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
,
393 std::vector
<uint32
>());
394 EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":true"));
396 // Session 2, message 2
397 AddPendingFeedback();
398 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
,
399 std::vector
<uint32
>());
400 EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":false"));
403 // Flush all feedback when the spellcheck language and country change.
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\""));
413 // The field names and types should correspond to the API.
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(static_cast<base::DictionaryValue
*>(
420 base::JSONReader::DeprecatedRead(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\","
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
;
452 // The default API version is "v2".
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\""));
461 // The API version should not change for field-trial participants that do not
462 // append the command-line switch.
463 TEST_F(FeedbackSenderTest
, FieldTrialAloneHasSameApiVersion
) {
466 AddPendingFeedback();
467 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
,
468 std::vector
<uint32
>());
470 EXPECT_TRUE(UploadDataContains("\"apiVersion\":\"v2\""));
471 EXPECT_FALSE(UploadDataContains("\"apiVersion\":\"v2-internal\""));
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.
476 TEST_F(FeedbackSenderTest
, CommandLineSwitchAloneHasSameApiVersion
) {
477 AppendCommandLineSwitch();
479 AddPendingFeedback();
480 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
,
481 std::vector
<uint32
>());
483 EXPECT_TRUE(UploadDataContains("\"apiVersion\":\"v2\""));
484 EXPECT_FALSE(UploadDataContains("\"apiVersion\":\"v2-internal\""));
487 // The API version should be different for field-trial participants that also
488 // append the command-line switch.
489 TEST_F(FeedbackSenderTest
, InternalApiVersion
) {
490 AppendCommandLineSwitch();
493 AddPendingFeedback();
494 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
,
495 std::vector
<uint32
>());
497 EXPECT_FALSE(UploadDataContains("\"apiVersion\":\"v2\""));
498 EXPECT_TRUE(UploadDataContains("\"apiVersion\":\"v2-internal\""));
501 // Duplicate spellcheck results should be matched to the existing markers.
502 TEST_F(FeedbackSenderTest
, MatchDupliateResultsWithExistingMarkers
) {
503 uint32 hash
= AddPendingFeedback();
504 std::vector
<SpellCheckResult
> results(
506 SpellCheckResult(SpellCheckResult::SPELLING
,
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
);
518 // Adding a word to dictionary should trigger ADD_TO_DICT feedback for every
519 // occurrence of that word.
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
,
530 base::ASCIIToUTF16("Hello")));
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
,
537 std::vector
<SpellCheckMarker
>(),
539 last_renderer_process_id
= kRendererProcessId
+ i
;
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
,
546 EXPECT_TRUE(UploadDataContains("PENDING", 2));
547 EXPECT_FALSE(UploadDataContains("ADD_TO_DICT"));
549 feedback_
->AddedToDictionary(results
[0].hash
);
550 feedback_
->OnReceiveDocumentMarkers(last_renderer_process_id
,
552 EXPECT_FALSE(UploadDataContains("PENDING"));
553 EXPECT_TRUE(UploadDataContains("ADD_TO_DICT", 2));
556 // ADD_TO_DICT feedback for multiple occurrences of a word should trigger only
557 // for pending feedback.
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));
570 // Spellcheck results that are out-of-bounds are not added to feedback.
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
>(),
587 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
,
588 std::vector
<uint32
>());
589 EXPECT_FALSE(IsUploadingData());
592 // FeedbackSender does not collect and upload feedback when instructed to stop.
593 TEST_F(FeedbackSenderTest
, CanStopFeedbackCollection
) {
594 feedback_
->StopFeedbackCollection();
595 AddPendingFeedback();
596 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
,
597 std::vector
<uint32
>());
598 EXPECT_FALSE(IsUploadingData());
601 // FeedbackSender resumes collecting and uploading feedback when instructed to
602 // start after stopping.
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());
612 // FeedbackSender does not collect data while being stopped and upload it later.
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());
624 // The feedback context is trimmed to 2 words on the left and 2 words on the
625 // right side of the misspelling.
626 TEST_F(FeedbackSenderTest
, TrimFeedback
) {
627 std::vector
<SpellCheckResult
> results(
628 1, SpellCheckResult(SpellCheckResult::SPELLING
, 13, 3,
629 base::UTF8ToUTF16("the")));
630 feedback_
->OnSpellcheckResults(
632 base::UTF8ToUTF16("Far and away teh best prize that life has to offer is "
633 "the chance to work hard at work worth doing."),
634 std::vector
<SpellCheckMarker
>(), &results
);
635 feedback_
->OnReceiveDocumentMarkers(kRendererProcessId
,
636 std::vector
<uint32
>());
638 UploadDataContains(",\"originalText\":\"and away teh best prize\","));
639 EXPECT_TRUE(UploadDataContains(",\"misspelledStart\":9,"));
642 } // namespace spellcheck