1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
9 #include "base/json/json_reader.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/spellchecker/spelling_service_client.h"
16 #include "chrome/common/pref_names.h"
17 #include "chrome/common/spellcheck_result.h"
18 #include "chrome/test/base/testing_profile.h"
19 #include "content/public/test/test_browser_thread_bundle.h"
20 #include "net/base/load_flags.h"
21 #include "net/url_request/test_url_fetcher_factory.h"
22 #include "testing/gtest/include/gtest/gtest.h"
26 // A mock URL fetcher used in the TestingSpellingServiceClient class. This class
27 // verifies JSON-RPC requests when the SpellingServiceClient class sends them to
28 // the Spelling service. This class also verifies the SpellingServiceClient
29 // class does not either send cookies to the Spelling service or accept cookies
31 class TestSpellingURLFetcher
: public net::TestURLFetcher
{
33 TestSpellingURLFetcher(int id
,
35 net::URLFetcherDelegate
* d
,
37 const std::string
& text
,
38 const std::string
& language
,
40 const std::string
& response
)
41 : net::TestURLFetcher(0, url
, d
),
42 version_(base::StringPrintf("v%d", version
)),
43 language_(language
.empty() ? std::string("en") : language
),
45 set_response_code(status
);
46 SetResponseString(response
);
48 virtual ~TestSpellingURLFetcher() {
51 virtual void SetUploadData(const std::string
& upload_content_type
,
52 const std::string
& upload_content
) OVERRIDE
{
53 // Verify the given content type is JSON. (The Spelling service returns an
54 // internal server error when this content type is not JSON.)
55 EXPECT_EQ("application/json", upload_content_type
);
57 // Parse the JSON to be sent to the service, and verify its parameters.
58 scoped_ptr
<base::DictionaryValue
> value(static_cast<base::DictionaryValue
*>(
59 base::JSONReader::Read(upload_content
,
60 base::JSON_ALLOW_TRAILING_COMMAS
)));
61 ASSERT_TRUE(!!value
.get());
63 EXPECT_TRUE(value
->GetString("method", &method
));
64 EXPECT_EQ("spelling.check", method
);
66 EXPECT_TRUE(value
->GetString("apiVersion", &version
));
67 EXPECT_EQ(version_
, version
);
69 EXPECT_TRUE(value
->GetString("params.text", &text
));
70 EXPECT_EQ(text_
, text
);
72 EXPECT_TRUE(value
->GetString("params.language", &language
));
73 EXPECT_EQ(language_
, language
);
74 ASSERT_TRUE(GetExpectedCountry(language
, &country_
));
76 EXPECT_TRUE(value
->GetString("params.originCountry", &country
));
77 EXPECT_EQ(country_
, country
);
79 net::TestURLFetcher::SetUploadData(upload_content_type
, upload_content
);
82 virtual void Start() OVERRIDE
{
83 // Verify that this client does not either send cookies to the Spelling
84 // service or accept cookies from it.
85 EXPECT_EQ(net::LOAD_DO_NOT_SEND_COOKIES
| net::LOAD_DO_NOT_SAVE_COOKIES
,
90 bool GetExpectedCountry(const std::string
& language
, std::string
* country
) {
98 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(kCountries
); ++i
) {
99 if (!language
.compare(kCountries
[i
].language
)) {
100 country
->assign(kCountries
[i
].country
);
107 std::string version_
;
108 std::string language_
;
109 std::string country_
;
113 // A class derived from the SpellingServiceClient class used by the
114 // SpellingServiceClientTest class. This class overrides CreateURLFetcher so
115 // this test can use TestSpellingURLFetcher. This class also lets tests access
116 // the ParseResponse method.
117 class TestingSpellingServiceClient
: public SpellingServiceClient
{
119 TestingSpellingServiceClient()
125 virtual ~TestingSpellingServiceClient() {
128 void SetHTTPRequest(int type
,
129 const std::string
& text
,
130 const std::string
& language
) {
131 request_type_
= type
;
132 request_text_
= text
;
133 request_language_
= language
;
136 void SetHTTPResponse(int status
, const char* data
) {
137 response_status_
= status
;
138 response_data_
.assign(data
);
141 void SetExpectedTextCheckResult(bool success
, const char* text
) {
143 corrected_text_
.assign(base::UTF8ToUTF16(text
));
146 void CallOnURLFetchComplete() {
147 ASSERT_TRUE(!!fetcher_
);
148 fetcher_
->delegate()->OnURLFetchComplete(fetcher_
);
152 void VerifyResponse(bool success
,
153 const base::string16
& request_text
,
154 const std::vector
<SpellCheckResult
>& results
) {
155 EXPECT_EQ(success_
, success
);
156 base::string16
text(base::UTF8ToUTF16(request_text_
));
157 EXPECT_EQ(text
, request_text
);
158 for (std::vector
<SpellCheckResult
>::const_iterator it
= results
.begin();
159 it
!= results
.end(); ++it
) {
160 text
.replace(it
->location
, it
->length
, it
->replacement
);
162 EXPECT_EQ(corrected_text_
, text
);
165 bool ParseResponseSuccess(const std::string
& data
) {
166 std::vector
<SpellCheckResult
> results
;
167 return ParseResponse(data
, &results
);
171 virtual net::URLFetcher
* CreateURLFetcher(const GURL
& url
) OVERRIDE
{
172 EXPECT_EQ("https://www.googleapis.com/rpc", url
.spec());
173 fetcher_
= new TestSpellingURLFetcher(0, url
, this,
174 request_type_
, request_text_
,
176 response_status_
, response_data_
);
181 std::string request_text_
;
182 std::string request_language_
;
183 int response_status_
;
184 std::string response_data_
;
186 base::string16 corrected_text_
;
187 TestSpellingURLFetcher
* fetcher_
; // weak
190 // A test class used for testing the SpellingServiceClient class. This class
191 // implements a callback function used by the SpellingServiceClient class to
192 // monitor the class calls the callback with expected results.
193 class SpellingServiceClientTest
: public testing::Test
{
195 void OnTextCheckComplete(int tag
,
197 const base::string16
& text
,
198 const std::vector
<SpellCheckResult
>& results
) {
199 client_
.VerifyResponse(success
, text
, results
);
203 content::TestBrowserThreadBundle thread_bundle_
;
204 TestingSpellingServiceClient client_
;
205 TestingProfile profile_
;
210 // Verifies that SpellingServiceClient::RequestTextCheck() creates a JSON
211 // request sent to the Spelling service as we expect. This test also verifies
212 // that it parses a JSON response from the service and calls the callback
213 // function. To avoid sending JSON-RPC requests to the service, this test uses a
214 // custom TestURLFecher class (TestSpellingURLFetcher) which calls
215 // SpellingServiceClient::OnURLFetchComplete() with the parameters set by this
216 // test. This test also uses a custom callback function that replaces all
217 // misspelled words with ones suggested by the service so this test can compare
218 // the corrected text with the expected results. (If there are not any
219 // misspelled words, |corrected_text| should be equal to |request_text|.)
220 TEST_F(SpellingServiceClientTest
, RequestTextCheck
) {
221 static const struct {
222 const char* request_text
;
223 SpellingServiceClient::ServiceType request_type
;
225 const char* response_data
;
227 const char* corrected_text
;
228 const char* language
;
232 SpellingServiceClient::SUGGEST
,
240 SpellingServiceClient::SUGGEST
,
248 SpellingServiceClient::SUGGEST
,
252 " \"spellingCheckResponse\": {\n"
253 " \"misspellings\": [{\n"
254 " \"charStart\": 0,\n"
255 " \"charLength\": 9,\n"
256 " \"suggestions\": [{ \"suggestion\": \"chromebook\" }],\n"
257 " \"canAutoCorrect\": false\n"
267 SpellingServiceClient::SPELLCHECK
,
274 "I have been to USA.",
275 SpellingServiceClient::SPELLCHECK
,
279 "I have been to USA.",
282 "I have bean to USA.",
283 SpellingServiceClient::SPELLCHECK
,
287 " \"spellingCheckResponse\": {\n"
288 " \"misspellings\": [{\n"
289 " \"charStart\": 7,\n"
290 " \"charLength\": 4,\n"
291 " \"suggestions\": [{ \"suggestion\": \"been\" }],\n"
292 " \"canAutoCorrect\": false\n"
298 "I have been to USA.",
303 PrefService
* pref
= profile_
.GetPrefs();
304 pref
->SetBoolean(prefs::kEnableContinuousSpellcheck
, true);
305 pref
->SetBoolean(prefs::kSpellCheckUseSpellingService
, true);
307 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(kTests
); ++i
) {
308 client_
.SetHTTPRequest(kTests
[i
].request_type
, kTests
[i
].request_text
,
310 client_
.SetHTTPResponse(kTests
[i
].response_status
, kTests
[i
].response_data
);
311 client_
.SetExpectedTextCheckResult(kTests
[i
].success
,
312 kTests
[i
].corrected_text
);
313 pref
->SetString(prefs::kSpellCheckDictionary
, kTests
[i
].language
);
314 client_
.RequestTextCheck(
316 kTests
[i
].request_type
,
317 base::ASCIIToUTF16(kTests
[i
].request_text
),
318 base::Bind(&SpellingServiceClientTest::OnTextCheckComplete
,
319 base::Unretained(this), 0));
320 client_
.CallOnURLFetchComplete();
324 // Verify that SpellingServiceClient::IsAvailable() returns true only when it
325 // can send suggest requests or spellcheck requests.
326 TEST_F(SpellingServiceClientTest
, AvailableServices
) {
327 const SpellingServiceClient::ServiceType kSuggest
=
328 SpellingServiceClient::SUGGEST
;
329 const SpellingServiceClient::ServiceType kSpellcheck
=
330 SpellingServiceClient::SPELLCHECK
;
332 // When a user disables spellchecking or prevent using the Spelling service,
333 // this function should return false both for suggestions and for spellcheck.
334 PrefService
* pref
= profile_
.GetPrefs();
335 pref
->SetBoolean(prefs::kEnableContinuousSpellcheck
, false);
336 pref
->SetBoolean(prefs::kSpellCheckUseSpellingService
, false);
337 EXPECT_FALSE(client_
.IsAvailable(&profile_
, kSuggest
));
338 EXPECT_FALSE(client_
.IsAvailable(&profile_
, kSpellcheck
));
340 pref
->SetBoolean(prefs::kEnableContinuousSpellcheck
, true);
341 pref
->SetBoolean(prefs::kSpellCheckUseSpellingService
, true);
343 // For locales supported by the SpellCheck service, this function returns
344 // false for suggestions and true for spellcheck. (The comment in
345 // SpellingServiceClient::IsAvailable() describes why this function returns
346 // false for suggestions.) If there is no language set, then we
347 // do not allow any remote.
348 pref
->SetString(prefs::kSpellCheckDictionary
, std::string());
349 EXPECT_FALSE(client_
.IsAvailable(&profile_
, kSuggest
));
350 EXPECT_FALSE(client_
.IsAvailable(&profile_
, kSpellcheck
));
352 static const char* kSupported
[] = {
353 #if !defined(OS_MACOSX)
354 "en-AU", "en-CA", "en-GB", "en-US",
357 // If spellcheck is allowed, then suggest is not since spellcheck is a
358 // superset of suggest.
359 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(kSupported
); ++i
) {
360 pref
->SetString(prefs::kSpellCheckDictionary
, kSupported
[i
]);
361 EXPECT_FALSE(client_
.IsAvailable(&profile_
, kSuggest
));
362 EXPECT_TRUE(client_
.IsAvailable(&profile_
, kSpellcheck
));
365 // This function returns true for suggestions for all and false for
366 // spellcheck for unsupported locales.
367 static const char* kUnsupported
[] = {
368 #if !defined(OS_MACOSX)
369 "af-ZA", "bg-BG", "ca-ES", "cs-CZ", "da-DK", "de-DE", "el-GR", "es-ES",
370 "et-EE", "fo-FO", "fr-FR", "he-IL", "hi-IN", "hr-HR", "hu-HU", "id-ID",
371 "it-IT", "lt-LT", "lv-LV", "nb-NO", "nl-NL", "pl-PL", "pt-BR", "pt-PT",
372 "ro-RO", "ru-RU", "sk-SK", "sl-SI", "sh", "sr", "sv-SE", "tr-TR",
376 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(kUnsupported
); ++i
) {
377 pref
->SetString(prefs::kSpellCheckDictionary
, kUnsupported
[i
]);
378 EXPECT_TRUE(client_
.IsAvailable(&profile_
, kSuggest
));
379 EXPECT_FALSE(client_
.IsAvailable(&profile_
, kSpellcheck
));
383 // Verify that an error in JSON response from spelling service will result in
384 // ParseResponse returning false.
385 TEST_F(SpellingServiceClientTest
, ResponseErrorTest
) {
386 EXPECT_TRUE(client_
.ParseResponseSuccess("{\"result\": {}}"));
387 EXPECT_FALSE(client_
.ParseResponseSuccess("{\"error\": {}}"));