Home | History | Annotate | Download | only in browser
      1 // Copyright 2013 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 "content/shell/browser/shell_browser_main.h"
      6 
      7 #include <iostream>
      8 
      9 #include "base/command_line.h"
     10 #include "base/files/file_path.h"
     11 #include "base/files/file_util.h"
     12 #include "base/files/scoped_temp_dir.h"
     13 #include "base/logging.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/message_loop/message_loop.h"
     16 #include "base/strings/sys_string_conversions.h"
     17 #include "base/strings/utf_string_conversions.h"
     18 #include "base/threading/thread_restrictions.h"
     19 #include "content/public/browser/browser_main_runner.h"
     20 #include "content/public/common/url_constants.h"
     21 #include "content/shell/browser/shell.h"
     22 #include "content/shell/browser/webkit_test_controller.h"
     23 #include "content/shell/common/shell_switches.h"
     24 #include "content/shell/common/webkit_test_helpers.h"
     25 #include "net/base/filename_util.h"
     26 
     27 #if defined(OS_ANDROID)
     28 #include "base/run_loop.h"
     29 #include "content/shell/browser/shell_layout_tests_android.h"
     30 #endif
     31 
     32 namespace {
     33 
     34 GURL GetURLForLayoutTest(const std::string& test_name,
     35                          base::FilePath* current_working_directory,
     36                          bool* enable_pixel_dumping,
     37                          std::string* expected_pixel_hash) {
     38   // A test name is formated like file:///path/to/test'--pixel-test'pixelhash
     39   std::string path_or_url = test_name;
     40   std::string pixel_switch;
     41   std::string pixel_hash;
     42   std::string::size_type separator_position = path_or_url.find('\'');
     43   if (separator_position != std::string::npos) {
     44     pixel_switch = path_or_url.substr(separator_position + 1);
     45     path_or_url.erase(separator_position);
     46   }
     47   separator_position = pixel_switch.find('\'');
     48   if (separator_position != std::string::npos) {
     49     pixel_hash = pixel_switch.substr(separator_position + 1);
     50     pixel_switch.erase(separator_position);
     51   }
     52   if (enable_pixel_dumping) {
     53     *enable_pixel_dumping =
     54         (pixel_switch == "--pixel-test" || pixel_switch == "-p");
     55   }
     56   if (expected_pixel_hash)
     57     *expected_pixel_hash = pixel_hash;
     58 
     59   GURL test_url;
     60 #if defined(OS_ANDROID)
     61   if (content::GetTestUrlForAndroid(path_or_url, &test_url))
     62     return test_url;
     63 #endif
     64 
     65   test_url = GURL(path_or_url);
     66   if (!(test_url.is_valid() && test_url.has_scheme())) {
     67     // We're outside of the message loop here, and this is a test.
     68     base::ThreadRestrictions::ScopedAllowIO allow_io;
     69 #if defined(OS_WIN)
     70     std::wstring wide_path_or_url =
     71         base::SysNativeMBToWide(path_or_url);
     72     base::FilePath local_file(wide_path_or_url);
     73 #else
     74     base::FilePath local_file(path_or_url);
     75 #endif
     76     if (!base::PathExists(local_file)) {
     77       local_file = content::GetWebKitRootDirFilePath()
     78           .Append(FILE_PATH_LITERAL("LayoutTests")).Append(local_file);
     79     }
     80     test_url = net::FilePathToFileURL(base::MakeAbsoluteFilePath(local_file));
     81   }
     82   base::FilePath local_path;
     83   if (current_working_directory) {
     84     // We're outside of the message loop here, and this is a test.
     85     base::ThreadRestrictions::ScopedAllowIO allow_io;
     86     if (net::FileURLToFilePath(test_url, &local_path))
     87       *current_working_directory = local_path.DirName();
     88     else
     89       base::GetCurrentDirectory(current_working_directory);
     90   }
     91   return test_url;
     92 }
     93 
     94 bool GetNextTest(const CommandLine::StringVector& args,
     95                  size_t* position,
     96                  std::string* test) {
     97   if (*position >= args.size())
     98     return false;
     99   if (args[*position] == FILE_PATH_LITERAL("-"))
    100     return !!std::getline(std::cin, *test, '\n');
    101 #if defined(OS_WIN)
    102   *test = base::WideToUTF8(args[(*position)++]);
    103 #else
    104   *test = args[(*position)++];
    105 #endif
    106   return true;
    107 }
    108 
    109 bool RunOneTest(const std::string& test_string,
    110                 bool* ran_at_least_once,
    111                 const scoped_ptr<content::BrowserMainRunner>& main_runner) {
    112   if (test_string.empty())
    113     return true;
    114   if (test_string == "QUIT")
    115     return false;
    116 
    117   bool enable_pixel_dumps;
    118   std::string pixel_hash;
    119   base::FilePath cwd;
    120   GURL test_url = GetURLForLayoutTest(
    121       test_string, &cwd, &enable_pixel_dumps, &pixel_hash);
    122   if (!content::WebKitTestController::Get()->PrepareForLayoutTest(
    123           test_url, cwd, enable_pixel_dumps, pixel_hash)) {
    124     return false;
    125   }
    126 
    127   *ran_at_least_once = true;
    128 #if defined(OS_ANDROID)
    129   // The message loop on Android is provided by the system, and does not
    130   // offer a blocking Run() method. For layout tests, use a nested loop
    131   // together with a base::RunLoop so it can block until a QuitClosure.
    132   base::RunLoop run_loop;
    133   run_loop.Run();
    134 #else
    135   main_runner->Run();
    136 #endif
    137 
    138   if (!content::WebKitTestController::Get()->ResetAfterLayoutTest())
    139     return false;
    140 
    141 #if defined(OS_ANDROID)
    142   // There will be left-over tasks in the queue for Android because the
    143   // main window is being destroyed. Run them before starting the next test.
    144   base::MessageLoop::current()->RunUntilIdle();
    145 #endif
    146   return true;
    147 }
    148 
    149 }  // namespace
    150 
    151 // Main routine for running as the Browser process.
    152 int ShellBrowserMain(
    153     const content::MainFunctionParams& parameters,
    154     const scoped_ptr<content::BrowserMainRunner>& main_runner) {
    155   bool layout_test_mode =
    156       CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree);
    157   base::ScopedTempDir browser_context_path_for_layout_tests;
    158 
    159   if (layout_test_mode) {
    160     CHECK(browser_context_path_for_layout_tests.CreateUniqueTempDir());
    161     CHECK(!browser_context_path_for_layout_tests.path().MaybeAsASCII().empty());
    162     CommandLine::ForCurrentProcess()->AppendSwitchASCII(
    163         switches::kContentShellDataPath,
    164         browser_context_path_for_layout_tests.path().MaybeAsASCII());
    165 
    166 #if defined(OS_ANDROID)
    167     content::EnsureInitializeForAndroidLayoutTests();
    168 #endif
    169   }
    170 
    171   int exit_code = main_runner->Initialize(parameters);
    172   DCHECK_LT(exit_code, 0)
    173       << "BrowserMainRunner::Initialize failed in ShellBrowserMain";
    174 
    175   if (exit_code >= 0)
    176     return exit_code;
    177 
    178   if (CommandLine::ForCurrentProcess()->HasSwitch(
    179         switches::kCheckLayoutTestSysDeps)) {
    180     base::MessageLoop::current()->PostTask(FROM_HERE,
    181                                            base::MessageLoop::QuitClosure());
    182     main_runner->Run();
    183     content::Shell::CloseAllWindows();
    184     main_runner->Shutdown();
    185     return 0;
    186   }
    187 
    188   if (layout_test_mode) {
    189     content::WebKitTestController test_controller;
    190     {
    191       // We're outside of the message loop here, and this is a test.
    192       base::ThreadRestrictions::ScopedAllowIO allow_io;
    193       base::FilePath temp_path;
    194       base::GetTempDir(&temp_path);
    195       test_controller.SetTempPath(temp_path);
    196     }
    197     std::string test_string;
    198     CommandLine::StringVector args =
    199         CommandLine::ForCurrentProcess()->GetArgs();
    200     size_t command_line_position = 0;
    201     bool ran_at_least_once = false;
    202 
    203     std::cout << "#READY\n";
    204     std::cout.flush();
    205 
    206     while (GetNextTest(args, &command_line_position, &test_string)) {
    207       if (!RunOneTest(test_string, &ran_at_least_once, main_runner))
    208         break;
    209     }
    210     if (!ran_at_least_once) {
    211       base::MessageLoop::current()->PostTask(FROM_HERE,
    212                                              base::MessageLoop::QuitClosure());
    213       main_runner->Run();
    214     }
    215 
    216 #if defined(OS_ANDROID)
    217     // Android should only execute Shutdown() here when running layout tests.
    218     main_runner->Shutdown();
    219 #endif
    220 
    221     exit_code = 0;
    222   }
    223 
    224 #if !defined(OS_ANDROID)
    225   if (!layout_test_mode)
    226     exit_code = main_runner->Run();
    227 
    228   main_runner->Shutdown();
    229 #endif
    230 
    231   return exit_code;
    232 }
    233