From a0844129c3b840fbe8c6ee1dce3d52cd807e049f Mon Sep 17 00:00:00 2001 From: "craigdh@chromium.org" Date: Fri, 31 May 2013 07:07:40 +0000 Subject: [PATCH] [chromedriver] Android device management and auto-device assignment. BUG=chromedriver:343 TEST=unittests Review URL: https://chromiumcodereview.appspot.com/15851004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@203350 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/adb.h | 31 +++++ chrome/adb_impl.cc | 166 ++++++++++++++++++++++++ chrome/adb_impl.h | 58 +++++++++ chrome/chrome_android_impl.cc | 13 +- chrome/chrome_android_impl.h | 6 + chrome/device_manager.cc | 201 +++++++++++------------------- chrome/device_manager.h | 80 +++++++----- chrome/device_manager_unittest.cc | 95 ++++++++++++++ chrome_launcher.cc | 39 +++--- chrome_launcher.h | 2 + command_executor_impl.cc | 8 +- command_executor_impl.h | 4 + commands.cc | 8 +- commands.h | 5 +- 14 files changed, 521 insertions(+), 195 deletions(-) create mode 100644 chrome/adb.h create mode 100644 chrome/adb_impl.cc create mode 100644 chrome/adb_impl.h create mode 100644 chrome/device_manager_unittest.cc diff --git a/chrome/adb.h b/chrome/adb.h new file mode 100644 index 00000000..ad934eac --- /dev/null +++ b/chrome/adb.h @@ -0,0 +1,31 @@ +// Copyright 2013 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. + +#ifndef CHROME_TEST_CHROMEDRIVER_CHROME_ADB_H_ +#define CHROME_TEST_CHROMEDRIVER_CHROME_ADB_H_ + +#include +#include + +class Status; + +class Adb { + public: + virtual ~Adb() {} + + virtual Status GetDevices(std::vector* devices) = 0; + virtual Status ForwardPort(const std::string& device_serial, + int local_port, + const std::string& remote_abstract) = 0; + virtual Status SetChromeFlags(const std::string& device_serial) = 0; + virtual Status ClearAppData(const std::string& device_serial, + const std::string& package) = 0; + virtual Status Launch(const std::string& device_serial, + const std::string& package, + const std::string& activity) = 0; + virtual Status ForceStop(const std::string& device_serial, + const std::string& package) = 0; +}; + +#endif // CHROME_TEST_CHROMEDRIVER_CHROME_ADB_H_ diff --git a/chrome/adb_impl.cc b/chrome/adb_impl.cc new file mode 100644 index 00000000..a90792fd --- /dev/null +++ b/chrome/adb_impl.cc @@ -0,0 +1,166 @@ +// Copyright 2013 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 "chrome/test/chromedriver/chrome/adb_impl.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/string_tokenizer.h" +#include "base/synchronization/waitable_event.h" +#include "chrome/test/chromedriver/chrome/log.h" +#include "chrome/test/chromedriver/chrome/status.h" +#include "chrome/test/chromedriver/net/adb_client_socket.h" + +namespace { + +const int kAdbPort = 5037; + +void ReceiveAdbResponse(std::string* response_out, bool* success, + base::WaitableEvent* event, int result, + const std::string& response) { + *response_out = response; + *success = (result >= 0) ? true : false; + event->Signal(); +} + +void ExecuteCommandOnIOThread( + const std::string& command, std::string* response, bool* success, + base::WaitableEvent* event) { + CHECK(MessageLoop::current()->IsType(MessageLoop::TYPE_IO)); + AdbClientSocket::AdbQuery(kAdbPort, command, + base::Bind(&ReceiveAdbResponse, response, success, event)); +} + +} // namespace + +AdbImpl::AdbImpl( + const scoped_refptr& io_message_loop_proxy, + Log* log) + : io_message_loop_proxy_(io_message_loop_proxy), log_(log) { + CHECK(io_message_loop_proxy_); +} + +AdbImpl::~AdbImpl() {} + +Status AdbImpl::GetDevices(std::vector* devices) { + std::string response; + Status status = ExecuteCommand("host:devices", &response); + if (!status.IsOk()) + return status; + base::StringTokenizer lines(response, "\n"); + while (lines.GetNext()) { + std::vector fields; + base::SplitStringAlongWhitespace(lines.token(), &fields); + if (fields.size() == 2 && fields[1] == "device") { + devices->push_back(fields[0]); + } + } + return Status(kOk); +} + +Status AdbImpl::ForwardPort( + const std::string& device_serial, int local_port, + const std::string& remote_abstract) { + std::string response; + Status status = ExecuteHostCommand( + device_serial, + "forward:tcp:" + base::IntToString(local_port) + ";localabstract:" + + remote_abstract, + &response); + if (!status.IsOk()) + return status; + if (response == "OKAY") + return Status(kOk); + return Status(kUnknownError, "Failed to forward ports: " + response); +} + +Status AdbImpl::SetChromeFlags(const std::string& device_serial) { + std::string response; + Status status = ExecuteHostShellCommand( + device_serial, + "echo chrome --disable-fre --metrics-recording-only " + "--enable-remote-debugging > /data/local/chrome-command-line;" + "echo $?", + &response); + if (!status.IsOk()) + return status; + if (response.find("0") == std::string::npos) + return Status(kUnknownError, "Failed to set Chrome flags"); + return Status(kOk); +} + +Status AdbImpl::ClearAppData( + const std::string& device_serial, const std::string& package) { + std::string response; + std::string command = "pm clear " + package; + Status status = ExecuteHostShellCommand(device_serial, command, &response); + if (!status.IsOk()) + return status; + if (response.find("Success") == std::string::npos) + return Status(kUnknownError, "Failed to clear app data: " + response); + return Status(kOk); +} + +Status AdbImpl::Launch( + const std::string& device_serial, const std::string& package, + const std::string& activity) { + std::string response; + Status status = ExecuteHostShellCommand( + device_serial, + "am start -a android.intent.action.VIEW -S -W -n " + + package + "/" + activity + " -d \"data:text/html;charset=utf-8,\"", + &response); + if (!status.IsOk()) + return status; + if (response.find("Complete") == std::string::npos) + return Status(kUnknownError, + "Failed to start " + package + ": " + response); + return Status(kOk); +} + +Status AdbImpl::ForceStop( + const std::string& device_serial, const std::string& package) { + std::string response; + return ExecuteHostShellCommand( + device_serial, "am force-stop " + package, &response); +} + +Status AdbImpl::ExecuteCommand( + const std::string& command, std::string* response) { + bool success; + base::WaitableEvent event(false, false); + log_->AddEntry(Log::kDebug, "Adb command: " + command); + io_message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&ExecuteCommandOnIOThread, command, response, &success, + &event)); + event.Wait(); + log_->AddEntry(Log::kDebug, "Adb response: " + *response); + if (success) + return Status(kOk); + return Status(kUnknownError, + "Adb command \"" + command + "\" failed, is the Adb server running?"); +} + +Status AdbImpl::ExecuteHostCommand( + const std::string& device_serial, + const std::string& host_command, std::string* response) { + return ExecuteCommand( + "host-serial:" + device_serial + ":" + host_command, response); +} + +Status AdbImpl::ExecuteHostShellCommand( + const std::string& device_serial, + const std::string& shell_command, + std::string* response) { + return ExecuteCommand( + "host:transport:" + device_serial + "|shell:" + shell_command, + response); +} + diff --git a/chrome/adb_impl.h b/chrome/adb_impl.h new file mode 100644 index 00000000..afe9c23f --- /dev/null +++ b/chrome/adb_impl.h @@ -0,0 +1,58 @@ +// Copyright 2013 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. + +#ifndef CHROME_TEST_CHROMEDRIVER_CHROME_ADB_IMPL_H_ +#define CHROME_TEST_CHROMEDRIVER_CHROME_ADB_IMPL_H_ + +#include +#include + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "chrome/test/chromedriver/chrome/adb.h" + +namespace base { +class MessageLoopProxy; +} + +class Log; +class Status; + +class AdbImpl : public Adb { + public: + explicit AdbImpl( + const scoped_refptr& io_message_loop_proxy, + Log* log); + virtual ~AdbImpl(); + + // Overridden from Adb: + virtual Status GetDevices(std::vector* devices) OVERRIDE; + virtual Status ForwardPort(const std::string& device_serial, + int local_port, + const std::string& remote_abstract) OVERRIDE; + virtual Status SetChromeFlags(const std::string& device_serial) OVERRIDE; + virtual Status ClearAppData(const std::string& device_serial, + const std::string& package) OVERRIDE; + virtual Status Launch(const std::string& device_serial, + const std::string& package, + const std::string& activity) OVERRIDE; + virtual Status ForceStop(const std::string& device_serial, + const std::string& package) OVERRIDE; + + private: + Status ExecuteCommand(const std::string& command, + std::string* response); + Status ExecuteHostCommand(const std::string& device_serial, + const std::string& host_command, + std::string* response); + Status ExecuteHostShellCommand(const std::string& device_serial, + const std::string& shell_command, + std::string* response); + + scoped_refptr io_message_loop_proxy_; + + Log* log_; +}; + +#endif // CHROME_TEST_CHROMEDRIVER_CHROME_ADB_IMPL_H_ diff --git a/chrome/chrome_android_impl.cc b/chrome/chrome_android_impl.cc index 44cdd345..f0668efb 100644 --- a/chrome/chrome_android_impl.cc +++ b/chrome/chrome_android_impl.cc @@ -4,6 +4,7 @@ #include "chrome/test/chromedriver/chrome/chrome_android_impl.h" +#include "chrome/test/chromedriver/chrome/device_manager.h" #include "chrome/test/chromedriver/chrome/devtools_http_client.h" #include "chrome/test/chromedriver/chrome/status.h" @@ -12,12 +13,11 @@ ChromeAndroidImpl::ChromeAndroidImpl( const std::string& version, int build_no, ScopedVector& devtools_event_listeners, + scoped_ptr device, Log* log) - : ChromeImpl(client.Pass(), - version, - build_no, - devtools_event_listeners, - log) {} + : ChromeImpl(client.Pass(), version, build_no, devtools_event_listeners, + log), + device_(device.Pass()) {} ChromeAndroidImpl::~ChromeAndroidImpl() {} @@ -26,7 +26,6 @@ std::string ChromeAndroidImpl::GetOperatingSystemName() { } Status ChromeAndroidImpl::Quit() { - // NOOP. - return Status(kOk); + return device_->StopChrome(); } diff --git a/chrome/chrome_android_impl.h b/chrome/chrome_android_impl.h index 52b778ad..cf1d185f 100644 --- a/chrome/chrome_android_impl.h +++ b/chrome/chrome_android_impl.h @@ -8,8 +8,10 @@ #include #include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" #include "chrome/test/chromedriver/chrome/chrome_impl.h" +class Device; class DevToolsHttpClient; class ChromeAndroidImpl : public ChromeImpl { @@ -19,12 +21,16 @@ class ChromeAndroidImpl : public ChromeImpl { const std::string& version, int build_no, ScopedVector& devtools_event_listeners, + scoped_ptr device, Log* log); virtual ~ChromeAndroidImpl(); // Overridden from Chrome: virtual std::string GetOperatingSystemName() OVERRIDE; virtual Status Quit() OVERRIDE; + + private: + scoped_ptr device_; }; #endif // CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_ANDROID_IMPL_H_ diff --git a/chrome/device_manager.cc b/chrome/device_manager.cc index 69e3343f..bb1ae0dd 100644 --- a/chrome/device_manager.cc +++ b/chrome/device_manager.cc @@ -4,21 +4,18 @@ #include "chrome/test/chromedriver/chrome/device_manager.h" +#include +#include + #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/callback.h" #include "base/logging.h" -#include "base/stringprintf.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_split.h" -#include "base/strings/string_tokenizer.h" -#include "base/synchronization/waitable_event.h" +#include "chrome/test/chromedriver/chrome/adb.h" #include "chrome/test/chromedriver/chrome/status.h" -#include "chrome/test/chromedriver/net/adb_client_socket.h" namespace { -const int kAdbPort = 5037; - std::string GetActivityForPackage(const std::string& package) { if (package == "org.chromium.chrome.testshell") return ".ChromiumTestShellActivity"; @@ -31,158 +28,108 @@ std::string GetDevtoolsSocket(const std::string& package) { return "chrome_devtools_remote"; } -void ReceiveAdbResponse(std::string* response_out, bool* success, - base::WaitableEvent* event, int result, - const std::string& response) { - *response_out = response; - *success = (result >= 0) ? true : false; - event->Signal(); -} - } // namespace -DeviceManager::DeviceManager() : thread_("Adb_IOThread") {} +Device::Device( + const std::string& device_serial, Adb* adb, + base::Callback release_callback) + : serial_(device_serial), + adb_(adb), + release_callback_(release_callback) {} -DeviceManager::~DeviceManager() {} +Device::~Device() { + release_callback_.Run(); +} -Status DeviceManager::StartChrome( - const std::string& device_serial, const std::string& package, int port) { - Status status = SetChromeFlags(device_serial); +Status Device::StartChrome(const std::string& package, int port) { + if (!active_package_.empty()) + return Status(kUnknownError, + active_package_ + " was launched and has not been quit"); + Status status = adb_->SetChromeFlags(serial_); if (!status.IsOk()) return status; - status = ClearAppData(device_serial, package); + status = adb_->ClearAppData(serial_, package); if (!status.IsOk()) return status; - status = Launch(device_serial, package); + status = adb_->Launch(serial_, package, GetActivityForPackage(package)); if (!status.IsOk()) return status; - return ForwardPort(device_serial, port, GetDevtoolsSocket(package)); + active_package_ = package; + return adb_->ForwardPort(serial_, port, GetDevtoolsSocket(package)); } -Status DeviceManager::GetDevices(std::vector* devices) { +Status Device::StopChrome() { + if (active_package_.empty()) + return Status(kUnknownError, "No package has been launched"); std::string response; - Status status = ExecuteCommand("host:devices", &response); + Status status = adb_->ForceStop(serial_, active_package_); if (!status.IsOk()) return status; - base::StringTokenizer lines(response, "\n"); - while (lines.GetNext()) { - std::vector fields; - base::SplitStringAlongWhitespace(lines.token(), &fields); - if (fields.size() == 2 && fields[1] == "device") { - devices->push_back(fields[0]); - } - } + active_package_ = ""; return Status(kOk); } -Status DeviceManager::ForwardPort( - const std::string& device_serial, int local_port, - const std::string& remote_abstract) { - std::string response; - Status status = ExecuteHostCommand( - device_serial, - "forward:tcp:" + base::IntToString(local_port) + ";localabstract:" + - remote_abstract, - &response); - if (!status.IsOk()) - return status; - if (response == "OKAY") - return Status(kOk); - return Status(kUnknownError, "Failed to forward ports: " + response); +DeviceManager::DeviceManager(Adb* adb) : adb_(adb) { + CHECK(adb_); } -Status DeviceManager::SetChromeFlags(const std::string& device_serial) { - std::string response; - Status status = ExecuteHostShellCommand( - device_serial, - "echo chrome --disable-fre --metrics-recording-only " - "--enable-remote-debugging > /data/local/chrome-command-line;" - "echo $?", - &response); - if (!status.IsOk()) - return status; - if (response.find("0") == std::string::npos) - return Status(kUnknownError, "Failed to set Chrome flags"); - return Status(kOk); -} +DeviceManager::~DeviceManager() {} -Status DeviceManager::ClearAppData(const std::string& device_serial, - const std::string& package) { - std::string response; - std::string command = "pm clear " + package; - Status status = ExecuteHostShellCommand(device_serial, command, &response); +Status DeviceManager::AcquireDevice(scoped_ptr* device) { + std::vector devices; + Status status = adb_->GetDevices(&devices); if (!status.IsOk()) return status; - if (response.find("Success") == std::string::npos) - return Status(kUnknownError, "Failed to clear app data: " + response); - return Status(kOk); + + base::AutoLock lock(devices_lock_); + status = Status(kUnknownError, "No device avaliable"); + std::vector::iterator iter; + for (iter = devices.begin(); iter != devices.end(); iter++) { + if (!IsDeviceLocked(*iter)) { + device->reset(LockDevice(*iter)); + status = Status(kOk); + break; + } + } + return status; } -Status DeviceManager::Launch( - const std::string& device_serial, const std::string& package) { - std::string response; - Status status = ExecuteHostShellCommand( - device_serial, - "am start -a android.intent.action.VIEW -S -W -n " + - package + "/" + GetActivityForPackage(package) + - " -d \"data:text/html;charset=utf-8,\"", - &response); +Status DeviceManager::AcquireSpecificDevice( + const std::string& device_serial, scoped_ptr* device) { + std::vector devices; + Status status = adb_->GetDevices(&devices); if (!status.IsOk()) return status; - if (response.find("Complete") == std::string::npos) - return Status(kUnknownError, - "Failed to start " + package + ": " + response); - return Status(kOk); -} - -Status DeviceManager::Init() { - base::Thread::Options options(base::MessageLoop::TYPE_IO, 0); - bool thread_started = thread_.StartWithOptions(options); - if (!thread_started) - return Status(kUnknownError, "Adb IO thread failed to start"); - return Status(kOk); -} -Status DeviceManager::ExecuteCommand( - const std::string& command, std::string* response) { - if (!thread_.IsRunning()) { - Status status(kOk); - status = Init(); - if (!status.IsOk()) - return status; + if (std::find(devices.begin(), devices.end(), device_serial) == devices.end()) + return Status(kUnknownError, + "Device " + device_serial + " is not avaliable"); + + base::AutoLock lock(devices_lock_); + if (IsDeviceLocked(device_serial)) { + status = Status(kUnknownError, + "Device " + device_serial + " already has an active session"); + } else { + device->reset(LockDevice(device_serial)); + status = Status(kOk); } - bool success; - base::WaitableEvent event(false, false); - thread_.message_loop_proxy()->PostTask( - FROM_HERE, - base::Bind(&DeviceManager::ExecuteCommandOnIOThread, - base::Unretained(this), command, response, &success, &event)); - event.Wait(); - LOG(INFO) << "Adb: " << command << " -> " << *response; - if (success) - return Status(kOk); - return Status(kUnknownError, "Adb command failed: " + command); + return status; } -Status DeviceManager::ExecuteHostCommand( - const std::string& device_serial, const std::string& host_command, - std::string* response) { - return ExecuteCommand( - "host-serial:" + device_serial + ":" + host_command, response); +void DeviceManager::ReleaseDevice(const std::string& device_serial) { + base::AutoLock lock(devices_lock_); + active_devices_.remove(device_serial); } -Status DeviceManager::ExecuteHostShellCommand( - const std::string& device_serial, const std::string& shell_command, - std::string* response) { - return ExecuteCommand( - "host:transport:" + device_serial + "|shell:" + shell_command, - response); +Device* DeviceManager::LockDevice(const std::string& device_serial) { + active_devices_.push_back(device_serial); + return new Device(device_serial, adb_, + base::Bind(&DeviceManager::ReleaseDevice, base::Unretained(this), + device_serial)); } -void DeviceManager::ExecuteCommandOnIOThread( - const std::string& command, std::string* response, bool* success, - base::WaitableEvent* event) { - CHECK(base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_IO)); - AdbClientSocket::AdbQuery(kAdbPort, command, - base::Bind(&ReceiveAdbResponse, response, success, event)); +bool DeviceManager::IsDeviceLocked(const std::string& device_serial) { + return std::find(active_devices_.begin(), active_devices_.end(), + device_serial) != active_devices_.end(); } + diff --git a/chrome/device_manager.h b/chrome/device_manager.h index 1f24108d..5a5844b2 100644 --- a/chrome/device_manager.h +++ b/chrome/device_manager.h @@ -5,51 +5,65 @@ #ifndef CHROME_TEST_CHROMEDRIVER_CHROME_DEVICE_MANAGER_H_ #define CHROME_TEST_CHROMEDRIVER_CHROME_DEVICE_MANAGER_H_ +#include #include -#include +#include "base/callback.h" #include "base/compiler_specific.h" -#include "base/threading/thread.h" - -namespace base { -class WaitableEvent; -} // namespace base +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/synchronization/lock.h" +class Adb; class Status; +class DeviceManager; + +class Device { + public: + ~Device(); + + Status StartChrome(const std::string& package, int port); + Status StopChrome(); + + private: + friend class DeviceManager; + + Device(const std::string& device_serial, + Adb* adb, + base::Callback release_callback); + + const std::string serial_; + std::string active_package_; + Adb* adb_; + base::Callback release_callback_; + + DISALLOW_COPY_AND_ASSIGN(Device); +}; class DeviceManager { public: - DeviceManager(); + explicit DeviceManager(Adb* adb); ~DeviceManager(); - Status StartChrome(const std::string& device_serial, - const std::string& package, - int port); - Status GetDevices(std::vector* devices); + // Returns a device which will not be reassigned during its lifetime. + Status AcquireDevice(scoped_ptr* device); + + // Returns a device with the same guarantees as AcquireDevice, but fails + // if the device with the given serial number is not avaliable. + Status AcquireSpecificDevice(const std::string& device_serial, + scoped_ptr* device); private: - Status ForwardPort(const std::string& device_serial, - int local_port, - const std::string& remote_abstract); - Status SetChromeFlags(const std::string& device_serial); - Status ClearAppData(const std::string& device_serial, - const std::string& package); - Status Launch(const std::string& device_serial, const std::string& package); - - Status Init(); - Status ExecuteCommand(const std::string& command, std::string* response); - Status ExecuteHostCommand(const std::string& device_serial, - const std::string& host_command, - std::string* response); - Status ExecuteHostShellCommand(const std::string& device_serial, - const std::string& shell_command, - std::string* response); - void ExecuteCommandOnIOThread(const std::string& command, - std::string* response, - bool* success, - base::WaitableEvent* event); - - base::Thread thread_; + void ReleaseDevice(const std::string& device_serial); + + Device* LockDevice(const std::string& device_serial); + bool IsDeviceLocked(const std::string& device_serial); + + base::Lock devices_lock_; + std::list active_devices_; + Adb* adb_; + + DISALLOW_COPY_AND_ASSIGN(DeviceManager); }; #endif // CHROME_TEST_CHROMEDRIVER_CHROME_DEVICE_MANAGER_H_ diff --git a/chrome/device_manager_unittest.cc b/chrome/device_manager_unittest.cc new file mode 100644 index 00000000..585e7cc2 --- /dev/null +++ b/chrome/device_manager_unittest.cc @@ -0,0 +1,95 @@ +// Copyright 2013 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 +#include + +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/test/chromedriver/chrome/adb.h" +#include "chrome/test/chromedriver/chrome/device_manager.h" +#include "chrome/test/chromedriver/chrome/status.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class FakeAdb : public Adb { + public: + FakeAdb() {}; + virtual ~FakeAdb() {}; + + virtual Status GetDevices(std::vector* devices) OVERRIDE { + devices->push_back("a"); + devices->push_back("b"); + return Status(kOk); + } + + virtual Status ForwardPort(const std::string& device_serial, + int local_port, + const std::string& remote_abstract) OVERRIDE { + return Status(kOk); + } + + virtual Status SetChromeFlags(const std::string& device_serial) OVERRIDE { + return Status(kOk); + } + + virtual Status ClearAppData(const std::string& device_serial, + const std::string& package) OVERRIDE { + return Status(kOk); + } + + virtual Status Launch(const std::string& device_serial, + const std::string& package, + const std::string& activity) OVERRIDE { + return Status(kOk); + } + + virtual Status ForceStop(const std::string& device_serial, + const std::string& package) OVERRIDE { + return Status(kOk); + } +}; + +} // namespace + +TEST(DeviceManager, AcquireDevice) { + FakeAdb adb; + DeviceManager device_manager(&adb); + scoped_ptr device1; + scoped_ptr device2; + scoped_ptr device3; + ASSERT_TRUE(device_manager.AcquireDevice(&device1).IsOk()); + ASSERT_TRUE(device_manager.AcquireDevice(&device2).IsOk()); + ASSERT_FALSE(device_manager.AcquireDevice(&device3).IsOk()); + device1.reset(NULL); + ASSERT_TRUE(device_manager.AcquireDevice(&device3).IsOk()); + ASSERT_FALSE(device_manager.AcquireDevice(&device1).IsOk()); +} + +TEST(DeviceManager, AcquireSpecificDevice) { + FakeAdb adb; + DeviceManager device_manager(&adb); + scoped_ptr device1; + scoped_ptr device2; + scoped_ptr device3; + ASSERT_TRUE(device_manager.AcquireSpecificDevice("a", &device1).IsOk()); + ASSERT_FALSE(device_manager.AcquireSpecificDevice("a", &device2).IsOk()); + ASSERT_TRUE(device_manager.AcquireSpecificDevice("b", &device3).IsOk()); + device1.reset(NULL); + ASSERT_TRUE(device_manager.AcquireSpecificDevice("a", &device2).IsOk()); + ASSERT_FALSE(device_manager.AcquireSpecificDevice("a", &device1).IsOk()); + ASSERT_FALSE(device_manager.AcquireSpecificDevice("b", &device1).IsOk()); +} + +TEST(Device, LaunchChrome) { + FakeAdb adb; + DeviceManager device_manager(&adb); + scoped_ptr device1; + ASSERT_TRUE(device_manager.AcquireDevice(&device1).IsOk()); + ASSERT_FALSE(device1->StopChrome().IsOk()); + ASSERT_TRUE(device1->StartChrome("p", 0).IsOk()); + ASSERT_FALSE(device1->StartChrome("p", 0).IsOk()); + ASSERT_TRUE(device1->StopChrome().IsOk()); +} diff --git a/chrome_launcher.cc b/chrome_launcher.cc index 65b3c5b6..a1cdbcab 100644 --- a/chrome_launcher.cc +++ b/chrome_launcher.cc @@ -280,27 +280,19 @@ Status LaunchAndroidChrome( Log* log, const Capabilities& capabilities, ScopedVector& devtools_event_listeners, + DeviceManager* device_manager, scoped_ptr* chrome) { - DeviceManager device_mgr; - std::vector devices; - Status status = device_mgr.GetDevices(&devices); - if (devices.empty()) - return Status(kUnknownError, "No devices attached"); - std::string device_serial; - if (!capabilities.device_serial.empty()) { - if (std::find(devices.begin(), devices.end(), capabilities.device_serial) == - devices.end()) - return Status(kUnknownError, - "Invalid device serial: " + capabilities.device_serial); - device_serial = capabilities.device_serial; - } else if (devices.size() == 1) { - device_serial = devices[0]; + Status status(kOk); + scoped_ptr device; + if (capabilities.device_serial.empty()) { + status = device_manager->AcquireDevice(&device); } else { - return Status(kUnknownError, - "No device serial supplied with multiple devices connected"); + status = device_manager->AcquireSpecificDevice( + capabilities.device_serial, &device); } - status = device_mgr.StartChrome( - device_serial, capabilities.android_package, port); + if (!status.IsOk()) + return status; + status = device->StartChrome(capabilities.android_package, port); if (!status.IsOk()) return status; @@ -317,11 +309,9 @@ Status LaunchAndroidChrome( if (status.IsError()) return status; - chrome->reset(new ChromeAndroidImpl(devtools_client.Pass(), - version, - build_no, - devtools_event_listeners, - log)); + chrome->reset(new ChromeAndroidImpl( + devtools_client.Pass(), version, build_no, devtools_event_listeners, + device.Pass(), log)); return Status(kOk); } @@ -332,13 +322,14 @@ Status LaunchChrome( int port, const SyncWebSocketFactory& socket_factory, Log* log, + DeviceManager* device_manager, const Capabilities& capabilities, ScopedVector& devtools_event_listeners, scoped_ptr* chrome) { if (capabilities.IsAndroid()) { return LaunchAndroidChrome( context_getter, port, socket_factory, log, capabilities, - devtools_event_listeners, chrome); + devtools_event_listeners, device_manager, chrome); } else { return LaunchDesktopChrome( context_getter, port, socket_factory, log, capabilities, diff --git a/chrome_launcher.h b/chrome_launcher.h index fa75c46e..405c275e 100644 --- a/chrome_launcher.h +++ b/chrome_launcher.h @@ -23,6 +23,7 @@ class FilePath; } class Chrome; +class DeviceManager; class Log; class Status; class URLRequestContextGetter; @@ -32,6 +33,7 @@ Status LaunchChrome( int port, const SyncWebSocketFactory& socket_factory, Log* log, + DeviceManager* device_manager, const Capabilities& capabilities, ScopedVector& devtools_event_listeners, scoped_ptr* chrome); diff --git a/command_executor_impl.cc b/command_executor_impl.cc index 1970706d..3e8ef990 100644 --- a/command_executor_impl.cc +++ b/command_executor_impl.cc @@ -12,8 +12,11 @@ #include "base/sys_info.h" #include "base/values.h" #include "chrome/test/chromedriver/alert_commands.h" +#include "chrome/test/chromedriver/chrome/adb_impl.h" +#include "chrome/test/chromedriver/chrome/device_manager.h" #include "chrome/test/chromedriver/chrome/status.h" #include "chrome/test/chromedriver/chrome/version.h" +#include "chrome/test/chromedriver/chrome_launcher.h" #include "chrome/test/chromedriver/command_names.h" #include "chrome/test/chromedriver/commands.h" #include "chrome/test/chromedriver/element_commands.h" @@ -48,6 +51,8 @@ void CommandExecutorImpl::Init() { context_getter_ = new URLRequestContextGetter( io_thread_.message_loop_proxy()); socket_factory_ = CreateSyncWebSocketFactory(context_getter_); + adb_.reset(new AdbImpl(io_thread_.message_loop_proxy(), log_)); + device_manager_.reset(new DeviceManager(adb_.get())); // Commands which require an element id. typedef std::map ElementCommandMap; @@ -257,7 +262,8 @@ void CommandExecutorImpl::Init() { CommandNames::kNewSession, base::Bind(&ExecuteNewSession, NewSessionParams( - log_, &session_map_, context_getter_, socket_factory_))); + log_, &session_map_, context_getter_, socket_factory_, + device_manager_.get()))); command_map_.Set( CommandNames::kQuitAll, base::Bind(&ExecuteQuitAll, diff --git a/command_executor_impl.h b/command_executor_impl.h index 53a1d6cf..fa8312bf 100644 --- a/command_executor_impl.h +++ b/command_executor_impl.h @@ -25,7 +25,9 @@ class DictionaryValue; class Value; } +class Adb; class ChromeLauncherImpl; +class DeviceManager; class Log; class URLRequestContextGetter; @@ -55,6 +57,8 @@ class CommandExecutorImpl : public CommandExecutor { SyncWebSocketFactory socket_factory_; SessionMap session_map_; SynchronizedMap command_map_; + scoped_ptr adb_; + scoped_ptr device_manager_; DISALLOW_COPY_AND_ASSIGN(CommandExecutorImpl); }; diff --git a/commands.cc b/commands.cc index 0321a06a..fbf1bf96 100644 --- a/commands.cc +++ b/commands.cc @@ -13,6 +13,7 @@ #include "chrome/test/chromedriver/chrome/chrome.h" #include "chrome/test/chromedriver/chrome/chrome_android_impl.h" #include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h" +#include "chrome/test/chromedriver/chrome/device_manager.h" #include "chrome/test/chromedriver/chrome/devtools_event_listener.h" #include "chrome/test/chromedriver/chrome/status.h" #include "chrome/test/chromedriver/chrome/version.h" @@ -49,11 +50,13 @@ NewSessionParams::NewSessionParams( Log* log, SessionMap* session_map, scoped_refptr context_getter, - const SyncWebSocketFactory& socket_factory) + const SyncWebSocketFactory& socket_factory, + DeviceManager* device_manager) : log(log), session_map(session_map), context_getter(context_getter), - socket_factory(socket_factory) {} + socket_factory(socket_factory), + device_manager(device_manager) {} NewSessionParams::~NewSessionParams() {} @@ -89,6 +92,7 @@ Status ExecuteNewSession( port, bound_params.socket_factory, bound_params.log, + bound_params.device_manager, capabilities, devtools_event_listeners, &chrome); diff --git a/commands.h b/commands.h index 22fdba76..7ce47b72 100644 --- a/commands.h +++ b/commands.h @@ -19,6 +19,7 @@ class DictionaryValue; class Value; } +class DeviceManager; class Log; class Status; class URLRequestContextGetter; @@ -34,13 +35,15 @@ struct NewSessionParams { NewSessionParams(Log* log, SessionMap* session_map, scoped_refptr context_getter, - const SyncWebSocketFactory& socket_factory); + const SyncWebSocketFactory& socket_factory, + DeviceManager* device_manager); ~NewSessionParams(); Log* log; SessionMap* session_map; scoped_refptr context_getter; SyncWebSocketFactory socket_factory; + DeviceManager* device_manager; }; // Creates a new session.