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

Side by Side Diff: chrome/browser/renderer_host/translation_service_unittest.cc

Issue 552216: This CL makes the TranslationService class send the text to be translated to ... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 10 years, 10 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 | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2010 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 #include "base/json/json_writer.h"
6 #include "base/stl_util-inl.h"
7 #include "base/string_tokenizer.h"
8 #include "base/string_util.h"
9 #include "chrome/browser/net/test_url_fetcher_factory.h"
10 #include "chrome/browser/renderer_host/translation_service.h"
11 #include "chrome/common/render_messages.h"
12 #include "ipc/ipc_message.h"
13 #include "net/base/escape.h"
14 #include "net/url_request/url_request_status.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 typedef std::vector<string16> TextChunks;
18 typedef std::vector<TextChunks> TextChunksList;
19
20 class TestMessageSender : public IPC::Message::Sender {
21 public:
22 virtual ~TestMessageSender() {
23 ClearMessages();
24 }
25
26 virtual bool Send(IPC::Message* msg) {
27 messages_.push_back(msg);
28 return true;
29 }
30
31 size_t message_count() const { return messages_.size(); }
32
33 void GetMessageParameters(size_t message_index, int* routing_id,
34 int* work_id, int* error_id,
35 std::vector<string16>* text_chunks) {
36 ASSERT_LT(message_index, messages_.size());
37 *routing_id = messages_.at(message_index)->routing_id();
38 ViewMsg_TranslateTextReponse::Read(messages_.at(message_index),
39 work_id, error_id, text_chunks);
40 }
41
42 void ClearMessages() {
43 STLDeleteElements(&messages_);
44 messages_.clear();
45 }
46
47 std::vector<IPC::Message*> messages_;
48 MessageLoop message_loop_;
49 };
50
51 class TestTranslationService : public TranslationService {
52 public:
53 explicit TestTranslationService(IPC::Message::Sender* message_sender)
54 : TranslationService(message_sender) {}
55
56 virtual int GetSendRequestDelay() const {
57 return 0;
58 }
59 };
60
61 class TranslationServiceTest : public testing::Test {
62 public:
63 TranslationServiceTest() : translation_service_(&message_sender_) {}
64
65 virtual void SetUp() {
66 URLFetcher::set_factory(&url_fetcher_factory_);
67 }
68
69 virtual void TearDown() {
70 URLFetcher::set_factory(NULL);
71 }
72
73 protected:
74 TestURLFetcherFactory url_fetcher_factory_;
75 TestMessageSender message_sender_;
76 TestTranslationService translation_service_;
77 };
78
79 static void SimulateErrorResponse(TestURLFetcher* url_fetcher,
80 int response_code) {
81 url_fetcher->delegate()->OnURLFetchComplete(url_fetcher,
82 url_fetcher->original_url(),
83 URLRequestStatus(),
84 response_code,
85 ResponseCookies(),
86 std::string());
87 }
88
89 // Merges the strings in |text_chunks| into the string that the translation
90 // server received.
91 static string16 BuildResponseString(const TextChunks& text_chunks) {
92 string16 text;
93 for (size_t i = 0; i < text_chunks.size(); ++i) {
94 text.append(ASCIIToUTF16("<a _CR_TR_ id='"));
95 text.append(IntToString16(i));
96 text.append(ASCIIToUTF16("'>"));
97 text.append(text_chunks.at(i));
98 text.append(ASCIIToUTF16("</a>"));
99 }
100 return text;
101 }
102
103 static void SimulateSuccessfulResponse(TestURLFetcher* url_fetcher,
104 const TextChunksList& text_chunks_list) {
105 scoped_ptr<ListValue> list(new ListValue());
106 for (TextChunksList::const_iterator iter = text_chunks_list.begin();
107 iter != text_chunks_list.end(); ++iter) {
108 list->Append(Value::CreateStringValueFromUTF16(BuildResponseString(*iter)));
109 }
110
111 std::string response;
112 base::JSONWriter::Write(list.get(), false, &response);
113 url_fetcher->delegate()->OnURLFetchComplete(url_fetcher,
114 url_fetcher->original_url(),
115 URLRequestStatus(),
116 200,
117 ResponseCookies(),
118 response);
119 }
120
121 static void SimulateSimpleSuccessfulResponse(TestURLFetcher* url_fetcher,
122 const char* const* c_text_chunks,
123 size_t text_chunks_length) {
124 TextChunks text_chunks;
125 for (size_t i = 0; i < text_chunks_length; i++)
126 text_chunks.push_back(ASCIIToUTF16(c_text_chunks[i]));
127
128 string16 text = BuildResponseString(text_chunks);
129
130 std::string response;
131 base::JSONWriter::Write(Value::CreateStringValueFromUTF16(text),
132 false, &response);
133 url_fetcher->delegate()->OnURLFetchComplete(url_fetcher,
134 url_fetcher->original_url(),
135 URLRequestStatus(),
136 200,
137 ResponseCookies(),
138 response);
139 }
140
141 // Parses the upload data from |url_fetcher| and puts the value for the q
142 // parameters in |text_chunks|.
143 static void ExtractQueryStringsFromUploadData(TestURLFetcher* url_fetcher,
144 TextChunks* text_chunks) {
145 std::string upload_data = url_fetcher->upload_data();
146
147 CStringTokenizer str_tok(upload_data.c_str(), upload_data.c_str() +
148 upload_data.length(), "&");
149 while (str_tok.GetNext()) {
150 std::string tok = str_tok.token();
151 if (tok.size() > 1U && tok.at(0) == 'q' && tok.at(1) == '=')
152 text_chunks->push_back(UnescapeForHTML(ASCIIToUTF16(tok.substr(2))));
153 }
154 }
155
156 TEST_F(TranslationServiceTest, MergeTestChunks) {
157 std::vector<string16> input;
158 input.push_back(ASCIIToUTF16("Hello"));
159 string16 result = TranslationService::MergeTextChunks(input);
160 EXPECT_EQ(ASCIIToUTF16("Hello"), result);
161 input.push_back(ASCIIToUTF16(" my name"));
162 input.push_back(ASCIIToUTF16(" is"));
163 input.push_back(ASCIIToUTF16(" Jay."));
164 result = TranslationService::MergeTextChunks(input);
165 EXPECT_EQ(ASCIIToUTF16("<a _CR_TR_ id='0'>Hello</a>"
166 "<a _CR_TR_ id='1'> my name</a>"
167 "<a _CR_TR_ id='2'> is</a>"
168 "<a _CR_TR_ id='3'> Jay.</a>"),
169 result);
170 }
171
172 TEST_F(TranslationServiceTest, RemoveTag) {
173 const char* kInputs[] = {
174 "", "Hello", "<a ></a>", " <a href='http://www.google.com'> Link </a>",
175 "<a >Link", "<a link</a>", "<a id=1><a id=1>broken</a></a>",
176 "<a id=1>broken</a></a> bad bad</a>",
177 };
178 const char* kExpected[] = {
179 "", "Hello", "", " Link ", "Link", "<a link", "broken", "broken bad bad"
180 };
181
182 ASSERT_EQ(arraysize(kInputs), arraysize(kExpected));
183 for (size_t i = 0; i < arraysize(kInputs); ++i) {
184 SCOPED_TRACE(::testing::Message::Message() << "Iteration " << i);
185 string16 input = ASCIIToUTF16(kInputs[i]);
186 string16 output = TranslationService::RemoveTag(input);
187 EXPECT_EQ(ASCIIToUTF16(kExpected[i]), output);
188 }
189 }
190
191 // Tests that we deal correctly with the various results the translation server
192 // can return, including the buggy ones.
193 TEST_F(TranslationServiceTest, SplitTestChunks) {
194 // Simple case.
195 std::vector<string16> text_chunks;
196 TranslationService::SplitTextChunks(ASCIIToUTF16("Hello"), &text_chunks);
197 ASSERT_EQ(1U, text_chunks.size());
198 EXPECT_EQ(ASCIIToUTF16("Hello"), text_chunks[0]);
199
200 text_chunks.clear();
201
202 // Multiple chunks case, correct syntax.
203 TranslationService::SplitTextChunks(
204 ASCIIToUTF16("<a _CR_TR_ id='0'>Bonjour</a>"
205 "<a _CR_TR_ id='1'> mon nom</a>"
206 "<a _CR_TR_ id='2'> est</a>"
207 "<a _CR_TR_ id='3'> Jay.</a>"), &text_chunks);
208 ASSERT_EQ(4U, text_chunks.size());
209 EXPECT_EQ(ASCIIToUTF16("Bonjour"), text_chunks[0]);
210 EXPECT_EQ(ASCIIToUTF16(" mon nom"), text_chunks[1]);
211 EXPECT_EQ(ASCIIToUTF16(" est"), text_chunks[2]);
212 EXPECT_EQ(ASCIIToUTF16(" Jay."), text_chunks[3]);
213 text_chunks.clear();
214
215 // Multiple chunks case, duplicate and unbalanced tags.
216 // For info, original input:
217 // <a _CR_TRANSLATE_ id='0'> Experience </a><a _CR_TRANSLATE_ id='1'>Nexus One
218 // </a><a _CR_TRANSLATE_ id='2'>, the new Android phone from Google</a>
219 TranslationService::SplitTextChunks(
220 ASCIIToUTF16("<a _CR_TR_ id='0'>Experience</a> <a _CR_TR_ id='1'>Nexus"
221 "<a _CR_TR_ id='2'> One,</a></a> <a _CR_TR_ id='2'>the new "
222 "Android Phone</a>"), &text_chunks);
223 ASSERT_EQ(3U, text_chunks.size());
224 EXPECT_EQ(ASCIIToUTF16("Experience "), text_chunks[0]);
225 EXPECT_EQ(ASCIIToUTF16("Nexus One, "), text_chunks[1]);
226 EXPECT_EQ(ASCIIToUTF16("the new Android Phone"), text_chunks[2]);
227 text_chunks.clear();
228 }
229
230 // Tests that a successful translate works as expected.
231 TEST_F(TranslationServiceTest, SimpleSuccessfulTranslation) {
jungshik at Google 2010/01/30 01:27:17 as a sanity check, adding one more case (e.g. en =
232 const char* const kEnglishTextChunks[] = {
233 "An atom is talking to another atom:",
234 "- I think I lost an electron.",
235 "- Are you sure?",
236 "- I am positive."
237 };
238
239 const char* const kFrenchTextChunks[] = {
240 "Un atome parle a un autre atome:",
241 "- Je crois que j'ai perdu un electron.",
242 "- T'es sur?",
243 "- Je suis positif." // Note that the joke translates poorly in French.
244 };
245
246 // Translate some text unsecurely.
247 std::vector<string16> text_chunks;
248 for (size_t i = 0; i < arraysize(kEnglishTextChunks); ++i)
249 text_chunks.push_back(ASCIIToUTF16(kEnglishTextChunks[i]));
250 translation_service_.Translate(0, 0, 0, text_chunks, "en", "fr", false);
251
252 TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
253 // The message was too short to triger the sending of a request to the
254 // translation request. A task has been pushed for that.
255 EXPECT_TRUE(url_fetcher == NULL);
256 MessageLoop::current()->RunAllPending();
257
258 // Now the task has been run, the message should have been sent.
259 url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
260 ASSERT_TRUE(url_fetcher != NULL);
261 // Check the URL is HTTP.
262 EXPECT_FALSE(url_fetcher->original_url().SchemeIsSecure());
263
264 // Let's simulate the JSON response from the server.
265 SimulateSimpleSuccessfulResponse(url_fetcher, &(kFrenchTextChunks[0]),
266 arraysize(kFrenchTextChunks));
267
268 // This should have triggered a ViewMsg_TranslateTextReponse message.
269 ASSERT_EQ(1U, message_sender_.message_count());
270
271 // Test the message has the right translation.
272 int routing_id = 0;
273 int work_id = 0;
274 int error_id = 0;
275 std::vector<string16> translated_text_chunks;
276 message_sender_.GetMessageParameters(0, &routing_id, &work_id, &error_id,
277 &translated_text_chunks);
278 EXPECT_EQ(0, routing_id);
279 EXPECT_EQ(0, error_id);
280 ASSERT_EQ(arraysize(kFrenchTextChunks), translated_text_chunks.size());
281 for (size_t i = 0; i < arraysize(kFrenchTextChunks); ++i)
282 EXPECT_EQ(ASCIIToUTF16(kFrenchTextChunks[i]), translated_text_chunks[i]);
283 }
284
285 // Tests that on failure we send the expected error message.
286 TEST_F(TranslationServiceTest, FailedTranslation) {
287 std::vector<string16> text_chunks;
288 text_chunks.push_back(ASCIIToUTF16("Hello"));
289 translation_service_.Translate(0, 0, 0, text_chunks, "en", "fr", false);
290
291 // Run the task that creates the URLFetcher and sends the request.
292 MessageLoop::current()->RunAllPending();
293
294 TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
295 ASSERT_TRUE(url_fetcher != NULL);
296 SimulateErrorResponse(url_fetcher, 500);
297
298 // This should have triggered a ViewMsg_TranslateTextReponse message.
299 ASSERT_EQ(1U, message_sender_.message_count());
300
301 // Test the message has some error.
302 int routing_id = 0;
303 int work_id = 0;
304 int error_id = 0;
305 std::vector<string16> translated_text_chunks;
306 message_sender_.GetMessageParameters(0, &routing_id, &work_id, &error_id,
307 &translated_text_chunks);
308
309 EXPECT_NE(0, error_id); // Anything but 0 means there was an error.
310 EXPECT_TRUE(translated_text_chunks.empty());
311 }
312
313 // Tests that a secure translation is done over a secure connection.
314 TEST_F(TranslationServiceTest, SecureTranslation) {
315 std::vector<string16> text_chunks;
316 text_chunks.push_back(ASCIIToUTF16("Hello"));
317 translation_service_.Translate(0, 0, 0, text_chunks, "en", "fr",
318 true /* secure */);
319
320 // Run the task that creates the URLFetcher and sends the request.
321 MessageLoop::current()->RunAllPending();
322
323 TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
324 ASSERT_TRUE(url_fetcher != NULL);
325 EXPECT_TRUE(url_fetcher->original_url().SchemeIsSecure());
326 }
327
328 // Test mixing requests from different renderers.
329 TEST_F(TranslationServiceTest, MultipleRVRequests) {
330 const char* const kExpectedRV1_1 = "Bonjour RV1";
331 const char* const kExpectedRV1_2 = "Encore bonjour RV1";
332 const char* const kExpectedRV1_3 = "Encore bonjour a nouveau RV1";
333 const char* const kExpectedRV2 = "Bonjour RV2";
334 const char* const kExpectedRV3 = "Bonjour RV3";
335
336 TextChunks text_chunks;
337 text_chunks.push_back(ASCIIToUTF16("Hello RV1"));
338 text_chunks.push_back(ASCIIToUTF16("Hello again RV1"));
339 translation_service_.Translate(1, 0, 0, text_chunks, "en", "fr", false);
340 text_chunks.clear();
341 text_chunks.push_back(ASCIIToUTF16("Hello RV2"));
342 translation_service_.Translate(2, 0, 0, text_chunks, "en", "fr", false);
343 text_chunks.clear();
344 text_chunks.push_back(ASCIIToUTF16("Hello again one more time RV1"));
345 translation_service_.Translate(1, 0, 1, text_chunks, "en", "fr", false);
346 text_chunks.clear();
347 text_chunks.push_back(ASCIIToUTF16("Hello RV3"));
348 translation_service_.Translate(3, 0, 0, text_chunks, "en", "fr", false);
349
350 // Run the tasks that create the URLFetcher and send the requests.
351 MessageLoop::current()->RunAllPending();
352
353 // We should have 3 pending URL fetchers. (The 2 translate requests for RV1
354 // should have been grouped in 1.) Simluate the translation server responses.
355 TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(1);
356 ASSERT_TRUE(url_fetcher != NULL);
357
358 TextChunksList text_chunks_list;
359 text_chunks.clear();
360 text_chunks.push_back(ASCIIToUTF16(kExpectedRV1_1));
361 text_chunks.push_back(ASCIIToUTF16(kExpectedRV1_2));
362 text_chunks_list.push_back(text_chunks);
363 text_chunks.clear();
364 text_chunks.push_back(ASCIIToUTF16(kExpectedRV1_3));
365 text_chunks_list.push_back(text_chunks);
366 SimulateSuccessfulResponse(url_fetcher, text_chunks_list);
367
368 url_fetcher = url_fetcher_factory_.GetFetcherByID(2);
369 ASSERT_TRUE(url_fetcher != NULL);
370 SimulateSimpleSuccessfulResponse(url_fetcher, &kExpectedRV2, 1);
371
372 url_fetcher = url_fetcher_factory_.GetFetcherByID(3);
373 ASSERT_TRUE(url_fetcher != NULL);
374 SimulateSimpleSuccessfulResponse(url_fetcher, &kExpectedRV3, 1);
375
376 // This should have triggered 4 ViewMsg_TranslateTextReponse messages.
377 ASSERT_EQ(4U, message_sender_.message_count());
378
379 const int kExpectedRoutingID[] = { 1, 1, 2, 3 };
380 const int kExpectedWorkID[] = { 0, 1, 0, 0 };
381 const size_t kExpectedStringCount[] = { 2U, 1U, 1U, 1U };
382
383 // Test the messages have the expected content.
384 for (size_t i = 0; i < 4; i++) {
385 SCOPED_TRACE(::testing::Message::Message() << "Iteration " << i);
386 int routing_id = 0;
387 int work_id = 0;
388 int error_id = 0;
389 std::vector<string16> translated_text_chunks;
390 message_sender_.GetMessageParameters(i, &routing_id, &work_id, &error_id,
391 &translated_text_chunks);
392 EXPECT_EQ(kExpectedRoutingID[i], routing_id);
393 EXPECT_EQ(kExpectedWorkID[i], work_id);
394 EXPECT_EQ(0, error_id);
395 EXPECT_EQ(kExpectedStringCount[i], translated_text_chunks.size());
396 // TODO(jcampan): we should compare the strings.
397 }
398 }
399
400 // Tests sending more than the max size.
401 TEST_F(TranslationServiceTest, MoreThanMaxSizeRequests) {
402 std::string one_kb_string(1024U, 'A');
403 TextChunks text_chunks;
404 text_chunks.push_back(ASCIIToUTF16(one_kb_string));
405 // Send 2 small requests, than a big one.
jungshik at Google 2010/01/30 01:27:17 nit: than ==> then
406 translation_service_.Translate(1, 0, 0, text_chunks, "en", "fr", false);
407 translation_service_.Translate(1, 0, 1, text_chunks, "en", "fr", false);
408 // We need a string big enough to be more than 30KB on top of the other 2
409 // requests, but to be less than 30KB when sent (that sizes includes the
410 // other parameters required by the translation server).
411 std::string twenty_nine_kb_string(29 * 1024, 'G');
412 text_chunks.clear();
413 text_chunks.push_back(ASCIIToUTF16(twenty_nine_kb_string));
414 translation_service_.Translate(1, 0, 2, text_chunks, "en", "fr", false);
415
416 // Without any task been run, the 2 first requests should have been sent.
417 TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(1);
418 TextChunks query_strings;
419 ExtractQueryStringsFromUploadData(url_fetcher, &query_strings);
420 ASSERT_EQ(2U, query_strings.size());
421 EXPECT_EQ(one_kb_string, UTF16ToASCII(query_strings[0]));
422 EXPECT_EQ(one_kb_string, UTF16ToASCII(query_strings[1]));
423
424 // Then when the task runs, the big request is sent.
425 MessageLoop::current()->RunAllPending();
426
427 url_fetcher = url_fetcher_factory_.GetFetcherByID(1);
428 query_strings.clear();
429 ExtractQueryStringsFromUploadData(url_fetcher, &query_strings);
430 ASSERT_EQ(1U, query_strings.size());
431 EXPECT_EQ(twenty_nine_kb_string, UTF16ToASCII(query_strings[0]));
432 }
433
434 // Test mixing secure/insecure requests and that secure requests are always sent
435 // over HTTPS.
436 TEST_F(TranslationServiceTest, MixedHTTPAndHTTPS) {
437 const char* kUnsecureMessage = "Hello";
438 const char* kSecureMessage = "Hello_Secure";
439
440 std::vector<string16> text_chunks;
441 text_chunks.push_back(ASCIIToUTF16(kUnsecureMessage));
442 translation_service_.Translate(0, 0, 0, text_chunks, "en", "fr", false);
443 text_chunks.clear();
444 text_chunks.push_back(ASCIIToUTF16(kSecureMessage));
445 translation_service_.Translate(0, 0, 0, text_chunks, "en", "fr", true);
446
447 // Run the task that creates the URLFetcher and send the request.
448 MessageLoop::current()->RunAllPending();
449
450 // We only get the last URLFetcher since we id them based on their routing id
451 // which we want to be the same in that test. We'll just check that as
452 // expected the last one is the HTTPS and contains only the secure string.
453 TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
454 TextChunks query_strings;
455 ExtractQueryStringsFromUploadData(url_fetcher, &query_strings);
456 ASSERT_EQ(1U, query_strings.size());
457 EXPECT_EQ(kSecureMessage, UTF16ToASCII(query_strings[0]));
458 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698