This source file includes following definitions.
- BuildSpellCheckResult
- CountOccurences
- AppendCommandLineSwitch
- EnableFieldTrial
- AddPendingFeedback
- ExpireSession
- UploadDataContains
- UploadDataContains
- IsUploadingData
- ClearUploadData
- GetUploadData
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
#include "chrome/browser/spellchecker/feedback_sender.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/json/json_reader.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/field_trial.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/spellcheck_common.h"
#include "chrome/common/spellcheck_marker.h"
#include "chrome/common/spellcheck_result.h"
#include "chrome/test/base/testing_profile.h"
#include "components/variations/entropy_provider.h"
#include "content/public/test/test_browser_thread.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace spellcheck {
namespace {
const char kCountry[] = "USA";
const char kLanguage[] = "en";
const char kText[] = "Helllo world.";
const int kMisspellingLength = 6;
const int kMisspellingStart = 0;
const int kRendererProcessId = 0;
const int kUrlFetcherId = 0;
SpellCheckResult BuildSpellCheckResult() {
return SpellCheckResult(SpellCheckResult::SPELLING,
kMisspellingStart,
kMisspellingLength,
base::UTF8ToUTF16("Hello"));
}
int CountOccurences(const std::string& haystack, const std::string& needle) {
int number_of_occurrences = 0;
for (size_t pos = haystack.find(needle);
pos != std::string::npos;
pos = haystack.find(needle, pos + needle.length())) {
++number_of_occurrences;
}
return number_of_occurrences;
}
}
class FeedbackSenderTest : public testing::Test {
public:
FeedbackSenderTest() : ui_thread_(content::BrowserThread::UI, &loop_) {
feedback_.reset(new FeedbackSender(NULL, kLanguage, kCountry));
feedback_->StartFeedbackCollection();
}
virtual ~FeedbackSenderTest() {}
protected:
void AppendCommandLineSwitch() {
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableSpellingFeedbackFieldTrial);
feedback_.reset(new FeedbackSender(NULL, kLanguage, kCountry));
feedback_->StartFeedbackCollection();
}
void EnableFieldTrial() {
field_trial_list_.reset(
new base::FieldTrialList(new metrics::SHA1EntropyProvider("foo")));
field_trial_ = base::FieldTrialList::CreateFieldTrial(
kFeedbackFieldTrialName, kFeedbackFieldTrialEnabledGroupName);
field_trial_->group();
feedback_.reset(new FeedbackSender(NULL, kLanguage, kCountry));
feedback_->StartFeedbackCollection();
}
uint32 AddPendingFeedback() {
std::vector<SpellCheckResult> results(1, BuildSpellCheckResult());
feedback_->OnSpellcheckResults(kRendererProcessId,
base::UTF8ToUTF16(kText),
std::vector<SpellCheckMarker>(),
&results);
return results[0].hash;
}
void ExpireSession() {
feedback_->session_start_ =
base::Time::Now() -
base::TimeDelta::FromHours(chrome::spellcheck_common::kSessionHours);
}
bool UploadDataContains(const std::string& data) const {
const net::TestURLFetcher* fetcher =
fetchers_.GetFetcherByID(kUrlFetcherId);
return fetcher && CountOccurences(fetcher->upload_data(), data) > 0;
}
bool UploadDataContains(const std::string& data,
int number_of_occurrences) const {
const net::TestURLFetcher* fetcher =
fetchers_.GetFetcherByID(kUrlFetcherId);
return fetcher && CountOccurences(fetcher->upload_data(), data) ==
number_of_occurrences;
}
bool IsUploadingData() const {
return !!fetchers_.GetFetcherByID(kUrlFetcherId);
}
void ClearUploadData() {
fetchers_.RemoveFetcherFromMap(kUrlFetcherId);
}
std::string GetUploadData() const {
const net::TestURLFetcher* fetcher =
fetchers_.GetFetcherByID(kUrlFetcherId);
return fetcher ? fetcher->upload_data() : std::string();
}
scoped_ptr<spellcheck::FeedbackSender> feedback_;
private:
TestingProfile profile_;
base::MessageLoop loop_;
content::TestBrowserThread ui_thread_;
scoped_ptr<base::FieldTrialList> field_trial_list_;
scoped_refptr<base::FieldTrial> field_trial_;
net::TestURLFetcherFactory fetchers_;
};
TEST_F(FeedbackSenderTest, NoFeedback) {
EXPECT_FALSE(IsUploadingData());
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_FALSE(IsUploadingData());
}
TEST_F(FeedbackSenderTest, NoDocumentMarkersReceived) {
EXPECT_FALSE(IsUploadingData());
uint32 hash = AddPendingFeedback();
EXPECT_FALSE(IsUploadingData());
static const int kSuggestionIndex = 1;
feedback_->SelectedSuggestion(hash, kSuggestionIndex);
EXPECT_FALSE(IsUploadingData());
}
TEST_F(FeedbackSenderTest, PendingFeedback) {
uint32 hash = AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>(1, hash));
EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
}
TEST_F(FeedbackSenderTest, NoActionFeedback) {
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
}
TEST_F(FeedbackSenderTest, SelectFeedback) {
uint32 hash = AddPendingFeedback();
static const int kSuggestionIndex = 0;
feedback_->SelectedSuggestion(hash, kSuggestionIndex);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"actionType\":\"SELECT\""));
EXPECT_TRUE(UploadDataContains("\"actionTargetIndex\":" + kSuggestionIndex));
}
TEST_F(FeedbackSenderTest, AddToDictFeedback) {
uint32 hash = AddPendingFeedback();
feedback_->AddedToDictionary(hash);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"actionType\":\"ADD_TO_DICT\""));
}
TEST_F(FeedbackSenderTest, InDictionaryFeedback) {
uint32 hash = AddPendingFeedback();
feedback_->RecordInDictionary(hash);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"actionType\":\"IN_DICTIONARY\""));
}
TEST_F(FeedbackSenderTest, IgnoreFeedbackMarkerInDocument) {
uint32 hash = AddPendingFeedback();
feedback_->IgnoredSuggestions(hash);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>(1, hash));
EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
}
TEST_F(FeedbackSenderTest, IgnoreFeedbackMarkerNotInDocument) {
uint32 hash = AddPendingFeedback();
feedback_->IgnoredSuggestions(hash);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"actionType\":\"IGNORE\""));
}
TEST_F(FeedbackSenderTest, ManuallyCorrectedFeedback) {
uint32 hash = AddPendingFeedback();
static const std::string kManualCorrection = "Howdy";
feedback_->ManuallyCorrected(hash, base::ASCIIToUTF16(kManualCorrection));
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"actionType\":\"MANUALLY_CORRECTED\""));
EXPECT_TRUE(UploadDataContains("\"actionTargetValue\":\"" +
kManualCorrection + "\""));
}
TEST_F(FeedbackSenderTest, BatchFeedback) {
std::vector<SpellCheckResult> results;
results.push_back(SpellCheckResult(SpellCheckResult::SPELLING,
kMisspellingStart,
kMisspellingLength,
base::ASCIIToUTF16("Hello")));
static const int kSecondMisspellingStart = 7;
static const int kSecondMisspellingLength = 5;
results.push_back(SpellCheckResult(SpellCheckResult::SPELLING,
kSecondMisspellingStart,
kSecondMisspellingLength,
base::ASCIIToUTF16("world")));
feedback_->OnSpellcheckResults(kRendererProcessId,
base::UTF8ToUTF16(kText),
std::vector<SpellCheckMarker>(),
&results);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\"", 2));
}
TEST_F(FeedbackSenderTest, SameHashFeedback) {
uint32 hash = AddPendingFeedback();
std::vector<uint32> remaining_markers(1, hash);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
std::string hash_string = base::StringPrintf("\"suggestionId\":\"%u\"", hash);
EXPECT_TRUE(UploadDataContains(hash_string));
ClearUploadData();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
EXPECT_TRUE(UploadDataContains(hash_string));
ClearUploadData();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
EXPECT_TRUE(UploadDataContains(hash_string));
ClearUploadData();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_FALSE(IsUploadingData());
}
TEST_F(FeedbackSenderTest, SessionExpirationFeedback) {
std::vector<SpellCheckResult> results(
1,
SpellCheckResult(SpellCheckResult::SPELLING,
kMisspellingStart,
kMisspellingLength,
base::ASCIIToUTF16("Hello")));
feedback_->OnSpellcheckResults(kRendererProcessId,
base::UTF8ToUTF16(kText),
std::vector<SpellCheckMarker>(),
&results);
uint32 original_hash = results[0].hash;
std::vector<uint32> remaining_markers(1, original_hash);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
EXPECT_FALSE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
std::string original_hash_string =
base::StringPrintf("\"suggestionId\":\"%u\"", original_hash);
EXPECT_TRUE(UploadDataContains(original_hash_string));
ClearUploadData();
ExpireSession();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
EXPECT_TRUE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
EXPECT_FALSE(UploadDataContains("\"actionType\":\"PENDING\""));
EXPECT_TRUE(UploadDataContains(original_hash_string));
ClearUploadData();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
EXPECT_FALSE(IsUploadingData());
std::vector<SpellCheckMarker> original_markers(
1, SpellCheckMarker(results[0].hash, results[0].location));
results[0] = SpellCheckResult(SpellCheckResult::SPELLING,
kMisspellingStart,
kMisspellingLength,
base::ASCIIToUTF16("Hello"));
feedback_->OnSpellcheckResults(
kRendererProcessId, base::UTF8ToUTF16(kText), original_markers, &results);
uint32 updated_hash = results[0].hash;
EXPECT_NE(updated_hash, original_hash);
remaining_markers[0] = updated_hash;
feedback_->OnReceiveDocumentMarkers(kRendererProcessId, remaining_markers);
EXPECT_FALSE(UploadDataContains("\"actionType\":\"NO_ACTION\""));
EXPECT_TRUE(UploadDataContains("\"actionType\":\"PENDING\""));
EXPECT_FALSE(UploadDataContains(original_hash_string));
std::string updated_hash_string =
base::StringPrintf("\"suggestionId\":\"%u\"", updated_hash);
EXPECT_TRUE(UploadDataContains(updated_hash_string));
}
TEST_F(FeedbackSenderTest, FirstMessageInSessionIndicator) {
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":true"));
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":false"));
ExpireSession();
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":false"));
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":true"));
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"isFirstInSession\":false"));
}
TEST_F(FeedbackSenderTest, OnLanguageCountryChange) {
AddPendingFeedback();
feedback_->OnLanguageCountryChange("pt", "BR");
EXPECT_TRUE(UploadDataContains("\"language\":\"en\""));
AddPendingFeedback();
feedback_->OnLanguageCountryChange("en", "US");
EXPECT_TRUE(UploadDataContains("\"language\":\"pt\""));
}
TEST_F(FeedbackSenderTest, FeedbackAPI) {
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
std::string actual_data = GetUploadData();
scoped_ptr<base::DictionaryValue> actual(
static_cast<base::DictionaryValue*>(base::JSONReader::Read(actual_data)));
actual->SetString("params.key", "TestDummyKey");
base::ListValue* suggestions = NULL;
actual->GetList("params.suggestionInfo", &suggestions);
base::DictionaryValue* suggestion = NULL;
suggestions->GetDictionary(0, &suggestion);
suggestion->SetString("suggestionId", "42");
suggestion->SetString("timestamp", "9001");
static const std::string expected_data =
"{\"apiVersion\":\"v2\","
"\"method\":\"spelling.feedback\","
"\"params\":"
"{\"clientName\":\"Chrome\","
"\"originCountry\":\"USA\","
"\"key\":\"TestDummyKey\","
"\"language\":\"en\","
"\"suggestionInfo\":[{"
"\"isAutoCorrection\":false,"
"\"isFirstInSession\":true,"
"\"misspelledLength\":6,"
"\"misspelledStart\":0,"
"\"originalText\":\"Helllo world\","
"\"suggestionId\":\"42\","
"\"suggestions\":[\"Hello\"],"
"\"timestamp\":\"9001\","
"\"userActions\":[{\"actionType\":\"NO_ACTION\"}]}]}}";
scoped_ptr<base::Value> expected(base::JSONReader::Read(expected_data));
EXPECT_TRUE(expected->Equals(actual.get()))
<< "Expected data: " << expected_data
<< "\nActual data: " << actual_data;
}
TEST_F(FeedbackSenderTest, DefaultApiVersion) {
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"apiVersion\":\"v2\""));
EXPECT_FALSE(UploadDataContains("\"apiVersion\":\"v2-internal\""));
}
TEST_F(FeedbackSenderTest, FieldTrialAloneHasSameApiVersion) {
EnableFieldTrial();
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"apiVersion\":\"v2\""));
EXPECT_FALSE(UploadDataContains("\"apiVersion\":\"v2-internal\""));
}
TEST_F(FeedbackSenderTest, CommandLineSwitchAloneHasSameApiVersion) {
AppendCommandLineSwitch();
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("\"apiVersion\":\"v2\""));
EXPECT_FALSE(UploadDataContains("\"apiVersion\":\"v2-internal\""));
}
TEST_F(FeedbackSenderTest, InternalApiVersion) {
AppendCommandLineSwitch();
EnableFieldTrial();
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_FALSE(UploadDataContains("\"apiVersion\":\"v2\""));
EXPECT_TRUE(UploadDataContains("\"apiVersion\":\"v2-internal\""));
}
TEST_F(FeedbackSenderTest, MatchDupliateResultsWithExistingMarkers) {
uint32 hash = AddPendingFeedback();
std::vector<SpellCheckResult> results(
1,
SpellCheckResult(SpellCheckResult::SPELLING,
kMisspellingStart,
kMisspellingLength,
base::ASCIIToUTF16("Hello")));
std::vector<SpellCheckMarker> markers(
1, SpellCheckMarker(hash, results[0].location));
EXPECT_EQ(static_cast<uint32>(0), results[0].hash);
feedback_->OnSpellcheckResults(
kRendererProcessId, base::UTF8ToUTF16(kText), markers, &results);
EXPECT_EQ(hash, results[0].hash);
}
TEST_F(FeedbackSenderTest, MultipleAddToDictFeedback) {
std::vector<SpellCheckResult> results;
static const int kSentenceLength = 14;
static const int kNumberOfSentences = 2;
static const base::string16 kTextWithDuplicates =
base::ASCIIToUTF16("Helllo world. Helllo world.");
for (int i = 0; i < kNumberOfSentences; ++i) {
results.push_back(SpellCheckResult(SpellCheckResult::SPELLING,
kMisspellingStart + i * kSentenceLength,
kMisspellingLength,
base::ASCIIToUTF16("Hello")));
}
static const int kNumberOfRenderers = 2;
int last_renderer_process_id = -1;
for (int i = 0; i < kNumberOfRenderers; ++i) {
feedback_->OnSpellcheckResults(kRendererProcessId + i,
kTextWithDuplicates,
std::vector<SpellCheckMarker>(),
&results);
last_renderer_process_id = kRendererProcessId + i;
}
std::vector<uint32> remaining_markers;
for (size_t i = 0; i < results.size(); ++i)
remaining_markers.push_back(results[i].hash);
feedback_->OnReceiveDocumentMarkers(last_renderer_process_id,
remaining_markers);
EXPECT_TRUE(UploadDataContains("PENDING", 2));
EXPECT_FALSE(UploadDataContains("ADD_TO_DICT"));
feedback_->AddedToDictionary(results[0].hash);
feedback_->OnReceiveDocumentMarkers(last_renderer_process_id,
remaining_markers);
EXPECT_FALSE(UploadDataContains("PENDING"));
EXPECT_TRUE(UploadDataContains("ADD_TO_DICT", 2));
}
TEST_F(FeedbackSenderTest, AddToDictOnlyPending) {
AddPendingFeedback();
uint32 add_to_dict_hash = AddPendingFeedback();
uint32 select_hash = AddPendingFeedback();
feedback_->SelectedSuggestion(select_hash, 0);
feedback_->AddedToDictionary(add_to_dict_hash);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(UploadDataContains("SELECT", 1));
EXPECT_TRUE(UploadDataContains("ADD_TO_DICT", 2));
}
TEST_F(FeedbackSenderTest, IgnoreOutOfBounds) {
std::vector<SpellCheckResult> results;
results.push_back(SpellCheckResult(
SpellCheckResult::SPELLING, 0, 100, base::UTF8ToUTF16("Hello")));
results.push_back(SpellCheckResult(
SpellCheckResult::SPELLING, 100, 3, base::UTF8ToUTF16("world")));
results.push_back(SpellCheckResult(
SpellCheckResult::SPELLING, -1, 3, base::UTF8ToUTF16("how")));
results.push_back(SpellCheckResult(
SpellCheckResult::SPELLING, 0, 0, base::UTF8ToUTF16("are")));
results.push_back(SpellCheckResult(
SpellCheckResult::SPELLING, 2, -1, base::UTF8ToUTF16("you")));
feedback_->OnSpellcheckResults(kRendererProcessId,
base::UTF8ToUTF16(kText),
std::vector<SpellCheckMarker>(),
&results);
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_FALSE(IsUploadingData());
}
TEST_F(FeedbackSenderTest, CanStopFeedbackCollection) {
feedback_->StopFeedbackCollection();
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_FALSE(IsUploadingData());
}
TEST_F(FeedbackSenderTest, CanResumeFeedbackCollection) {
feedback_->StopFeedbackCollection();
feedback_->StartFeedbackCollection();
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_TRUE(IsUploadingData());
}
TEST_F(FeedbackSenderTest, NoFeedbackCollectionWhenStopped) {
feedback_->StopFeedbackCollection();
AddPendingFeedback();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
feedback_->StartFeedbackCollection();
feedback_->OnReceiveDocumentMarkers(kRendererProcessId,
std::vector<uint32>());
EXPECT_FALSE(IsUploadingData());
}
}