Index: chrome/browser/renderer_host/translation_service_unittest.cc |
=================================================================== |
--- chrome/browser/renderer_host/translation_service_unittest.cc (revision 0) |
+++ chrome/browser/renderer_host/translation_service_unittest.cc (revision 0) |
@@ -0,0 +1,458 @@ |
+// Copyright (c) 2010 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/json/json_writer.h" |
+#include "base/stl_util-inl.h" |
+#include "base/string_tokenizer.h" |
+#include "base/string_util.h" |
+#include "chrome/browser/net/test_url_fetcher_factory.h" |
+#include "chrome/browser/renderer_host/translation_service.h" |
+#include "chrome/common/render_messages.h" |
+#include "ipc/ipc_message.h" |
+#include "net/base/escape.h" |
+#include "net/url_request/url_request_status.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+typedef std::vector<string16> TextChunks; |
+typedef std::vector<TextChunks> TextChunksList; |
+ |
+class TestMessageSender : public IPC::Message::Sender { |
+ public: |
+ virtual ~TestMessageSender() { |
+ ClearMessages(); |
+ } |
+ |
+ virtual bool Send(IPC::Message* msg) { |
+ messages_.push_back(msg); |
+ return true; |
+ } |
+ |
+ size_t message_count() const { return messages_.size(); } |
+ |
+ void GetMessageParameters(size_t message_index, int* routing_id, |
+ int* work_id, int* error_id, |
+ std::vector<string16>* text_chunks) { |
+ ASSERT_LT(message_index, messages_.size()); |
+ *routing_id = messages_.at(message_index)->routing_id(); |
+ ViewMsg_TranslateTextReponse::Read(messages_.at(message_index), |
+ work_id, error_id, text_chunks); |
+ } |
+ |
+ void ClearMessages() { |
+ STLDeleteElements(&messages_); |
+ messages_.clear(); |
+ } |
+ |
+ std::vector<IPC::Message*> messages_; |
+ MessageLoop message_loop_; |
+}; |
+ |
+class TestTranslationService : public TranslationService { |
+ public: |
+ explicit TestTranslationService(IPC::Message::Sender* message_sender) |
+ : TranslationService(message_sender) {} |
+ |
+ virtual int GetSendRequestDelay() const { |
+ return 0; |
+ } |
+}; |
+ |
+class TranslationServiceTest : public testing::Test { |
+ public: |
+ TranslationServiceTest() : translation_service_(&message_sender_) {} |
+ |
+ virtual void SetUp() { |
+ URLFetcher::set_factory(&url_fetcher_factory_); |
+ } |
+ |
+ virtual void TearDown() { |
+ URLFetcher::set_factory(NULL); |
+ } |
+ |
+ protected: |
+ TestURLFetcherFactory url_fetcher_factory_; |
+ TestMessageSender message_sender_; |
+ TestTranslationService translation_service_; |
+}; |
+ |
+static void SimulateErrorResponse(TestURLFetcher* url_fetcher, |
+ int response_code) { |
+ url_fetcher->delegate()->OnURLFetchComplete(url_fetcher, |
+ url_fetcher->original_url(), |
+ URLRequestStatus(), |
+ response_code, |
+ ResponseCookies(), |
+ std::string()); |
+} |
+ |
+// Merges the strings in |text_chunks| into the string that the translation |
+// server received. |
+static string16 BuildResponseString(const TextChunks& text_chunks) { |
+ string16 text; |
+ for (size_t i = 0; i < text_chunks.size(); ++i) { |
+ text.append(ASCIIToUTF16("<a _CR_TR_ id='")); |
+ text.append(IntToString16(i)); |
+ text.append(ASCIIToUTF16("'>")); |
+ text.append(text_chunks.at(i)); |
+ text.append(ASCIIToUTF16("</a>")); |
+ } |
+ return text; |
+} |
+ |
+static void SimulateSuccessfulResponse(TestURLFetcher* url_fetcher, |
+ const TextChunksList& text_chunks_list) { |
+ scoped_ptr<ListValue> list(new ListValue()); |
+ for (TextChunksList::const_iterator iter = text_chunks_list.begin(); |
+ iter != text_chunks_list.end(); ++iter) { |
+ list->Append(Value::CreateStringValueFromUTF16(BuildResponseString(*iter))); |
+ } |
+ |
+ std::string response; |
+ base::JSONWriter::Write(list.get(), false, &response); |
+ url_fetcher->delegate()->OnURLFetchComplete(url_fetcher, |
+ url_fetcher->original_url(), |
+ URLRequestStatus(), |
+ 200, |
+ ResponseCookies(), |
+ response); |
+} |
+ |
+static void SimulateSimpleSuccessfulResponse(TestURLFetcher* url_fetcher, |
+ const char* const* c_text_chunks, |
+ size_t text_chunks_length) { |
+ TextChunks text_chunks; |
+ for (size_t i = 0; i < text_chunks_length; i++) |
+ text_chunks.push_back(ASCIIToUTF16(c_text_chunks[i])); |
+ |
+ string16 text = BuildResponseString(text_chunks); |
+ |
+ std::string response; |
+ base::JSONWriter::Write(Value::CreateStringValueFromUTF16(text), |
+ false, &response); |
+ url_fetcher->delegate()->OnURLFetchComplete(url_fetcher, |
+ url_fetcher->original_url(), |
+ URLRequestStatus(), |
+ 200, |
+ ResponseCookies(), |
+ response); |
+} |
+ |
+// Parses the upload data from |url_fetcher| and puts the value for the q |
+// parameters in |text_chunks|. |
+static void ExtractQueryStringsFromUploadData(TestURLFetcher* url_fetcher, |
+ TextChunks* text_chunks) { |
+ std::string upload_data = url_fetcher->upload_data(); |
+ |
+ CStringTokenizer str_tok(upload_data.c_str(), upload_data.c_str() + |
+ upload_data.length(), "&"); |
+ while (str_tok.GetNext()) { |
+ std::string tok = str_tok.token(); |
+ if (tok.size() > 1U && tok.at(0) == 'q' && tok.at(1) == '=') |
+ text_chunks->push_back(UnescapeForHTML(ASCIIToUTF16(tok.substr(2)))); |
+ } |
+} |
+ |
+TEST_F(TranslationServiceTest, MergeTestChunks) { |
+ std::vector<string16> input; |
+ input.push_back(ASCIIToUTF16("Hello")); |
+ string16 result = TranslationService::MergeTextChunks(input); |
+ EXPECT_EQ(ASCIIToUTF16("Hello"), result); |
+ input.push_back(ASCIIToUTF16(" my name")); |
+ input.push_back(ASCIIToUTF16(" is")); |
+ input.push_back(ASCIIToUTF16(" Jay.")); |
+ result = TranslationService::MergeTextChunks(input); |
+ EXPECT_EQ(ASCIIToUTF16("<a _CR_TR_ id='0'>Hello</a>" |
+ "<a _CR_TR_ id='1'> my name</a>" |
+ "<a _CR_TR_ id='2'> is</a>" |
+ "<a _CR_TR_ id='3'> Jay.</a>"), |
+ result); |
+} |
+ |
+TEST_F(TranslationServiceTest, RemoveTag) { |
+ const char* kInputs[] = { |
+ "", "Hello", "<a ></a>", " <a href='http://www.google.com'> Link </a>", |
+ "<a >Link", "<a link</a>", "<a id=1><a id=1>broken</a></a>", |
+ "<a id=1>broken</a></a> bad bad</a>", |
+ }; |
+ const char* kExpected[] = { |
+ "", "Hello", "", " Link ", "Link", "<a link", "broken", "broken bad bad" |
+ }; |
+ |
+ ASSERT_EQ(arraysize(kInputs), arraysize(kExpected)); |
+ for (size_t i = 0; i < arraysize(kInputs); ++i) { |
+ SCOPED_TRACE(::testing::Message::Message() << "Iteration " << i); |
+ string16 input = ASCIIToUTF16(kInputs[i]); |
+ string16 output = TranslationService::RemoveTag(input); |
+ EXPECT_EQ(ASCIIToUTF16(kExpected[i]), output); |
+ } |
+} |
+ |
+// Tests that we deal correctly with the various results the translation server |
+// can return, including the buggy ones. |
+TEST_F(TranslationServiceTest, SplitTestChunks) { |
+ // Simple case. |
+ std::vector<string16> text_chunks; |
+ TranslationService::SplitTextChunks(ASCIIToUTF16("Hello"), &text_chunks); |
+ ASSERT_EQ(1U, text_chunks.size()); |
+ EXPECT_EQ(ASCIIToUTF16("Hello"), text_chunks[0]); |
+ |
+ text_chunks.clear(); |
+ |
+ // Multiple chunks case, correct syntax. |
+ TranslationService::SplitTextChunks( |
+ ASCIIToUTF16("<a _CR_TR_ id='0'>Bonjour</a>" |
+ "<a _CR_TR_ id='1'> mon nom</a>" |
+ "<a _CR_TR_ id='2'> est</a>" |
+ "<a _CR_TR_ id='3'> Jay.</a>"), &text_chunks); |
+ ASSERT_EQ(4U, text_chunks.size()); |
+ EXPECT_EQ(ASCIIToUTF16("Bonjour"), text_chunks[0]); |
+ EXPECT_EQ(ASCIIToUTF16(" mon nom"), text_chunks[1]); |
+ EXPECT_EQ(ASCIIToUTF16(" est"), text_chunks[2]); |
+ EXPECT_EQ(ASCIIToUTF16(" Jay."), text_chunks[3]); |
+ text_chunks.clear(); |
+ |
+ // Multiple chunks case, duplicate and unbalanced tags. |
+ // For info, original input: |
+ // <a _CR_TRANSLATE_ id='0'> Experience </a><a _CR_TRANSLATE_ id='1'>Nexus One |
+ // </a><a _CR_TRANSLATE_ id='2'>, the new Android phone from Google</a> |
+ TranslationService::SplitTextChunks( |
+ ASCIIToUTF16("<a _CR_TR_ id='0'>Experience</a> <a _CR_TR_ id='1'>Nexus" |
+ "<a _CR_TR_ id='2'> One,</a></a> <a _CR_TR_ id='2'>the new " |
+ "Android Phone</a>"), &text_chunks); |
+ ASSERT_EQ(3U, text_chunks.size()); |
+ EXPECT_EQ(ASCIIToUTF16("Experience "), text_chunks[0]); |
+ EXPECT_EQ(ASCIIToUTF16("Nexus One, "), text_chunks[1]); |
+ EXPECT_EQ(ASCIIToUTF16("the new Android Phone"), text_chunks[2]); |
+ text_chunks.clear(); |
+} |
+ |
+// Tests that a successful translate works as expected. |
+TEST_F(TranslationServiceTest, SimpleSuccessfulTranslation) { |
jungshik at Google
2010/01/30 01:27:17
as a sanity check, adding one more case (e.g. en =
|
+ const char* const kEnglishTextChunks[] = { |
+ "An atom is talking to another atom:", |
+ "- I think I lost an electron.", |
+ "- Are you sure?", |
+ "- I am positive." |
+ }; |
+ |
+ const char* const kFrenchTextChunks[] = { |
+ "Un atome parle a un autre atome:", |
+ "- Je crois que j'ai perdu un electron.", |
+ "- T'es sur?", |
+ "- Je suis positif." // Note that the joke translates poorly in French. |
+ }; |
+ |
+ // Translate some text unsecurely. |
+ std::vector<string16> text_chunks; |
+ for (size_t i = 0; i < arraysize(kEnglishTextChunks); ++i) |
+ text_chunks.push_back(ASCIIToUTF16(kEnglishTextChunks[i])); |
+ translation_service_.Translate(0, 0, 0, text_chunks, "en", "fr", false); |
+ |
+ TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0); |
+ // The message was too short to triger the sending of a request to the |
+ // translation request. A task has been pushed for that. |
+ EXPECT_TRUE(url_fetcher == NULL); |
+ MessageLoop::current()->RunAllPending(); |
+ |
+ // Now the task has been run, the message should have been sent. |
+ url_fetcher = url_fetcher_factory_.GetFetcherByID(0); |
+ ASSERT_TRUE(url_fetcher != NULL); |
+ // Check the URL is HTTP. |
+ EXPECT_FALSE(url_fetcher->original_url().SchemeIsSecure()); |
+ |
+ // Let's simulate the JSON response from the server. |
+ SimulateSimpleSuccessfulResponse(url_fetcher, &(kFrenchTextChunks[0]), |
+ arraysize(kFrenchTextChunks)); |
+ |
+ // This should have triggered a ViewMsg_TranslateTextReponse message. |
+ ASSERT_EQ(1U, message_sender_.message_count()); |
+ |
+ // Test the message has the right translation. |
+ int routing_id = 0; |
+ int work_id = 0; |
+ int error_id = 0; |
+ std::vector<string16> translated_text_chunks; |
+ message_sender_.GetMessageParameters(0, &routing_id, &work_id, &error_id, |
+ &translated_text_chunks); |
+ EXPECT_EQ(0, routing_id); |
+ EXPECT_EQ(0, error_id); |
+ ASSERT_EQ(arraysize(kFrenchTextChunks), translated_text_chunks.size()); |
+ for (size_t i = 0; i < arraysize(kFrenchTextChunks); ++i) |
+ EXPECT_EQ(ASCIIToUTF16(kFrenchTextChunks[i]), translated_text_chunks[i]); |
+} |
+ |
+// Tests that on failure we send the expected error message. |
+TEST_F(TranslationServiceTest, FailedTranslation) { |
+ std::vector<string16> text_chunks; |
+ text_chunks.push_back(ASCIIToUTF16("Hello")); |
+ translation_service_.Translate(0, 0, 0, text_chunks, "en", "fr", false); |
+ |
+ // Run the task that creates the URLFetcher and sends the request. |
+ MessageLoop::current()->RunAllPending(); |
+ |
+ TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0); |
+ ASSERT_TRUE(url_fetcher != NULL); |
+ SimulateErrorResponse(url_fetcher, 500); |
+ |
+ // This should have triggered a ViewMsg_TranslateTextReponse message. |
+ ASSERT_EQ(1U, message_sender_.message_count()); |
+ |
+ // Test the message has some error. |
+ int routing_id = 0; |
+ int work_id = 0; |
+ int error_id = 0; |
+ std::vector<string16> translated_text_chunks; |
+ message_sender_.GetMessageParameters(0, &routing_id, &work_id, &error_id, |
+ &translated_text_chunks); |
+ |
+ EXPECT_NE(0, error_id); // Anything but 0 means there was an error. |
+ EXPECT_TRUE(translated_text_chunks.empty()); |
+} |
+ |
+// Tests that a secure translation is done over a secure connection. |
+TEST_F(TranslationServiceTest, SecureTranslation) { |
+ std::vector<string16> text_chunks; |
+ text_chunks.push_back(ASCIIToUTF16("Hello")); |
+ translation_service_.Translate(0, 0, 0, text_chunks, "en", "fr", |
+ true /* secure */); |
+ |
+ // Run the task that creates the URLFetcher and sends the request. |
+ MessageLoop::current()->RunAllPending(); |
+ |
+ TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0); |
+ ASSERT_TRUE(url_fetcher != NULL); |
+ EXPECT_TRUE(url_fetcher->original_url().SchemeIsSecure()); |
+} |
+ |
+// Test mixing requests from different renderers. |
+TEST_F(TranslationServiceTest, MultipleRVRequests) { |
+ const char* const kExpectedRV1_1 = "Bonjour RV1"; |
+ const char* const kExpectedRV1_2 = "Encore bonjour RV1"; |
+ const char* const kExpectedRV1_3 = "Encore bonjour a nouveau RV1"; |
+ const char* const kExpectedRV2 = "Bonjour RV2"; |
+ const char* const kExpectedRV3 = "Bonjour RV3"; |
+ |
+ TextChunks text_chunks; |
+ text_chunks.push_back(ASCIIToUTF16("Hello RV1")); |
+ text_chunks.push_back(ASCIIToUTF16("Hello again RV1")); |
+ translation_service_.Translate(1, 0, 0, text_chunks, "en", "fr", false); |
+ text_chunks.clear(); |
+ text_chunks.push_back(ASCIIToUTF16("Hello RV2")); |
+ translation_service_.Translate(2, 0, 0, text_chunks, "en", "fr", false); |
+ text_chunks.clear(); |
+ text_chunks.push_back(ASCIIToUTF16("Hello again one more time RV1")); |
+ translation_service_.Translate(1, 0, 1, text_chunks, "en", "fr", false); |
+ text_chunks.clear(); |
+ text_chunks.push_back(ASCIIToUTF16("Hello RV3")); |
+ translation_service_.Translate(3, 0, 0, text_chunks, "en", "fr", false); |
+ |
+ // Run the tasks that create the URLFetcher and send the requests. |
+ MessageLoop::current()->RunAllPending(); |
+ |
+ // We should have 3 pending URL fetchers. (The 2 translate requests for RV1 |
+ // should have been grouped in 1.) Simluate the translation server responses. |
+ TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(1); |
+ ASSERT_TRUE(url_fetcher != NULL); |
+ |
+ TextChunksList text_chunks_list; |
+ text_chunks.clear(); |
+ text_chunks.push_back(ASCIIToUTF16(kExpectedRV1_1)); |
+ text_chunks.push_back(ASCIIToUTF16(kExpectedRV1_2)); |
+ text_chunks_list.push_back(text_chunks); |
+ text_chunks.clear(); |
+ text_chunks.push_back(ASCIIToUTF16(kExpectedRV1_3)); |
+ text_chunks_list.push_back(text_chunks); |
+ SimulateSuccessfulResponse(url_fetcher, text_chunks_list); |
+ |
+ url_fetcher = url_fetcher_factory_.GetFetcherByID(2); |
+ ASSERT_TRUE(url_fetcher != NULL); |
+ SimulateSimpleSuccessfulResponse(url_fetcher, &kExpectedRV2, 1); |
+ |
+ url_fetcher = url_fetcher_factory_.GetFetcherByID(3); |
+ ASSERT_TRUE(url_fetcher != NULL); |
+ SimulateSimpleSuccessfulResponse(url_fetcher, &kExpectedRV3, 1); |
+ |
+ // This should have triggered 4 ViewMsg_TranslateTextReponse messages. |
+ ASSERT_EQ(4U, message_sender_.message_count()); |
+ |
+ const int kExpectedRoutingID[] = { 1, 1, 2, 3 }; |
+ const int kExpectedWorkID[] = { 0, 1, 0, 0 }; |
+ const size_t kExpectedStringCount[] = { 2U, 1U, 1U, 1U }; |
+ |
+ // Test the messages have the expected content. |
+ for (size_t i = 0; i < 4; i++) { |
+ SCOPED_TRACE(::testing::Message::Message() << "Iteration " << i); |
+ int routing_id = 0; |
+ int work_id = 0; |
+ int error_id = 0; |
+ std::vector<string16> translated_text_chunks; |
+ message_sender_.GetMessageParameters(i, &routing_id, &work_id, &error_id, |
+ &translated_text_chunks); |
+ EXPECT_EQ(kExpectedRoutingID[i], routing_id); |
+ EXPECT_EQ(kExpectedWorkID[i], work_id); |
+ EXPECT_EQ(0, error_id); |
+ EXPECT_EQ(kExpectedStringCount[i], translated_text_chunks.size()); |
+ // TODO(jcampan): we should compare the strings. |
+ } |
+} |
+ |
+// Tests sending more than the max size. |
+TEST_F(TranslationServiceTest, MoreThanMaxSizeRequests) { |
+ std::string one_kb_string(1024U, 'A'); |
+ TextChunks text_chunks; |
+ text_chunks.push_back(ASCIIToUTF16(one_kb_string)); |
+ // Send 2 small requests, than a big one. |
jungshik at Google
2010/01/30 01:27:17
nit: than ==> then
|
+ translation_service_.Translate(1, 0, 0, text_chunks, "en", "fr", false); |
+ translation_service_.Translate(1, 0, 1, text_chunks, "en", "fr", false); |
+ // We need a string big enough to be more than 30KB on top of the other 2 |
+ // requests, but to be less than 30KB when sent (that sizes includes the |
+ // other parameters required by the translation server). |
+ std::string twenty_nine_kb_string(29 * 1024, 'G'); |
+ text_chunks.clear(); |
+ text_chunks.push_back(ASCIIToUTF16(twenty_nine_kb_string)); |
+ translation_service_.Translate(1, 0, 2, text_chunks, "en", "fr", false); |
+ |
+ // Without any task been run, the 2 first requests should have been sent. |
+ TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(1); |
+ TextChunks query_strings; |
+ ExtractQueryStringsFromUploadData(url_fetcher, &query_strings); |
+ ASSERT_EQ(2U, query_strings.size()); |
+ EXPECT_EQ(one_kb_string, UTF16ToASCII(query_strings[0])); |
+ EXPECT_EQ(one_kb_string, UTF16ToASCII(query_strings[1])); |
+ |
+ // Then when the task runs, the big request is sent. |
+ MessageLoop::current()->RunAllPending(); |
+ |
+ url_fetcher = url_fetcher_factory_.GetFetcherByID(1); |
+ query_strings.clear(); |
+ ExtractQueryStringsFromUploadData(url_fetcher, &query_strings); |
+ ASSERT_EQ(1U, query_strings.size()); |
+ EXPECT_EQ(twenty_nine_kb_string, UTF16ToASCII(query_strings[0])); |
+} |
+ |
+// Test mixing secure/insecure requests and that secure requests are always sent |
+// over HTTPS. |
+TEST_F(TranslationServiceTest, MixedHTTPAndHTTPS) { |
+ const char* kUnsecureMessage = "Hello"; |
+ const char* kSecureMessage = "Hello_Secure"; |
+ |
+ std::vector<string16> text_chunks; |
+ text_chunks.push_back(ASCIIToUTF16(kUnsecureMessage)); |
+ translation_service_.Translate(0, 0, 0, text_chunks, "en", "fr", false); |
+ text_chunks.clear(); |
+ text_chunks.push_back(ASCIIToUTF16(kSecureMessage)); |
+ translation_service_.Translate(0, 0, 0, text_chunks, "en", "fr", true); |
+ |
+ // Run the task that creates the URLFetcher and send the request. |
+ MessageLoop::current()->RunAllPending(); |
+ |
+ // We only get the last URLFetcher since we id them based on their routing id |
+ // which we want to be the same in that test. We'll just check that as |
+ // expected the last one is the HTTPS and contains only the secure string. |
+ TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0); |
+ TextChunks query_strings; |
+ ExtractQueryStringsFromUploadData(url_fetcher, &query_strings); |
+ ASSERT_EQ(1U, query_strings.size()); |
+ EXPECT_EQ(kSecureMessage, UTF16ToASCII(query_strings[0])); |
+} |
Property changes on: chrome\browser\renderer_host\translation_service_unittest.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |