Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(168)

Side by Side Diff: chrome/browser/spellchecker/feedback_sender_unittest.cc

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

Powered by Google App Engine
This is Rietveld 408576698