Chromium Code Reviews| Index: content/shell/browser/layout_test/scoped_android_configuration.cc |
| diff --git a/content/shell/browser/layout_test/scoped_android_configuration.cc b/content/shell/browser/layout_test/scoped_android_configuration.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..83b1191fcf738effa89fde9e7011567139789a1e |
| --- /dev/null |
| +++ b/content/shell/browser/layout_test/scoped_android_configuration.cc |
| @@ -0,0 +1,188 @@ |
| +// Copyright 2013 The Chromium Authors. All rights reserved. |
|
Peter Beverloo
2016/12/13 12:15:34
micro nit: 2016
jbudorick
2016/12/14 02:21:59
Done.
|
| +// 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/scoped_android_configuration.h" |
| + |
| +#include <fcntl.h> |
| +#include <iostream> |
| +#include <memory> |
| + |
| +#include "base/android/context_utils.h" |
| +#include "base/android/jni_android.h" |
| +#include "base/android/jni_string.h" |
| +#include "base/command_line.h" |
| +#include "base/files/file_path.h" |
| +#include "base/message_loop/message_loop.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "base/synchronization/waitable_event.h" |
| +#include "base/test/test_support_android.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "content/public/test/nested_message_pump_android.h" |
| +#include "content/shell/browser/layout_test/blink_test_controller.h" |
| +#include "content/shell/common/layout_test/layout_test_switches.h" |
| +#include "content/shell/common/shell_switches.h" |
| +#include "jni/ShellLayoutTestUtils_jni.h" |
| +#include "net/base/ip_address.h" |
| +#include "net/base/ip_endpoint.h" |
| +#include "net/base/net_errors.h" |
| +#include "net/base/sockaddr_storage.h" |
| +#include "net/socket/socket_posix.h" |
| +#include "url/gurl.h" |
| + |
| +using base::android::ScopedJavaLocalRef; |
| + |
| +namespace content { |
| + |
| +namespace { |
| + |
| +std::unique_ptr<base::MessagePump> CreateMessagePumpForUI() { |
| + return std::unique_ptr<base::MessagePump>(new NestedMessagePumpAndroid()); |
| +} |
| + |
| +void ConnectCompleted(const base::Closure& socket_connected, int rv) { |
| + CHECK_EQ(net::OK, rv) << " Failed to redirect to socket: " |
| + << net::ErrorToString(rv); |
|
Peter Beverloo
2016/12/13 12:15:34
nit: here and elsewhere -- we no longer use detail
jbudorick
2016/12/14 02:21:59
Switched to LOG_IF(FATAL, condition) << "msg";
|
| + socket_connected.Run(); |
| +} |
| + |
| +void CreateAndConnectSocket( |
| + uint16_t port, |
| + const base::Callback<void(std::unique_ptr<net::SocketPosix>)>& |
| + socket_connected) { |
| + net::SockaddrStorage storage; |
| + net::IPAddress address; |
| + CHECK(address.AssignFromIPLiteral("127.0.0.1")) |
| + << "Failed to create IPAddress from IP literal 127.0.0.1."; |
| + net::IPEndPoint endpoint(address, port); |
| + CHECK(endpoint.ToSockAddr(storage.addr, &storage.addr_len)) |
| + << "Failed to convert " << endpoint.ToString() << " to sockaddr."; |
| + |
| + std::unique_ptr<net::SocketPosix> socket(new net::SocketPosix); |
|
Peter Beverloo
2016/12/13 12:15:34
micro nit - prefer the following as it avoids the
jbudorick
2016/12/14 02:21:59
Done.
|
| + |
| + int result = socket->Open(AF_INET); |
| + CHECK_EQ(net::OK, result) << "Failed to open socket for " |
| + << endpoint.ToString() << ": " |
| + << net::ErrorToString(result); |
| + |
| + // Set the socket as blocking. |
| + const int flags = fcntl(socket->socket_fd(), F_GETFL); |
| + CHECK_NE(flags, -1); |
| + if (flags & O_NONBLOCK) { |
| + fcntl(socket->socket_fd(), F_SETFL, flags & ~O_NONBLOCK); |
| + } |
| + |
| + net::SocketPosix* socket_ptr = socket.get(); |
| + net::CompletionCallback connect_completed = |
| + base::Bind(&ConnectCompleted, |
| + base::Bind(socket_connected, base::Passed(std::move(socket)))); |
| + result = socket_ptr->Connect(storage, connect_completed); |
| + if (result != net::ERR_IO_PENDING) { |
| + connect_completed.Run(result); |
| + } |
| +} |
| + |
| +void RedirectStdout(int fd) { |
| + CHECK_NE(dup2(fd, STDOUT_FILENO), -1) << "Failed to dup2 stdout: " |
| + << strerror(errno); |
| +} |
| + |
| +void RedirectStdin(int fd) { |
| + CHECK_NE(dup2(fd, STDIN_FILENO), -1) << "Failed to dup2 stdin: " |
| + << strerror(errno); |
| +} |
| + |
| +void RedirectStderr(int fd) { |
| + CHECK_NE(dup2(fd, STDERR_FILENO), -1) << "Failed to dup2 stderr: " |
| + << strerror(errno); |
| +} |
| + |
| +void FinishRedirection( |
| + const base::Callback<void(int)>& redirect, |
| + const base::Callback<void(std::unique_ptr<net::SocketPosix>)>& |
| + transfer_socket, |
| + base::WaitableEvent* event, |
| + std::unique_ptr<net::SocketPosix> socket) { |
| + redirect.Run(socket->socket_fd()); |
| + transfer_socket.Run(std::move(socket)); |
| + event->Signal(); |
| +} |
| + |
| +void RedirectStream( |
| + uint16_t port, |
| + const base::Callback<void(base::WaitableEvent*, |
| + std::unique_ptr<net::SocketPosix>)>& |
| + finish_redirection) { |
| + base::WaitableEvent redirected( |
| + base::WaitableEvent::ResetPolicy::MANUAL, |
| + base::WaitableEvent::InitialState::NOT_SIGNALED); |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&CreateAndConnectSocket, port, |
| + base::Bind(finish_redirection, &redirected))); |
| + ScopedAllowWaitForAndroidLayoutTests allow_wait; |
| + while (!redirected.IsSignaled()) |
| + redirected.Wait(); |
| +} |
| + |
| +} // namespace |
| + |
| +ScopedAndroidConfiguration::ScopedAndroidConfiguration() : sockets_() { |
| + JNIEnv* env = base::android::AttachCurrentThread(); |
| + ScopedJavaLocalRef<jstring> jtest_data_dir = |
| + Java_ShellLayoutTestUtils_getIsolatedTestRoot(env); |
| + base::FilePath test_data_dir( |
| + base::android::ConvertJavaStringToUTF8(env, jtest_data_dir)); |
| + base::InitAndroidTestPaths(test_data_dir); |
| + |
| + bool success = |
| + base::MessageLoop::InitMessagePumpForUIFactory(&CreateMessagePumpForUI); |
| + CHECK(success) << "Unable to initialize the message pump for Android."; |
| +} |
| + |
| +ScopedAndroidConfiguration::~ScopedAndroidConfiguration() = default; |
| + |
| +void ScopedAndroidConfiguration::RedirectStreams() { |
| + // Unretained is safe here because all executions of add_socket finish |
| + // before this function returns. |
| + base::Callback<void(std::unique_ptr<net::SocketPosix>)> add_socket = |
| + base::Bind(&ScopedAndroidConfiguration::AddSocket, |
| + base::Unretained(this)); |
| + |
| + std::string stdout_port_str = |
| + base::CommandLine::ForCurrentProcess()->GetSwitchValueNative( |
| + switches::kAndroidStdoutPort); |
| + unsigned stdout_port = 0; |
| + if (base::StringToUint(stdout_port_str, &stdout_port)) { |
| + RedirectStream(base::checked_cast<uint16_t>(stdout_port), |
| + base::Bind(&FinishRedirection, base::Bind(&RedirectStdout), |
| + add_socket)); |
| + } |
| + |
| + std::string stdin_port_str = |
| + base::CommandLine::ForCurrentProcess()->GetSwitchValueNative( |
| + switches::kAndroidStdinPort); |
| + unsigned stdin_port = 0; |
| + if (base::StringToUint(stdin_port_str, &stdin_port)) { |
| + RedirectStream( |
| + base::checked_cast<uint16_t>(stdin_port), |
| + base::Bind(&FinishRedirection, base::Bind(&RedirectStdin), add_socket)); |
| + } |
| + |
| + std::string stderr_port_str = |
| + base::CommandLine::ForCurrentProcess()->GetSwitchValueNative( |
| + switches::kAndroidStderrPort); |
| + unsigned stderr_port = 0; |
| + if (base::StringToUint(stderr_port_str, &stderr_port)) { |
| + RedirectStream(base::checked_cast<uint16_t>(stderr_port), |
| + base::Bind(&FinishRedirection, base::Bind(&RedirectStderr), |
| + add_socket)); |
| + } |
| +} |
| + |
| +void ScopedAndroidConfiguration::AddSocket( |
| + std::unique_ptr<net::SocketPosix> socket) { |
| + sockets_.push_back(std::move(socket)); |
| +} |
| + |
| +} // namespace content |