Home | History | Annotate | Download | only in base
      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 "chrome/test/base/web_ui_browsertest.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/bind.h"
     11 #include "base/bind_helpers.h"
     12 #include "base/lazy_instance.h"
     13 #include "base/memory/ref_counted_memory.h"
     14 #include "base/path_service.h"
     15 #include "base/strings/utf_string_conversions.h"
     16 #include "base/values.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/browser/ui/browser.h"
     19 #include "chrome/browser/ui/browser_commands.h"
     20 #include "chrome/browser/ui/browser_navigator.h"
     21 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     22 #include "chrome/browser/ui/webui/web_ui_test_handler.h"
     23 #include "chrome/common/chrome_paths.h"
     24 #include "chrome/common/url_constants.h"
     25 #include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
     26 #include "chrome/test/base/test_tab_strip_model_observer.h"
     27 #include "chrome/test/base/ui_test_utils.h"
     28 #include "content/public/browser/navigation_controller.h"
     29 #include "content/public/browser/notification_observer.h"
     30 #include "content/public/browser/notification_registrar.h"
     31 #include "content/public/browser/notification_service.h"
     32 #include "content/public/browser/notification_types.h"
     33 #include "content/public/browser/render_view_host_observer.h"
     34 #include "content/public/browser/url_data_source.h"
     35 #include "content/public/browser/web_contents.h"
     36 #include "content/public/browser/web_ui_controller.h"
     37 #include "content/public/browser/web_ui_message_handler.h"
     38 #include "net/base/net_util.h"
     39 #include "testing/gmock/include/gmock/gmock.h"
     40 #include "testing/gtest/include/gtest/gtest-spi.h"
     41 #include "ui/base/resource/resource_bundle.h"
     42 #include "ui/base/resource/resource_handle.h"
     43 
     44 #if defined(ENABLE_FULL_PRINTING)
     45 #include "chrome/browser/printing/print_preview_dialog_controller.h"
     46 #endif
     47 
     48 using content::NavigationController;
     49 using content::RenderViewHost;
     50 using content::WebContents;
     51 using content::WebUIController;
     52 using content::WebUIMessageHandler;
     53 
     54 namespace {
     55 
     56 const base::FilePath::CharType kA11yAuditLibraryJSPath[] = FILE_PATH_LITERAL(
     57     "third_party/accessibility-developer-tools/gen/axs_testing.js");
     58 const base::FilePath::CharType kMockJSPath[] =
     59     FILE_PATH_LITERAL("chrome/third_party/mock4js/mock4js.js");
     60 const base::FilePath::CharType kWebUILibraryJS[] =
     61     FILE_PATH_LITERAL("test_api.js");
     62 const base::FilePath::CharType kWebUITestFolder[] = FILE_PATH_LITERAL("webui");
     63 base::LazyInstance<std::vector<std::string> > error_messages_ =
     64     LAZY_INSTANCE_INITIALIZER;
     65 
     66 // Intercepts all log messages.
     67 bool LogHandler(int severity,
     68                 const char* file,
     69                 int line,
     70                 size_t message_start,
     71                 const std::string& str) {
     72   if (severity == logging::LOG_ERROR &&
     73       file &&
     74       std::string("CONSOLE") == file) {
     75     error_messages_.Get().push_back(str);
     76   }
     77 
     78   return false;
     79 }
     80 
     81 class RenderViewHostInitializedObserver
     82     : public content::RenderViewHostObserver {
     83  public:
     84   RenderViewHostInitializedObserver(content::RenderViewHost* render_view_host,
     85                                     content::JsInjectionReadyObserver* observer)
     86       : content::RenderViewHostObserver(render_view_host),
     87         injection_observer_(observer) {
     88   }
     89 
     90   // content::RenderViewHostObserver:
     91   virtual void RenderViewHostInitialized() OVERRIDE {
     92     injection_observer_->OnJsInjectionReady(render_view_host());
     93   }
     94 
     95  private:
     96   content::JsInjectionReadyObserver* injection_observer_;
     97 
     98   DISALLOW_COPY_AND_ASSIGN(RenderViewHostInitializedObserver);
     99 };
    100 
    101 class WebUIJsInjectionReadyObserver {
    102  public:
    103   explicit WebUIJsInjectionReadyObserver(
    104       content::JsInjectionReadyObserver* observer)
    105       : injection_observer_(observer),
    106         rvh_callback_(
    107             base::Bind(&WebUIJsInjectionReadyObserver::RenderViewHostCreated,
    108                        base::Unretained(this))) {
    109     content::RenderViewHost::AddCreatedCallback(rvh_callback_);
    110   }
    111 
    112   ~WebUIJsInjectionReadyObserver() {
    113     content::RenderViewHost::RemoveCreatedCallback(rvh_callback_);
    114   }
    115 
    116  private:
    117   void RenderViewHostCreated(content::RenderViewHost* rvh) {
    118     rvh_observer_.reset(
    119         new RenderViewHostInitializedObserver(rvh, injection_observer_));
    120   }
    121 
    122   content::JsInjectionReadyObserver* injection_observer_;
    123 
    124   scoped_ptr<RenderViewHostInitializedObserver> rvh_observer_;
    125 
    126   content::RenderViewHost::CreatedCallback rvh_callback_;
    127 
    128   DISALLOW_COPY_AND_ASSIGN(WebUIJsInjectionReadyObserver);
    129 };
    130 
    131 }  // namespace
    132 
    133 WebUIBrowserTest::~WebUIBrowserTest() {}
    134 
    135 void WebUIBrowserTest::AddLibrary(const base::FilePath& library_path) {
    136   user_libraries_.push_back(library_path);
    137 }
    138 
    139 // Add a helper JS library to the given WebUIBrowserTest from a path relative to
    140 // base::DIR_SOURCE_ROOT.
    141 // static
    142 void AddLibraryFromSourceRoot(WebUIBrowserTest* browser_test,
    143                               const base::FilePath& path) {
    144   base::FilePath filePath;
    145   ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &filePath));
    146   filePath = filePath.Append(path);
    147   browser_test->AddLibrary(filePath);
    148 }
    149 
    150 bool WebUIBrowserTest::RunJavascriptFunction(const std::string& function_name) {
    151   ConstValueVector empty_args;
    152   return RunJavascriptFunction(function_name, empty_args);
    153 }
    154 
    155 bool WebUIBrowserTest::RunJavascriptFunction(const std::string& function_name,
    156                                              base::Value* arg) {
    157   ConstValueVector args;
    158   args.push_back(arg);
    159   return RunJavascriptFunction(function_name, args);
    160 }
    161 
    162 bool WebUIBrowserTest::RunJavascriptFunction(const std::string& function_name,
    163                                              base::Value* arg1,
    164                                              base::Value* arg2) {
    165   ConstValueVector args;
    166   args.push_back(arg1);
    167   args.push_back(arg2);
    168   return RunJavascriptFunction(function_name, args);
    169 }
    170 
    171 bool WebUIBrowserTest::RunJavascriptFunction(
    172     const std::string& function_name,
    173     const ConstValueVector& function_arguments) {
    174   return RunJavascriptUsingHandler(
    175       function_name, function_arguments, false, false, NULL);
    176 }
    177 
    178 bool WebUIBrowserTest::RunJavascriptTestF(bool is_async,
    179                                           const std::string& test_fixture,
    180                                           const std::string& test_name) {
    181   ConstValueVector args;
    182   args.push_back(new base::StringValue(test_fixture));
    183   args.push_back(new base::StringValue(test_name));
    184 
    185   if (is_async)
    186     return RunJavascriptAsyncTest("RUN_TEST_F", args);
    187   else
    188     return RunJavascriptTest("RUN_TEST_F", args);
    189 }
    190 
    191 bool WebUIBrowserTest::RunJavascriptTest(const std::string& test_name) {
    192   ConstValueVector empty_args;
    193   return RunJavascriptTest(test_name, empty_args);
    194 }
    195 
    196 bool WebUIBrowserTest::RunJavascriptTest(const std::string& test_name,
    197                                          base::Value* arg) {
    198   ConstValueVector args;
    199   args.push_back(arg);
    200   return RunJavascriptTest(test_name, args);
    201 }
    202 
    203 bool WebUIBrowserTest::RunJavascriptTest(const std::string& test_name,
    204                                          base::Value* arg1,
    205                                          base::Value* arg2) {
    206   ConstValueVector args;
    207   args.push_back(arg1);
    208   args.push_back(arg2);
    209   return RunJavascriptTest(test_name, args);
    210 }
    211 
    212 bool WebUIBrowserTest::RunJavascriptTest(
    213     const std::string& test_name,
    214     const ConstValueVector& test_arguments) {
    215   return RunJavascriptUsingHandler(
    216       test_name, test_arguments, true, false, NULL);
    217 }
    218 
    219 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name) {
    220   ConstValueVector empty_args;
    221   return RunJavascriptAsyncTest(test_name, empty_args);
    222 }
    223 
    224 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name,
    225                                               base::Value* arg) {
    226   ConstValueVector args;
    227   args.push_back(arg);
    228   return RunJavascriptAsyncTest(test_name, args);
    229 }
    230 
    231 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name,
    232                                               base::Value* arg1,
    233                                               base::Value* arg2) {
    234   ConstValueVector args;
    235   args.push_back(arg1);
    236   args.push_back(arg2);
    237   return RunJavascriptAsyncTest(test_name, args);
    238 }
    239 
    240 bool WebUIBrowserTest::RunJavascriptAsyncTest(const std::string& test_name,
    241                                               base::Value* arg1,
    242                                               base::Value* arg2,
    243                                               base::Value* arg3) {
    244   ConstValueVector args;
    245   args.push_back(arg1);
    246   args.push_back(arg2);
    247   args.push_back(arg3);
    248   return RunJavascriptAsyncTest(test_name, args);
    249 }
    250 
    251 bool WebUIBrowserTest::RunJavascriptAsyncTest(
    252     const std::string& test_name,
    253     const ConstValueVector& test_arguments) {
    254   return RunJavascriptUsingHandler(test_name, test_arguments, true, true, NULL);
    255 }
    256 
    257 void WebUIBrowserTest::PreLoadJavascriptLibraries(
    258     const std::string& preload_test_fixture,
    259     const std::string& preload_test_name,
    260     RenderViewHost* preload_host) {
    261   ASSERT_FALSE(libraries_preloaded_);
    262   ConstValueVector args;
    263   args.push_back(new base::StringValue(preload_test_fixture));
    264   args.push_back(new base::StringValue(preload_test_name));
    265   RunJavascriptUsingHandler(
    266       "preloadJavascriptLibraries", args, false, false, preload_host);
    267   libraries_preloaded_ = true;
    268 }
    269 
    270 void WebUIBrowserTest::BrowsePreload(const GURL& browse_to) {
    271   WebUIJsInjectionReadyObserver injection_observer(this);
    272   content::TestNavigationObserver navigation_observer(
    273       browser()->tab_strip_model()->GetActiveWebContents());
    274   chrome::NavigateParams params(browser(), GURL(browse_to),
    275                                 content::PAGE_TRANSITION_TYPED);
    276   params.disposition = CURRENT_TAB;
    277   chrome::Navigate(&params);
    278   navigation_observer.WaitForObservation(
    279       base::Bind(&content::RunMessageLoop),
    280       base::Bind(&base::MessageLoop::Quit,
    281                  base::Unretained(base::MessageLoopForUI::current())));
    282 }
    283 
    284 void WebUIBrowserTest::BrowsePrintPreload(const GURL& browse_to) {
    285 #if defined(ENABLE_FULL_PRINTING)
    286   ui_test_utils::NavigateToURL(browser(), browse_to);
    287 
    288   TestTabStripModelObserver tabstrip_observer(
    289       browser()->tab_strip_model(), this);
    290   chrome::Print(browser());
    291   tabstrip_observer.WaitForObservation(
    292       base::Bind(&content::RunMessageLoop),
    293       base::Bind(&base::MessageLoop::Quit,
    294                  base::Unretained(base::MessageLoopForUI::current())));
    295 
    296   printing::PrintPreviewDialogController* tab_controller =
    297       printing::PrintPreviewDialogController::GetInstance();
    298   ASSERT_TRUE(tab_controller);
    299   WebContents* preview_dialog = tab_controller->GetPrintPreviewForContents(
    300       browser()->tab_strip_model()->GetActiveWebContents());
    301   ASSERT_TRUE(preview_dialog);
    302   SetWebUIInstance(preview_dialog->GetWebUI());
    303 #else
    304   NOTREACHED();
    305 #endif
    306 }
    307 
    308 const char WebUIBrowserTest::kDummyURL[] = "chrome://DummyURL";
    309 
    310 WebUIBrowserTest::WebUIBrowserTest()
    311     : test_handler_(new WebUITestHandler()),
    312       libraries_preloaded_(false),
    313       override_selected_web_ui_(NULL) {}
    314 
    315 void WebUIBrowserTest::set_preload_test_fixture(
    316     const std::string& preload_test_fixture) {
    317   preload_test_fixture_ = preload_test_fixture;
    318 }
    319 
    320 void WebUIBrowserTest::set_preload_test_name(
    321     const std::string& preload_test_name) {
    322   preload_test_name_ = preload_test_name;
    323 }
    324 
    325 namespace {
    326 
    327 // DataSource for the dummy URL.  If no data source is provided then an error
    328 // page is shown. While this doesn't matter for most tests, without it,
    329 // navigation to different anchors cannot be listened to (via the hashchange
    330 // event).
    331 class MockWebUIDataSource : public content::URLDataSource {
    332  public:
    333   MockWebUIDataSource() {}
    334 
    335  private:
    336   virtual ~MockWebUIDataSource() {}
    337 
    338   virtual std::string GetSource() const OVERRIDE {
    339     return "dummyurl";
    340   }
    341 
    342   virtual void StartDataRequest(
    343       const std::string& path,
    344       int render_process_id,
    345       int render_view_id,
    346       const content::URLDataSource::GotDataCallback& callback) OVERRIDE {
    347     std::string dummy_html = "<html><body>Dummy</body></html>";
    348     scoped_refptr<base::RefCountedString> response =
    349         base::RefCountedString::TakeString(&dummy_html);
    350     callback.Run(response.get());
    351   }
    352 
    353   virtual std::string GetMimeType(const std::string& path) const OVERRIDE {
    354     return "text/html";
    355   }
    356 
    357   DISALLOW_COPY_AND_ASSIGN(MockWebUIDataSource);
    358 };
    359 
    360 // WebUIProvider to allow attaching the DataSource for the dummy URL when
    361 // testing.
    362 class MockWebUIProvider
    363     : public TestChromeWebUIControllerFactory::WebUIProvider {
    364  public:
    365   MockWebUIProvider() {}
    366 
    367   // Returns a new WebUI
    368   virtual WebUIController* NewWebUI(content::WebUI* web_ui,
    369                                     const GURL& url) OVERRIDE {
    370     WebUIController* controller = new content::WebUIController(web_ui);
    371     Profile* profile = Profile::FromWebUI(web_ui);
    372     content::URLDataSource::Add(profile, new MockWebUIDataSource());
    373     return controller;
    374   }
    375 
    376  private:
    377   DISALLOW_COPY_AND_ASSIGN(MockWebUIProvider);
    378 };
    379 
    380 base::LazyInstance<MockWebUIProvider> mock_provider_ =
    381     LAZY_INSTANCE_INITIALIZER;
    382 
    383 }  // namespace
    384 
    385 void WebUIBrowserTest::SetUpOnMainThread() {
    386   logging::SetLogMessageHandler(&LogHandler);
    387 
    388   content::WebUIControllerFactory::UnregisterFactoryForTesting(
    389       ChromeWebUIControllerFactory::GetInstance());
    390 
    391   test_factory_.reset(new TestChromeWebUIControllerFactory);
    392 
    393   content::WebUIControllerFactory::RegisterFactory(test_factory_.get());
    394 
    395   test_factory_->AddFactoryOverride(
    396       GURL(kDummyURL).host(), mock_provider_.Pointer());
    397 
    398   ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory_));
    399   test_data_directory_ = test_data_directory_.Append(kWebUITestFolder);
    400   ASSERT_TRUE(PathService::Get(chrome::DIR_GEN_TEST_DATA,
    401                                &gen_test_data_directory_));
    402 
    403   // TODO(dtseng): should this be part of every BrowserTest or just WebUI test.
    404   base::FilePath resources_pack_path;
    405   PathService::Get(chrome::FILE_RESOURCES_PACK, &resources_pack_path);
    406   ResourceBundle::GetSharedInstance().AddDataPackFromPath(
    407       resources_pack_path, ui::SCALE_FACTOR_NONE);
    408 
    409   AddLibraryFromSourceRoot(this, base::FilePath(kA11yAuditLibraryJSPath));
    410   AddLibraryFromSourceRoot(this, base::FilePath(kMockJSPath));
    411   AddLibrary(base::FilePath(kWebUILibraryJS));
    412 }
    413 
    414 void WebUIBrowserTest::CleanUpOnMainThread() {
    415   logging::SetLogMessageHandler(NULL);
    416 
    417   test_factory_->RemoveFactoryOverride(GURL(kDummyURL).host());
    418   content::WebUIControllerFactory::UnregisterFactoryForTesting(
    419       test_factory_.get());
    420 
    421   // This is needed to avoid a debug assert after the test completes, see stack
    422   // trace in http://crrev.com/179347
    423   content::WebUIControllerFactory::RegisterFactory(
    424       ChromeWebUIControllerFactory::GetInstance());
    425 
    426   test_factory_.reset();
    427 }
    428 
    429 void WebUIBrowserTest::SetWebUIInstance(content::WebUI* web_ui) {
    430   override_selected_web_ui_ = web_ui;
    431 }
    432 
    433 WebUIMessageHandler* WebUIBrowserTest::GetMockMessageHandler() {
    434   return NULL;
    435 }
    436 
    437 GURL WebUIBrowserTest::WebUITestDataPathToURL(
    438     const base::FilePath::StringType& path) {
    439   base::FilePath dir_test_data;
    440   EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &dir_test_data));
    441   base::FilePath test_path(dir_test_data.Append(kWebUITestFolder).Append(path));
    442   EXPECT_TRUE(base::PathExists(test_path));
    443   return net::FilePathToFileURL(test_path);
    444 }
    445 
    446 void WebUIBrowserTest::OnJsInjectionReady(RenderViewHost* render_view_host) {
    447   PreLoadJavascriptLibraries(preload_test_fixture_, preload_test_name_,
    448                              render_view_host);
    449 }
    450 
    451 void WebUIBrowserTest::BuildJavascriptLibraries(string16* content) {
    452   ASSERT_TRUE(content != NULL);
    453   std::string utf8_content;
    454   std::vector<base::FilePath>::iterator user_libraries_iterator;
    455   for (user_libraries_iterator = user_libraries_.begin();
    456        user_libraries_iterator != user_libraries_.end();
    457        ++user_libraries_iterator) {
    458     std::string library_content;
    459     if (user_libraries_iterator->IsAbsolute()) {
    460       ASSERT_TRUE(file_util::ReadFileToString(*user_libraries_iterator,
    461                                               &library_content))
    462           << user_libraries_iterator->value();
    463     } else {
    464       bool ok = file_util::ReadFileToString(
    465           gen_test_data_directory_.Append(*user_libraries_iterator),
    466           &library_content);
    467       if (!ok) {
    468         ok = file_util::ReadFileToString(
    469             test_data_directory_.Append(*user_libraries_iterator),
    470             &library_content);
    471       }
    472       ASSERT_TRUE(ok) << user_libraries_iterator->value();
    473     }
    474     utf8_content.append(library_content);
    475     utf8_content.append(";\n");
    476   }
    477   content->append(UTF8ToUTF16(utf8_content));
    478 }
    479 
    480 string16 WebUIBrowserTest::BuildRunTestJSCall(
    481     bool is_async,
    482     const std::string& function_name,
    483     const WebUIBrowserTest::ConstValueVector& test_func_args) {
    484   ConstValueVector arguments;
    485   base::FundamentalValue* is_async_arg = new base::FundamentalValue(is_async);
    486   arguments.push_back(is_async_arg);
    487   base::StringValue* function_name_arg = new base::StringValue(function_name);
    488   arguments.push_back(function_name_arg);
    489   base::ListValue* baked_argument_list = new base::ListValue();
    490   ConstValueVector::const_iterator arguments_iterator;
    491   for (arguments_iterator = test_func_args.begin();
    492        arguments_iterator != test_func_args.end();
    493        ++arguments_iterator) {
    494     baked_argument_list->Append((*arguments_iterator)->DeepCopy());
    495   }
    496   arguments.push_back(baked_argument_list);
    497   return content::WebUI::GetJavascriptCall(std::string("runTest"),
    498                                            arguments.get());
    499 }
    500 
    501 bool WebUIBrowserTest::RunJavascriptUsingHandler(
    502     const std::string& function_name,
    503     const ConstValueVector& function_arguments,
    504     bool is_test,
    505     bool is_async,
    506     RenderViewHost* preload_host) {
    507 
    508   string16 content;
    509   if (!libraries_preloaded_)
    510     BuildJavascriptLibraries(&content);
    511 
    512   if (!function_name.empty()) {
    513     string16 called_function;
    514     if (is_test) {
    515       called_function =
    516           BuildRunTestJSCall(is_async, function_name, function_arguments);
    517     } else {
    518       called_function =
    519           content::WebUI::GetJavascriptCall(function_name,
    520                                             function_arguments.get());
    521     }
    522     content.append(called_function);
    523   }
    524 
    525   if (!preload_host)
    526     SetupHandlers();
    527 
    528   bool result = true;
    529 
    530   if (is_test)
    531     result = test_handler_->RunJavaScriptTestWithResult(content);
    532   else if (preload_host)
    533     test_handler_->PreloadJavaScript(content, preload_host);
    534   else
    535     test_handler_->RunJavaScript(content);
    536 
    537   if (error_messages_.Get().size() > 0) {
    538     LOG(ERROR) << "Encountered javascript console error(s)";
    539     result = false;
    540     error_messages_.Get().clear();
    541   }
    542   return result;
    543 }
    544 
    545 void WebUIBrowserTest::SetupHandlers() {
    546   content::WebUI* web_ui_instance = override_selected_web_ui_ ?
    547       override_selected_web_ui_ :
    548       browser()->tab_strip_model()->GetActiveWebContents()->GetWebUI();
    549   ASSERT_TRUE(web_ui_instance != NULL);
    550 
    551   test_handler_->set_web_ui(web_ui_instance);
    552   test_handler_->RegisterMessages();
    553 
    554   if (GetMockMessageHandler()) {
    555     GetMockMessageHandler()->set_web_ui(web_ui_instance);
    556     GetMockMessageHandler()->RegisterMessages();
    557   }
    558 }
    559 
    560 // According to the interface for EXPECT_FATAL_FAILURE
    561 // (http://code.google.com/p/googletest/wiki/AdvancedGuide#Catching_Failures)
    562 // the statement must be statically available. Therefore, we make a static
    563 // global s_test_ which should point to |this| for the duration of the test run
    564 // and be cleared afterward.
    565 class WebUIBrowserExpectFailTest : public WebUIBrowserTest {
    566  public:
    567   WebUIBrowserExpectFailTest() {
    568     EXPECT_FALSE(s_test_);
    569     s_test_ = this;
    570   }
    571 
    572  protected:
    573   virtual ~WebUIBrowserExpectFailTest() {
    574     EXPECT_TRUE(s_test_);
    575     s_test_ = NULL;
    576   }
    577 
    578   static void RunJavascriptTestNoReturn(const std::string& testname) {
    579     EXPECT_TRUE(s_test_);
    580     s_test_->RunJavascriptTest(testname);
    581   }
    582 
    583   static void RunJavascriptAsyncTestNoReturn(const std::string& testname) {
    584     EXPECT_TRUE(s_test_);
    585     s_test_->RunJavascriptAsyncTest(testname);
    586   }
    587 
    588  private:
    589   static WebUIBrowserTest* s_test_;
    590 };
    591 
    592 WebUIBrowserTest* WebUIBrowserExpectFailTest::s_test_ = NULL;
    593 
    594 // Test that bogus javascript fails fast - no timeout waiting for result.
    595 IN_PROC_BROWSER_TEST_F(WebUIBrowserExpectFailTest, TestFailsFast) {
    596   AddLibrary(base::FilePath(FILE_PATH_LITERAL("sample_downloads.js")));
    597   ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIDownloadsURL));
    598   EXPECT_FATAL_FAILURE(RunJavascriptTestNoReturn("DISABLED_BogusFunctionName"),
    599                        "WebUITestHandler::JavaScriptComplete");
    600 }
    601 
    602 // Test that bogus javascript fails fast - no timeout waiting for result.
    603 IN_PROC_BROWSER_TEST_F(WebUIBrowserExpectFailTest, TestRuntimeErrorFailsFast) {
    604   AddLibrary(base::FilePath(FILE_PATH_LITERAL("runtime_error.js")));
    605   ui_test_utils::NavigateToURL(browser(), GURL(kDummyURL));
    606   EXPECT_FATAL_FAILURE(RunJavascriptTestNoReturn("TestRuntimeErrorFailsFast"),
    607                        "WebUITestHandler::JavaScriptComplete");
    608 }
    609 
    610 // Test that bogus javascript fails async test fast as well - no timeout waiting
    611 // for result.
    612 IN_PROC_BROWSER_TEST_F(WebUIBrowserExpectFailTest, TestFailsAsyncFast) {
    613   AddLibrary(base::FilePath(FILE_PATH_LITERAL("sample_downloads.js")));
    614   ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIDownloadsURL));
    615   EXPECT_FATAL_FAILURE(
    616       RunJavascriptAsyncTestNoReturn("DISABLED_BogusFunctionName"),
    617       "WebUITestHandler::JavaScriptComplete");
    618 }
    619 
    620 // Tests that the async framework works.
    621 class WebUIBrowserAsyncTest : public WebUIBrowserTest {
    622  public:
    623   // Calls the testDone() function from test_api.js
    624   void TestDone() {
    625     RunJavascriptFunction("testDone");
    626   }
    627 
    628   // Starts a failing test.
    629   void RunTestFailsAssert() {
    630     RunJavascriptFunction("runAsync", new base::StringValue("testFailsAssert"));
    631   }
    632 
    633   // Starts a passing test.
    634   void RunTestPasses() {
    635     RunJavascriptFunction("runAsync", new base::StringValue("testPasses"));
    636   }
    637 
    638  protected:
    639   WebUIBrowserAsyncTest() {}
    640 
    641   // Class to synchronize asynchronous javascript activity with the tests.
    642   class AsyncWebUIMessageHandler : public WebUIMessageHandler {
    643    public:
    644     AsyncWebUIMessageHandler() {}
    645 
    646     MOCK_METHOD1(HandleTestContinues, void(const base::ListValue*));
    647     MOCK_METHOD1(HandleTestFails, void(const base::ListValue*));
    648     MOCK_METHOD1(HandleTestPasses, void(const base::ListValue*));
    649 
    650    private:
    651     virtual void RegisterMessages() OVERRIDE {
    652       web_ui()->RegisterMessageCallback("startAsyncTest",
    653           base::Bind(&AsyncWebUIMessageHandler::HandleStartAsyncTest,
    654                      base::Unretained(this)));
    655       web_ui()->RegisterMessageCallback("testContinues",
    656           base::Bind(&AsyncWebUIMessageHandler::HandleTestContinues,
    657                      base::Unretained(this)));
    658       web_ui()->RegisterMessageCallback("testFails",
    659           base::Bind(&AsyncWebUIMessageHandler::HandleTestFails,
    660                      base::Unretained(this)));
    661       web_ui()->RegisterMessageCallback("testPasses",
    662           base::Bind(&AsyncWebUIMessageHandler::HandleTestPasses,
    663                      base::Unretained(this)));
    664     }
    665 
    666     // Starts the test in |list_value|[0] with the runAsync wrapper.
    667     void HandleStartAsyncTest(const base::ListValue* list_value) {
    668       const base::Value* test_name;
    669       ASSERT_TRUE(list_value->Get(0, &test_name));
    670       web_ui()->CallJavascriptFunction("runAsync", *test_name);
    671     }
    672 
    673     DISALLOW_COPY_AND_ASSIGN(AsyncWebUIMessageHandler);
    674   };
    675 
    676   // Handler for this object.
    677   ::testing::StrictMock<AsyncWebUIMessageHandler> message_handler_;
    678 
    679  private:
    680   // Provide this object's handler.
    681   virtual WebUIMessageHandler* GetMockMessageHandler() OVERRIDE {
    682     return &message_handler_;
    683   }
    684 
    685   // Set up and browse to kDummyURL for all tests.
    686   virtual void SetUpOnMainThread() OVERRIDE {
    687     WebUIBrowserTest::SetUpOnMainThread();
    688     AddLibrary(base::FilePath(FILE_PATH_LITERAL("async.js")));
    689     ui_test_utils::NavigateToURL(browser(), GURL(kDummyURL));
    690   }
    691 
    692   DISALLOW_COPY_AND_ASSIGN(WebUIBrowserAsyncTest);
    693 };
    694 
    695 // Test that assertions fail immediately after assertion fails (no testContinues
    696 // message). (Sync version).
    697 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestSyncOkTestFail) {
    698   ASSERT_FALSE(RunJavascriptTest("testFailsAssert"));
    699 }
    700 
    701 // Test that assertions fail immediately after assertion fails (no testContinues
    702 // message). (Async version).
    703 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestAsyncFailsAssert) {
    704   EXPECT_CALL(message_handler_, HandleTestFails(::testing::_));
    705   ASSERT_FALSE(RunJavascriptAsyncTest(
    706       "startAsyncTest", new base::StringValue("testFailsAssert")));
    707 }
    708 
    709 // Test that expectations continue the function, but fail the test.
    710 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestAsyncFailsExpect) {
    711   ::testing::InSequence s;
    712   EXPECT_CALL(message_handler_, HandleTestContinues(::testing::_));
    713   EXPECT_CALL(message_handler_, HandleTestFails(::testing::_));
    714   ASSERT_FALSE(RunJavascriptAsyncTest(
    715       "startAsyncTest", new base::StringValue("testFailsExpect")));
    716 }
    717 
    718 // Test that test continues and passes. (Sync version).
    719 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestSyncPasses) {
    720   EXPECT_CALL(message_handler_, HandleTestContinues(::testing::_));
    721   ASSERT_TRUE(RunJavascriptTest("testPasses"));
    722 }
    723 
    724 // Test that test continues and passes. (Async version).
    725 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestAsyncPasses) {
    726   ::testing::InSequence s;
    727   EXPECT_CALL(message_handler_, HandleTestContinues(::testing::_));
    728   EXPECT_CALL(message_handler_, HandleTestPasses(::testing::_))
    729       .WillOnce(::testing::InvokeWithoutArgs(
    730           this, &WebUIBrowserAsyncTest::TestDone));
    731   ASSERT_TRUE(RunJavascriptAsyncTest(
    732       "startAsyncTest", new base::StringValue("testPasses")));
    733 }
    734 
    735 // Test that two tests pass.
    736 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestAsyncPassPass) {
    737   ::testing::InSequence s;
    738   EXPECT_CALL(message_handler_, HandleTestContinues(::testing::_));
    739   EXPECT_CALL(message_handler_, HandleTestPasses(::testing::_))
    740       .WillOnce(::testing::InvokeWithoutArgs(
    741           this, &WebUIBrowserAsyncTest::RunTestPasses));
    742   EXPECT_CALL(message_handler_, HandleTestContinues(::testing::_));
    743   EXPECT_CALL(message_handler_, HandleTestPasses(::testing::_))
    744       .WillOnce(::testing::InvokeWithoutArgs(
    745           this, &WebUIBrowserAsyncTest::TestDone));
    746   ASSERT_TRUE(RunJavascriptAsyncTest(
    747       "startAsyncTest", new base::StringValue("testPasses")));
    748 }
    749 
    750 // Test that first test passes; second fails.
    751 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestAsyncPassThenFail) {
    752   ::testing::InSequence s;
    753   EXPECT_CALL(message_handler_, HandleTestContinues(::testing::_));
    754   EXPECT_CALL(message_handler_, HandleTestPasses(::testing::_))
    755       .WillOnce(::testing::InvokeWithoutArgs(
    756           this, &WebUIBrowserAsyncTest::RunTestFailsAssert));
    757   EXPECT_CALL(message_handler_, HandleTestFails(::testing::_));
    758   ASSERT_FALSE(RunJavascriptAsyncTest(
    759       "startAsyncTest", new base::StringValue("testPasses")));
    760 }
    761 
    762 // Test that testDone() with failure first then sync pass still fails.
    763 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestAsyncDoneFailFirstSyncPass) {
    764   ::testing::InSequence s;
    765   EXPECT_CALL(message_handler_, HandleTestContinues(::testing::_));
    766   EXPECT_CALL(message_handler_, HandleTestFails(::testing::_));
    767 
    768   // Call runAsync directly instead of deferring through startAsyncTest. It will
    769   // call testDone() on failure, then return.
    770   ASSERT_FALSE(RunJavascriptAsyncTest(
    771       "runAsync", new base::StringValue("testAsyncDoneFailFirstSyncPass")));
    772 }
    773 
    774 // Test that calling testDone during RunJavascriptAsyncTest still completes
    775 // when waiting for async result. This is similar to the previous test, but call
    776 // testDone directly and expect pass result.
    777 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestTestDoneEarlyPassesAsync) {
    778   ASSERT_TRUE(RunJavascriptAsyncTest("testDone"));
    779 }
    780 
    781 // Test that calling testDone during RunJavascriptTest still completes when
    782 // waiting for async result.
    783 IN_PROC_BROWSER_TEST_F(WebUIBrowserAsyncTest, TestTestDoneEarlyPasses) {
    784   ASSERT_TRUE(RunJavascriptTest("testDone"));
    785 }
    786