|  | // Copyright 2015 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 "content/shell/browser/layout_test/test_info_extractor.h" | 
|  |  | 
|  | #include <iostream> | 
|  |  | 
|  | #include "base/base_paths.h" | 
|  | #include "base/files/file_util.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/path_service.h" | 
|  | #include "base/strings/sys_string_conversions.h" | 
|  | #include "base/strings/utf_string_conversions.h" | 
|  | #include "base/threading/thread_restrictions.h" | 
|  | #include "build/build_config.h" | 
|  | #include "net/base/filename_util.h" | 
|  | #include "net/base/ip_address.h" | 
|  | #include "net/base/ip_endpoint.h" | 
|  | #include "net/base/url_util.h" | 
|  |  | 
|  | #if defined(OS_FUCHSIA) | 
|  | #include <sys/socket.h> | 
|  | #include <unistd.h> | 
|  | #endif | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | #if defined(OS_FUCHSIA) | 
|  | // Fuchsia doesn't support stdin stream for packaged apps. This means that when | 
|  | // running content_shell on Fuchsia it's not possible to use stdin to pass list | 
|  | // of tests. To workaround this issue for layout tests we redirect stdin stream | 
|  | // to a TCP socket connected to the layout test runner. The runner uses | 
|  | // --stdin-redirect to specify address and port for stdin redirection. | 
|  | constexpr char kStdinRedirectSwitch[] = "stdin-redirect"; | 
|  |  | 
|  | void ConnectStdinSocket(const std::string& host_and_port) { | 
|  | std::string host; | 
|  | int port; | 
|  | net::IPAddress address; | 
|  | if (!net::ParseHostAndPort(host_and_port, &host, &port) || | 
|  | !address.AssignFromIPLiteral(host)) { | 
|  | LOG(FATAL) << "Invalid stdin address: " << host_and_port; | 
|  | } | 
|  |  | 
|  | sockaddr_storage sockaddr_storage; | 
|  | sockaddr* addr = reinterpret_cast<sockaddr*>(&sockaddr_storage); | 
|  | socklen_t addr_len = sizeof(sockaddr_storage); | 
|  | net::IPEndPoint endpoint(address, port); | 
|  | bool converted = endpoint.ToSockAddr(addr, &addr_len); | 
|  | CHECK(converted); | 
|  |  | 
|  | int fd = socket(addr->sa_family, SOCK_STREAM, 0); | 
|  | PCHECK(fd >= 0); | 
|  | int result = connect(fd, addr, addr_len); | 
|  | PCHECK(result == 0) << "Failed to connect to " << host_and_port; | 
|  |  | 
|  | result = dup2(fd, STDIN_FILENO); | 
|  | PCHECK(result == STDIN_FILENO) << "Failed to dup socket to stdin"; | 
|  |  | 
|  | PCHECK(close(fd) == 0); | 
|  | } | 
|  |  | 
|  | #endif  // defined(OS_FUCHSIA) | 
|  |  | 
|  | std::unique_ptr<TestInfo> GetTestInfoFromLayoutTestName( | 
|  | const std::string& test_name) { | 
|  | // A test name is formated like file:///path/to/test'--pixel-test'pixelhash | 
|  | std::string path_or_url = test_name; | 
|  | std::string pixel_switch; | 
|  | std::string::size_type separator_position = path_or_url.find('\''); | 
|  | if (separator_position != std::string::npos) { | 
|  | pixel_switch = path_or_url.substr(separator_position + 1); | 
|  | path_or_url.erase(separator_position); | 
|  | } | 
|  | separator_position = pixel_switch.find('\''); | 
|  | std::string expected_pixel_hash; | 
|  | if (separator_position != std::string::npos) { | 
|  | expected_pixel_hash = pixel_switch.substr(separator_position + 1); | 
|  | pixel_switch.erase(separator_position); | 
|  | } | 
|  | const bool enable_pixel_dumping = | 
|  | (pixel_switch == "--pixel-test" || pixel_switch == "-p"); | 
|  |  | 
|  | GURL test_url(path_or_url); | 
|  | if (!(test_url.is_valid() && test_url.has_scheme())) { | 
|  | // We're outside of the message loop here, and this is a test. | 
|  | base::ScopedAllowBlockingForTesting allow_blocking; | 
|  | #if defined(OS_WIN) | 
|  | base::FilePath::StringType wide_path_or_url = | 
|  | base::SysNativeMBToWide(path_or_url); | 
|  | base::FilePath local_file(wide_path_or_url); | 
|  | #else | 
|  | base::FilePath local_file(path_or_url); | 
|  | #endif | 
|  | if (!base::PathExists(local_file)) { | 
|  | base::FilePath base_path; | 
|  | PathService::Get(base::DIR_SOURCE_ROOT, &base_path); | 
|  | local_file = base_path.Append(FILE_PATH_LITERAL("third_party")) | 
|  | .Append(FILE_PATH_LITERAL("WebKit")) | 
|  | .Append(FILE_PATH_LITERAL("LayoutTests")) | 
|  | .Append(local_file); | 
|  | } | 
|  | test_url = net::FilePathToFileURL(base::MakeAbsoluteFilePath(local_file)); | 
|  | } | 
|  | base::FilePath local_path; | 
|  | base::FilePath current_working_directory; | 
|  |  | 
|  | // We're outside of the message loop here, and this is a test. | 
|  | base::ScopedAllowBlockingForTesting allow_blocking; | 
|  | if (net::FileURLToFilePath(test_url, &local_path)) | 
|  | current_working_directory = local_path.DirName(); | 
|  | else | 
|  | base::GetCurrentDirectory(¤t_working_directory); | 
|  | return std::make_unique<TestInfo>(test_url, enable_pixel_dumping, | 
|  | expected_pixel_hash, | 
|  | current_working_directory); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TestInfo::TestInfo(const GURL& url, | 
|  | bool enable_pixel_dumping, | 
|  | const std::string& expected_pixel_hash, | 
|  | const base::FilePath& current_working_directory) | 
|  | : url(url), | 
|  | enable_pixel_dumping(enable_pixel_dumping), | 
|  | expected_pixel_hash(expected_pixel_hash), | 
|  | current_working_directory(current_working_directory) {} | 
|  | TestInfo::~TestInfo() {} | 
|  |  | 
|  | TestInfoExtractor::TestInfoExtractor(const base::CommandLine& cmd_line) | 
|  | : cmdline_args_(cmd_line.GetArgs()), cmdline_position_(0) { | 
|  | #if defined(OS_FUCHSIA) | 
|  | if (cmd_line.HasSwitch(kStdinRedirectSwitch)) | 
|  | ConnectStdinSocket(cmd_line.GetSwitchValueASCII(kStdinRedirectSwitch)); | 
|  | #endif  // defined(OS_FUCHSIA) | 
|  | } | 
|  |  | 
|  | TestInfoExtractor::~TestInfoExtractor() {} | 
|  |  | 
|  | std::unique_ptr<TestInfo> TestInfoExtractor::GetNextTest() { | 
|  | if (cmdline_position_ >= cmdline_args_.size()) | 
|  | return nullptr; | 
|  |  | 
|  | std::string test_string; | 
|  | if (cmdline_args_[cmdline_position_] == FILE_PATH_LITERAL("-")) { | 
|  | do { | 
|  | bool success = !!std::getline(std::cin, test_string, '\n'); | 
|  | if (!success) | 
|  | return nullptr; | 
|  | } while (test_string.empty()); | 
|  | } else { | 
|  | #if defined(OS_WIN) | 
|  | test_string = base::WideToUTF8(cmdline_args_[cmdline_position_++]); | 
|  | #else | 
|  | test_string = cmdline_args_[cmdline_position_++]; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | DCHECK(!test_string.empty()); | 
|  | if (test_string == "QUIT") | 
|  | return nullptr; | 
|  | return GetTestInfoFromLayoutTestName(test_string); | 
|  | } | 
|  |  | 
|  | }  // namespace content |