Home | History | Annotate | Download | only in translate
      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.
      4 
      5 #include "base/time/time.h"
      6 #include "chrome/common/chrome_constants.h"
      7 #include "chrome/common/render_messages.h"
      8 #include "chrome/renderer/translate/translate_helper.h"
      9 #include "chrome/test/base/chrome_render_view_test.h"
     10 #include "content/public/renderer/render_view.h"
     11 #include "testing/gmock/include/gmock/gmock.h"
     12 #include "testing/gtest/include/gtest/gtest.h"
     13 #include "third_party/WebKit/public/web/WebHistoryItem.h"
     14 
     15 using testing::AtLeast;
     16 using testing::Return;
     17 using testing::_;
     18 
     19 class TestTranslateHelper : public TranslateHelper {
     20  public:
     21   explicit TestTranslateHelper(content::RenderView* render_view)
     22       : TranslateHelper(render_view) {
     23   }
     24 
     25   virtual base::TimeDelta AdjustDelay(int delayInMs) OVERRIDE {
     26     // Just returns base::TimeDelta() which has initial value 0.
     27     // Tasks doesn't need to be delayed in tests.
     28     return base::TimeDelta();
     29   }
     30 
     31   void TranslatePage(int page_id,
     32                      const std::string& source_lang,
     33                      const std::string& target_lang,
     34                      const std::string& translate_script) {
     35     OnTranslatePage(page_id, translate_script, source_lang, target_lang);
     36   }
     37 
     38   MOCK_METHOD0(IsTranslateLibAvailable, bool());
     39   MOCK_METHOD0(IsTranslateLibReady, bool());
     40   MOCK_METHOD0(HasTranslationFinished, bool());
     41   MOCK_METHOD0(HasTranslationFailed, bool());
     42   MOCK_METHOD0(GetOriginalPageLanguage, std::string());
     43   MOCK_METHOD0(StartTranslation, bool());
     44   MOCK_METHOD1(ExecuteScript, void(const std::string&));
     45   MOCK_METHOD2(ExecuteScriptAndGetBoolResult, bool(const std::string&, bool));
     46   MOCK_METHOD1(ExecuteScriptAndGetStringResult,
     47                std::string(const std::string&));
     48   MOCK_METHOD1(ExecuteScriptAndGetDoubleResult, double(const std::string&));
     49 
     50  private:
     51   DISALLOW_COPY_AND_ASSIGN(TestTranslateHelper);
     52 };
     53 
     54 class TranslateHelperBrowserTest : public ChromeRenderViewTest {
     55  public:
     56   TranslateHelperBrowserTest() : translate_helper_(NULL) {}
     57 
     58  protected:
     59   virtual void SetUp() OVERRIDE {
     60     ChromeRenderViewTest::SetUp();
     61     translate_helper_ = new TestTranslateHelper(view_);
     62   }
     63 
     64   virtual void TearDown() OVERRIDE {
     65     delete translate_helper_;
     66     ChromeRenderViewTest::TearDown();
     67   }
     68 
     69   bool GetPageTranslatedMessage(int* page_id,
     70                                 std::string* original_lang,
     71                                 std::string* target_lang,
     72                                 TranslateErrors::Type* error) {
     73     const IPC::Message* message = render_thread_->sink().
     74         GetUniqueMessageMatching(ChromeViewHostMsg_PageTranslated::ID);
     75     if (!message)
     76       return false;
     77     Tuple4<int, std::string, std::string, TranslateErrors::Type>
     78         translate_param;
     79     ChromeViewHostMsg_PageTranslated::Read(message, &translate_param);
     80     if (page_id)
     81       *page_id = translate_param.a;
     82     if (original_lang)
     83       *original_lang = translate_param.b;
     84     if (target_lang)
     85       *target_lang = translate_param.c;
     86     if (error)
     87       *error = translate_param.d;
     88     return true;
     89   }
     90 
     91   TestTranslateHelper* translate_helper_;
     92 
     93  private:
     94   DISALLOW_COPY_AND_ASSIGN(TranslateHelperBrowserTest);
     95 };
     96 
     97 // Tests that the browser gets notified of the translation failure if the
     98 // translate library fails/times-out during initialization.
     99 TEST_F(TranslateHelperBrowserTest, TranslateLibNeverReady) {
    100   // We make IsTranslateLibAvailable true so we don't attempt to inject the
    101   // library.
    102   EXPECT_CALL(*translate_helper_, IsTranslateLibAvailable())
    103       .Times(AtLeast(1))
    104       .WillRepeatedly(Return(true));
    105 
    106   EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
    107       .Times(AtLeast(5))  // See kMaxTranslateInitCheckAttempts in
    108                           // translate_helper.cc
    109       .WillRepeatedly(Return(false));
    110 
    111   translate_helper_->TranslatePage(
    112       view_->GetPageId(), "en", "fr", std::string());
    113   base::MessageLoop::current()->RunUntilIdle();
    114 
    115   int page_id;
    116   TranslateErrors::Type error;
    117   ASSERT_TRUE(GetPageTranslatedMessage(&page_id, NULL, NULL, &error));
    118   EXPECT_EQ(view_->GetPageId(), page_id);
    119   EXPECT_EQ(TranslateErrors::INITIALIZATION_ERROR, error);
    120 }
    121 
    122 // Tests that the browser gets notified of the translation success when the
    123 // translation succeeds.
    124 TEST_F(TranslateHelperBrowserTest, TranslateSuccess) {
    125   // We make IsTranslateLibAvailable true so we don't attempt to inject the
    126   // library.
    127   EXPECT_CALL(*translate_helper_, IsTranslateLibAvailable())
    128       .Times(AtLeast(1))
    129       .WillRepeatedly(Return(true));
    130 
    131   EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
    132       .WillOnce(Return(false))
    133       .WillOnce(Return(true));
    134 
    135   EXPECT_CALL(*translate_helper_, StartTranslation()).WillOnce(Return(true));
    136 
    137   // Succeed after few checks.
    138   EXPECT_CALL(*translate_helper_, HasTranslationFailed())
    139       .WillRepeatedly(Return(false));
    140   EXPECT_CALL(*translate_helper_, HasTranslationFinished())
    141       .WillOnce(Return(false))
    142       .WillOnce(Return(false))
    143       .WillOnce(Return(true));
    144 
    145   // V8 call for performance monitoring should be ignored.
    146   EXPECT_CALL(*translate_helper_,
    147               ExecuteScriptAndGetDoubleResult(_)).Times(3);
    148 
    149   std::string original_lang("en");
    150   std::string target_lang("fr");
    151   translate_helper_->TranslatePage(
    152       view_->GetPageId(), original_lang, target_lang, std::string());
    153   base::MessageLoop::current()->RunUntilIdle();
    154 
    155   int page_id;
    156   std::string received_original_lang;
    157   std::string received_target_lang;
    158   TranslateErrors::Type error;
    159   ASSERT_TRUE(GetPageTranslatedMessage(&page_id,
    160                                        &received_original_lang,
    161                                        &received_target_lang,
    162                                        &error));
    163   EXPECT_EQ(view_->GetPageId(), page_id);
    164   EXPECT_EQ(original_lang, received_original_lang);
    165   EXPECT_EQ(target_lang, received_target_lang);
    166   EXPECT_EQ(TranslateErrors::NONE, error);
    167 }
    168 
    169 // Tests that the browser gets notified of the translation failure when the
    170 // translation fails.
    171 TEST_F(TranslateHelperBrowserTest, TranslateFailure) {
    172   // We make IsTranslateLibAvailable true so we don't attempt to inject the
    173   // library.
    174   EXPECT_CALL(*translate_helper_, IsTranslateLibAvailable())
    175       .Times(AtLeast(1))
    176       .WillRepeatedly(Return(true));
    177 
    178   EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
    179       .WillOnce(Return(true));
    180 
    181   EXPECT_CALL(*translate_helper_, StartTranslation()).WillOnce(Return(true));
    182 
    183   // Fail after few checks.
    184   EXPECT_CALL(*translate_helper_, HasTranslationFailed())
    185       .WillOnce(Return(false))
    186       .WillOnce(Return(false))
    187       .WillOnce(Return(false))
    188       .WillOnce(Return(true));
    189 
    190   EXPECT_CALL(*translate_helper_, HasTranslationFinished())
    191       .Times(AtLeast(1))
    192       .WillRepeatedly(Return(false));
    193 
    194   // V8 call for performance monitoring should be ignored.
    195   EXPECT_CALL(*translate_helper_,
    196               ExecuteScriptAndGetDoubleResult(_)).Times(2);
    197 
    198   translate_helper_->TranslatePage(
    199       view_->GetPageId(), "en", "fr", std::string());
    200   base::MessageLoop::current()->RunUntilIdle();
    201 
    202   int page_id;
    203   TranslateErrors::Type error;
    204   ASSERT_TRUE(GetPageTranslatedMessage(&page_id, NULL, NULL, &error));
    205   EXPECT_EQ(view_->GetPageId(), page_id);
    206   EXPECT_EQ(TranslateErrors::TRANSLATION_ERROR, error);
    207 }
    208 
    209 // Tests that when the browser translate a page for which the language is
    210 // undefined we query the translate element to get the language.
    211 TEST_F(TranslateHelperBrowserTest, UndefinedSourceLang) {
    212   // We make IsTranslateLibAvailable true so we don't attempt to inject the
    213   // library.
    214   EXPECT_CALL(*translate_helper_, IsTranslateLibAvailable())
    215       .Times(AtLeast(1))
    216       .WillRepeatedly(Return(true));
    217 
    218   EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
    219       .WillOnce(Return(true));
    220 
    221   EXPECT_CALL(*translate_helper_, GetOriginalPageLanguage())
    222       .WillOnce(Return("de"));
    223 
    224   EXPECT_CALL(*translate_helper_, StartTranslation()).WillOnce(Return(true));
    225   EXPECT_CALL(*translate_helper_, HasTranslationFailed())
    226       .WillOnce(Return(false));
    227   EXPECT_CALL(*translate_helper_, HasTranslationFinished())
    228       .Times(AtLeast(1))
    229       .WillRepeatedly(Return(true));
    230 
    231   // V8 call for performance monitoring should be ignored.
    232   EXPECT_CALL(*translate_helper_,
    233               ExecuteScriptAndGetDoubleResult(_)).Times(3);
    234 
    235   translate_helper_->TranslatePage(view_->GetPageId(),
    236                                    chrome::kUnknownLanguageCode, "fr",
    237                                    std::string());
    238   base::MessageLoop::current()->RunUntilIdle();
    239 
    240   int page_id;
    241   TranslateErrors::Type error;
    242   std::string original_lang;
    243   std::string target_lang;
    244   ASSERT_TRUE(GetPageTranslatedMessage(&page_id, &original_lang, &target_lang,
    245                                        &error));
    246   EXPECT_EQ(view_->GetPageId(), page_id);
    247   EXPECT_EQ("de", original_lang);
    248   EXPECT_EQ("fr", target_lang);
    249   EXPECT_EQ(TranslateErrors::NONE, error);
    250 }
    251 
    252 // Tests that starting a translation while a similar one is pending does not
    253 // break anything.
    254 TEST_F(TranslateHelperBrowserTest, MultipleSimilarTranslations) {
    255   // We make IsTranslateLibAvailable true so we don't attempt to inject the
    256   // library.
    257   EXPECT_CALL(*translate_helper_, IsTranslateLibAvailable())
    258       .Times(AtLeast(1))
    259       .WillRepeatedly(Return(true));
    260 
    261   EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
    262       .WillRepeatedly(Return(true));
    263   EXPECT_CALL(*translate_helper_, StartTranslation())
    264       .WillRepeatedly(Return(true));
    265   EXPECT_CALL(*translate_helper_, HasTranslationFailed())
    266       .WillRepeatedly(Return(false));
    267   EXPECT_CALL(*translate_helper_, HasTranslationFinished())
    268       .WillOnce(Return(true));
    269 
    270   // V8 call for performance monitoring should be ignored.
    271   EXPECT_CALL(*translate_helper_,
    272               ExecuteScriptAndGetDoubleResult(_)).Times(3);
    273 
    274   std::string original_lang("en");
    275   std::string target_lang("fr");
    276   translate_helper_->TranslatePage(
    277       view_->GetPageId(), original_lang, target_lang, std::string());
    278   // While this is running call again TranslatePage to make sure noting bad
    279   // happens.
    280   translate_helper_->TranslatePage(
    281       view_->GetPageId(), original_lang, target_lang, std::string());
    282   base::MessageLoop::current()->RunUntilIdle();
    283 
    284   int page_id;
    285   std::string received_original_lang;
    286   std::string received_target_lang;
    287   TranslateErrors::Type error;
    288   ASSERT_TRUE(GetPageTranslatedMessage(&page_id,
    289                                        &received_original_lang,
    290                                        &received_target_lang,
    291                                        &error));
    292   EXPECT_EQ(view_->GetPageId(), page_id);
    293   EXPECT_EQ(original_lang, received_original_lang);
    294   EXPECT_EQ(target_lang, received_target_lang);
    295   EXPECT_EQ(TranslateErrors::NONE, error);
    296 }
    297 
    298 // Tests that starting a translation while a different one is pending works.
    299 TEST_F(TranslateHelperBrowserTest, MultipleDifferentTranslations) {
    300   EXPECT_CALL(*translate_helper_, IsTranslateLibAvailable())
    301       .Times(AtLeast(1))
    302       .WillRepeatedly(Return(true));
    303   EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
    304       .WillRepeatedly(Return(true));
    305   EXPECT_CALL(*translate_helper_, StartTranslation())
    306       .WillRepeatedly(Return(true));
    307   EXPECT_CALL(*translate_helper_, HasTranslationFailed())
    308       .WillRepeatedly(Return(false));
    309   EXPECT_CALL(*translate_helper_, HasTranslationFinished())
    310       .WillOnce(Return(true));
    311 
    312   // V8 call for performance monitoring should be ignored.
    313   EXPECT_CALL(*translate_helper_,
    314               ExecuteScriptAndGetDoubleResult(_)).Times(5);
    315 
    316   std::string original_lang("en");
    317   std::string target_lang("fr");
    318   translate_helper_->TranslatePage(
    319       view_->GetPageId(), original_lang, target_lang, std::string());
    320   // While this is running call again TranslatePage with a new target lang.
    321   std::string new_target_lang("de");
    322   translate_helper_->TranslatePage(
    323       view_->GetPageId(), original_lang, new_target_lang, std::string());
    324   base::MessageLoop::current()->RunUntilIdle();
    325 
    326   int page_id;
    327   std::string received_original_lang;
    328   std::string received_target_lang;
    329   TranslateErrors::Type error;
    330   ASSERT_TRUE(GetPageTranslatedMessage(&page_id,
    331                                        &received_original_lang,
    332                                        &received_target_lang,
    333                                        &error));
    334   EXPECT_EQ(view_->GetPageId(), page_id);
    335   EXPECT_EQ(original_lang, received_original_lang);
    336   EXPECT_EQ(new_target_lang, received_target_lang);
    337   EXPECT_EQ(TranslateErrors::NONE, error);
    338 }
    339 
    340 // Tests that we send the right translate language message for a page and that
    341 // we respect the "no translate" meta-tag.
    342 TEST_F(ChromeRenderViewTest, TranslatablePage) {
    343   // Suppress the normal delay that occurs when the page is loaded before which
    344   // the renderer sends the page contents to the browser.
    345   SendContentStateImmediately();
    346 
    347   LoadHTML("<html><body>A random page with random content.</body></html>");
    348   const IPC::Message* message = render_thread_->sink().GetUniqueMessageMatching(
    349       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    350   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    351   ChromeViewHostMsg_TranslateLanguageDetermined::Param params;
    352   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    353   EXPECT_TRUE(params.b) << "Page should be translatable.";
    354   render_thread_->sink().ClearMessages();
    355 
    356   // Now the page specifies the META tag to prevent translation.
    357   LoadHTML("<html><head><meta name=\"google\" value=\"notranslate\"></head>"
    358            "<body>A random page with random content.</body></html>");
    359   message = render_thread_->sink().GetUniqueMessageMatching(
    360       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    361   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    362   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    363   EXPECT_FALSE(params.b) << "Page should not be translatable.";
    364   render_thread_->sink().ClearMessages();
    365 
    366   // Try the alternate version of the META tag (content instead of value).
    367   LoadHTML("<html><head><meta name=\"google\" content=\"notranslate\"></head>"
    368            "<body>A random page with random content.</body></html>");
    369   message = render_thread_->sink().GetUniqueMessageMatching(
    370       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    371   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    372   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    373   EXPECT_FALSE(params.b) << "Page should not be translatable.";
    374 }
    375 
    376 // Tests that the language meta tag takes precedence over the CLD when reporting
    377 // the page's language.
    378 TEST_F(ChromeRenderViewTest, LanguageMetaTag) {
    379   // Suppress the normal delay that occurs when the page is loaded before which
    380   // the renderer sends the page contents to the browser.
    381   SendContentStateImmediately();
    382 
    383   LoadHTML("<html><head><meta http-equiv=\"content-language\" content=\"es\">"
    384            "</head><body>A random page with random content.</body></html>");
    385   const IPC::Message* message = render_thread_->sink().GetUniqueMessageMatching(
    386       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    387   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    388   ChromeViewHostMsg_TranslateLanguageDetermined::Param params;
    389   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    390   EXPECT_EQ("es", params.a.adopted_language);
    391   render_thread_->sink().ClearMessages();
    392 
    393   // Makes sure we support multiple languages specified.
    394   LoadHTML("<html><head><meta http-equiv=\"content-language\" "
    395            "content=\" fr , es,en \">"
    396            "</head><body>A random page with random content.</body></html>");
    397   message = render_thread_->sink().GetUniqueMessageMatching(
    398       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    399   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    400   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    401   EXPECT_EQ("fr", params.a.adopted_language);
    402 }
    403 
    404 // Tests that the language meta tag works even with non-all-lower-case.
    405 // http://code.google.com/p/chromium/issues/detail?id=145689
    406 TEST_F(ChromeRenderViewTest, LanguageMetaTagCase) {
    407   // Suppress the normal delay that occurs when the page is loaded before which
    408   // the renderer sends the page contents to the browser.
    409   SendContentStateImmediately();
    410 
    411   LoadHTML("<html><head><meta http-equiv=\"Content-Language\" content=\"es\">"
    412            "</head><body>A random page with random content.</body></html>");
    413   const IPC::Message* message = render_thread_->sink().GetUniqueMessageMatching(
    414       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    415   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    416   ChromeViewHostMsg_TranslateLanguageDetermined::Param params;
    417   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    418   EXPECT_EQ("es", params.a.adopted_language);
    419   render_thread_->sink().ClearMessages();
    420 
    421   // Makes sure we support multiple languages specified.
    422   LoadHTML("<html><head><meta http-equiv=\"Content-Language\" "
    423            "content=\" fr , es,en \">"
    424            "</head><body>A random page with random content.</body></html>");
    425   message = render_thread_->sink().GetUniqueMessageMatching(
    426       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    427   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    428   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    429   EXPECT_EQ("fr", params.a.adopted_language);
    430 }
    431 
    432 // Tests that the language meta tag is converted to Chrome standard of dashes
    433 // instead of underscores and proper capitalization.
    434 // http://code.google.com/p/chromium/issues/detail?id=159487
    435 TEST_F(ChromeRenderViewTest, LanguageCommonMistakesAreCorrected) {
    436   // Suppress the normal delay that occurs when the page is loaded before which
    437   // the renderer sends the page contents to the browser.
    438   SendContentStateImmediately();
    439 
    440   LoadHTML("<html><head><meta http-equiv='Content-Language' content='EN_us'>"
    441            "</head><body>A random page with random content.</body></html>");
    442   const IPC::Message* message = render_thread_->sink().GetUniqueMessageMatching(
    443       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    444   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    445   ChromeViewHostMsg_TranslateLanguageDetermined::Param params;
    446   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    447   EXPECT_EQ("en-US", params.a.adopted_language);
    448   render_thread_->sink().ClearMessages();
    449 }
    450 
    451 // Tests that a back navigation gets a translate language message.
    452 TEST_F(ChromeRenderViewTest, BackToTranslatablePage) {
    453   SendContentStateImmediately();
    454   LoadHTML("<html><head><meta http-equiv=\"content-language\" content=\"zh\">"
    455            "</head><body>This page is in Chinese.</body></html>");
    456   const IPC::Message* message = render_thread_->sink().GetUniqueMessageMatching(
    457       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    458   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    459   ChromeViewHostMsg_TranslateLanguageDetermined::Param params;
    460   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    461   EXPECT_EQ("zh", params.a.adopted_language);
    462   render_thread_->sink().ClearMessages();
    463 
    464   LoadHTML("<html><head><meta http-equiv=\"content-language\" content=\"fr\">"
    465            "</head><body>This page is in French.</body></html>");
    466   message = render_thread_->sink().GetUniqueMessageMatching(
    467       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    468   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    469   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    470   EXPECT_EQ("fr", params.a.adopted_language);
    471   render_thread_->sink().ClearMessages();
    472 
    473   GoBack(GetMainFrame()->previousHistoryItem());
    474 
    475   message = render_thread_->sink().GetUniqueMessageMatching(
    476       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    477   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    478   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    479   EXPECT_EQ("zh", params.a.adopted_language);
    480   render_thread_->sink().ClearMessages();
    481 }
    482