| // Copyright 2014 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 <stddef.h> |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <unordered_map> |
| #include <utility> |
| |
| #include "base/containers/queue.h" |
| #include "base/location.h" |
| #include "base/memory/ref_counted_memory.h" |
| #include "base/message_loop/message_loop_current.h" |
| #include "base/run_loop.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/post_task.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "chrome/browser/devtools/device/adb/mock_adb_server.h" |
| #include "chrome/browser/devtools/device/devtools_android_bridge.h" |
| #include "chrome/browser/devtools/device/usb/android_usb_device.h" |
| #include "chrome/browser/devtools/device/usb/usb_device_provider.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/test/test_utils.h" |
| #include "device/usb/public/cpp/fake_usb_device.h" |
| #include "device/usb/public/cpp/fake_usb_device_info.h" |
| #include "device/usb/public/cpp/fake_usb_device_manager.h" |
| #include "device/usb/public/mojom/device.mojom.h" |
| #include "device/usb/public/mojom/device_manager.mojom.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using content::BrowserThread; |
| |
| using device::FakeUsbDevice; |
| using device::FakeUsbDeviceInfo; |
| using device::FakeUsbDeviceManager; |
| using device::mojom::UsbAlternateInterfaceInfo; |
| using device::mojom::UsbConfigurationInfo; |
| using device::mojom::UsbConfigurationInfoPtr; |
| using device::mojom::UsbDevice; |
| using device::mojom::UsbEndpointInfo; |
| using device::mojom::UsbEndpointInfoPtr; |
| using device::mojom::UsbInterfaceInfo; |
| using device::mojom::UsbInterfaceInfoPtr; |
| using device::mojom::UsbTransferDirection; |
| using device::mojom::UsbTransferStatus; |
| using device::mojom::UsbTransferType; |
| |
| namespace { |
| |
| struct NoConfigTraits { |
| static const int kClass = 0xff; |
| static const int kSubclass = 0x42; |
| static const int kProtocol = 0x1; |
| static const bool kBreaks = false; |
| static const bool kConfigured = false; |
| }; |
| |
| struct AndroidTraits { |
| static const int kClass = 0xff; |
| static const int kSubclass = 0x42; |
| static const int kProtocol = 0x1; |
| static const bool kBreaks = false; |
| static const bool kConfigured = true; |
| }; |
| |
| struct NonAndroidTraits { |
| static const int kClass = 0xf0; |
| static const int kSubclass = 0x42; |
| static const int kProtocol = 0x2; |
| static const bool kBreaks = false; |
| static const bool kConfigured = true; |
| }; |
| |
| struct BreakingAndroidTraits { |
| static const int kClass = 0xff; |
| static const int kSubclass = 0x42; |
| static const int kProtocol = 0x1; |
| static const bool kBreaks = true; |
| static const bool kConfigured = true; |
| }; |
| |
| const uint32_t kMaxPayload = 4096; |
| const uint32_t kVersion = 0x01000000; |
| const uint8_t kAndroidConfigValue = 1; |
| |
| const char kDeviceManufacturer[] = "Test Manufacturer"; |
| const char kDeviceModel[] = "Nexus 6"; |
| const char kDeviceSerial[] = "01498B321301A00A"; |
| |
| UsbConfigurationInfoPtr ConstructAndroidConfig(uint8_t class_code, |
| uint8_t subclass_code, |
| uint8_t protocol_code) { |
| std::vector<UsbEndpointInfoPtr> endpoints; |
| endpoints.push_back(UsbEndpointInfo::New( |
| /*endpoint_number=*/0x01, UsbTransferDirection::INBOUND, |
| UsbTransferType::BULK, |
| /*packet_size=*/512)); |
| endpoints.push_back(UsbEndpointInfo::New( |
| /*endpoint_number=*/0x01, UsbTransferDirection::OUTBOUND, |
| UsbTransferType::BULK, |
| /*packet_size=*/512)); |
| |
| auto interface = UsbInterfaceInfo::New(); |
| interface->interface_number = 0; |
| interface->alternates.push_back(UsbAlternateInterfaceInfo::New( |
| /*alternate_setting=*/0, // alternate_setting |
| class_code, subclass_code, protocol_code, |
| /*interface_name=*/base::nullopt, std::move(endpoints))); |
| |
| auto config = UsbConfigurationInfo::New(); |
| config->configuration_value = kAndroidConfigValue; |
| config->interfaces.push_back(std::move(interface)); |
| |
| return config; |
| } |
| |
| class FakeAndroidUsbDeviceInfo : public FakeUsbDeviceInfo { |
| public: |
| explicit FakeAndroidUsbDeviceInfo(bool is_broken) |
| : FakeUsbDeviceInfo(0x0200, // usb_version |
| 0, // device_class |
| 0, // device_subclass |
| 0, // device_protocol |
| 0, // vendor_id |
| 0, // product_id |
| 0x0100, // device_version |
| kDeviceManufacturer, |
| kDeviceModel, |
| kDeviceSerial), |
| broken_traits_(is_broken) {} |
| |
| bool broken_traits() const { return broken_traits_; } |
| device::mojom::UsbDeviceInfoPtr ClonePtr() { return GetDeviceInfo().Clone(); } |
| |
| private: |
| ~FakeAndroidUsbDeviceInfo() override {} |
| |
| bool broken_traits_; |
| }; |
| |
| template <class T> |
| scoped_refptr<FakeAndroidUsbDeviceInfo> ConstructFakeUsbDevice() { |
| const bool broken = T::kBreaks; |
| auto device = base::MakeRefCounted<FakeAndroidUsbDeviceInfo>(broken); |
| auto config = ConstructAndroidConfig(T::kClass, T::kSubclass, T::kProtocol); |
| auto config_value = config->configuration_value; |
| device->AddConfig(std::move(config)); |
| if (T::kConfigured) |
| device->SetActiveConfig(config_value); |
| |
| return device; |
| } |
| |
| class MockLocalSocket : public MockAndroidConnection::Delegate { |
| public: |
| using Callback = |
| base::RepeatingCallback<void(int command, const std::string& message)>; |
| |
| MockLocalSocket(const Callback& callback, |
| const std::string& serial, |
| const std::string& command) |
| : callback_(callback), |
| connection_(new MockAndroidConnection(this, serial, command)) { |
| } |
| |
| void Receive(const std::string& data) { |
| connection_->Receive(data); |
| } |
| |
| private: |
| void SendSuccess(const std::string& message) override { |
| if (!message.empty()) |
| callback_.Run(AdbMessage::kCommandWRTE, message); |
| } |
| |
| void SendRaw(const std::string& message) override { |
| callback_.Run(AdbMessage::kCommandWRTE, message); |
| } |
| |
| void Close() override { |
| callback_.Run(AdbMessage::kCommandCLSE, std::string()); |
| } |
| |
| Callback callback_; |
| std::unique_ptr<MockAndroidConnection> connection_; |
| }; |
| |
| class FakeAndroidUsbDevice : public FakeUsbDevice { |
| public: |
| static void Create(scoped_refptr<FakeUsbDeviceInfo> device_info, |
| device::mojom::UsbDeviceRequest request, |
| device::mojom::UsbDeviceClientPtr client) { |
| auto* device_object = |
| new FakeAndroidUsbDevice(device_info, std::move(client)); |
| device_object->binding_ = mojo::MakeStrongBinding( |
| base::WrapUnique(device_object), std::move(request)); |
| } |
| |
| protected: |
| FakeAndroidUsbDevice(scoped_refptr<FakeUsbDeviceInfo> device, |
| device::mojom::UsbDeviceClientPtr client) |
| : FakeUsbDevice(device, std::move(client)) { |
| broken_traits_ = |
| static_cast<FakeAndroidUsbDeviceInfo*>(device.get())->broken_traits(); |
| } |
| |
| // override device::FakeUsbDevice |
| void GenericTransferIn(uint8_t endpoint_number, |
| uint32_t length, |
| uint32_t timeout, |
| GenericTransferInCallback callback) override { |
| queries_.push(Query(std::move(callback), length)); |
| ProcessQueries(); |
| } |
| |
| void GenericTransferOut(uint8_t endpoint_number, |
| const std::vector<uint8_t>& buffer, |
| uint32_t timeout, |
| GenericTransferOutCallback callback) override { |
| if (remaining_body_length_ == 0) { |
| // A new message, parse header first. |
| const auto* header = reinterpret_cast<const uint32_t*>(buffer.data()); |
| current_message_.reset( |
| new AdbMessage(header[0], header[1], header[2], std::string())); |
| remaining_body_length_ = header[3]; |
| uint32_t magic = header[5]; |
| if ((current_message_->command ^ 0xffffffff) != magic) { |
| DCHECK(false) << "Header checksum error"; |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), |
| UsbTransferStatus::TRANSFER_ERROR)); |
| return; |
| } |
| } else { |
| // Parse body. |
| DCHECK(current_message_.get()); |
| current_message_->body += std::string( |
| reinterpret_cast<const char*>(buffer.data()), buffer.size()); |
| remaining_body_length_ -= buffer.size(); |
| } |
| |
| if (remaining_body_length_ == 0) { |
| ProcessIncoming(); |
| } |
| |
| UsbTransferStatus status = broken_ ? UsbTransferStatus::TRANSFER_ERROR |
| : UsbTransferStatus::COMPLETED; |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), status)); |
| ProcessQueries(); |
| } |
| |
| template <class D> |
| void append(D data) { |
| std::copy(reinterpret_cast<uint8_t*>(&data), |
| (reinterpret_cast<uint8_t*>(&data)) + sizeof(D), |
| std::back_inserter(output_buffer_)); |
| } |
| |
| // Copied from AndroidUsbDevice::Checksum |
| uint32_t Checksum(const std::string& data) { |
| unsigned char* x = (unsigned char*)data.data(); |
| int count = data.length(); |
| uint32_t sum = 0; |
| while (count-- > 0) |
| sum += *x++; |
| return sum; |
| } |
| |
| void ProcessIncoming() { |
| DCHECK(current_message_.get()); |
| switch (current_message_->command) { |
| case AdbMessage::kCommandCNXN: { |
| WriteResponse(kVersion, |
| kMaxPayload, |
| AdbMessage::kCommandCNXN, |
| "device::ro.product.name=SampleProduct;ro.product.model=" |
| "SampleModel;ro.product.device=SampleDevice;"); |
| break; |
| } |
| case AdbMessage::kCommandCLSE: { |
| WriteResponse(0, |
| current_message_->arg0, |
| AdbMessage::kCommandCLSE, |
| std::string()); |
| local_sockets_.erase(current_message_->arg0); |
| break; |
| } |
| case AdbMessage::kCommandWRTE: { |
| if (broken_traits_) { |
| broken_ = true; |
| return; |
| } |
| |
| auto it = local_sockets_.find(current_message_->arg0); |
| if (it == local_sockets_.end()) |
| return; |
| |
| DCHECK(current_message_->arg1); |
| WriteResponse(current_message_->arg1, |
| current_message_->arg0, |
| AdbMessage::kCommandOKAY, |
| std::string()); |
| it->second->Receive(current_message_->body); |
| break; |
| } |
| case AdbMessage::kCommandOPEN: { |
| DCHECK(!current_message_->arg1); |
| DCHECK(current_message_->arg0); |
| std::string response; |
| WriteResponse(++last_local_socket_, |
| current_message_->arg0, |
| AdbMessage::kCommandOKAY, |
| std::string()); |
| local_sockets_[current_message_->arg0] = |
| std::make_unique<MockLocalSocket>( |
| base::BindRepeating(&FakeAndroidUsbDevice::WriteResponse, |
| base::Unretained(this), last_local_socket_, |
| current_message_->arg0), |
| kDeviceSerial, |
| current_message_->body.substr( |
| 0, current_message_->body.size() - 1)); |
| return; |
| } |
| default: { |
| return; |
| } |
| } |
| ProcessQueries(); |
| } |
| |
| void WriteResponse(int arg0, int arg1, int command, const std::string& body) { |
| append(command); |
| append(arg0); |
| append(arg1); |
| bool add_zero = !body.empty() && (command != AdbMessage::kCommandWRTE); |
| append(static_cast<uint32_t>(body.size() + (add_zero ? 1 : 0))); |
| append(Checksum(body)); |
| append(command ^ 0xffffffff); |
| const auto* body_head = reinterpret_cast<const uint8_t*>(body.data()); |
| std::copy(body_head, body_head + body.size(), |
| std::back_inserter(output_buffer_)); |
| if (add_zero) { |
| output_buffer_.push_back(0); |
| } |
| ProcessQueries(); |
| } |
| |
| void ProcessQueries() { |
| if (queries_.empty()) |
| return; |
| |
| if (broken_) { |
| Query query = std::move(queries_.front()); |
| queries_.pop(); |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(query.callback), |
| UsbTransferStatus::TRANSFER_ERROR, |
| std::vector<uint8_t>())); |
| return; |
| } |
| |
| if (queries_.front().size > output_buffer_.size()) |
| return; |
| |
| Query query = std::move(queries_.front()); |
| queries_.pop(); |
| std::vector<uint8_t> response_buffer; |
| std::copy(output_buffer_.begin(), output_buffer_.begin() + query.size, |
| std::back_inserter(response_buffer)); |
| output_buffer_.erase(output_buffer_.begin(), |
| output_buffer_.begin() + query.size); |
| |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::BindOnce(std::move(query.callback), UsbTransferStatus::COMPLETED, |
| std::move(response_buffer))); |
| } |
| |
| struct Query { |
| GenericTransferInCallback callback; |
| size_t size; |
| |
| Query(GenericTransferInCallback callback, int size) |
| : callback(std::move(callback)), size(size) {} |
| }; |
| |
| uint32_t remaining_body_length_ = 0; |
| std::unique_ptr<AdbMessage> current_message_; |
| std::vector<uint8_t> output_buffer_; |
| base::queue<Query> queries_; |
| std::unordered_map<int, std::unique_ptr<MockLocalSocket>> local_sockets_; |
| int last_local_socket_ = 0; |
| bool broken_ = false; |
| bool broken_traits_ = false; |
| }; |
| |
| class FakeAndroidUsbManager : public FakeUsbDeviceManager { |
| void GetDevice(const std::string& guid, |
| device::mojom::UsbDeviceRequest device_request, |
| device::mojom::UsbDeviceClientPtr device_client) override { |
| DCHECK(base::ContainsKey(devices(), guid)); |
| FakeAndroidUsbDevice::Create(devices()[guid], std::move(device_request), |
| std::move(device_client)); |
| } |
| }; |
| |
| class FakeUsbManagerForCheckingTraits : public FakeAndroidUsbManager { |
| public: |
| FakeUsbManagerForCheckingTraits() : step_(0) {} |
| |
| void GetDevices(device::mojom::UsbEnumerationOptionsPtr options, |
| GetDevicesCallback callback) override { |
| std::vector<device::mojom::UsbDeviceInfoPtr> device_infos; |
| // This switch should be kept in sync with |
| // AndroidUsbBrowserTest::DeviceCountChanged. |
| switch (step_) { |
| case 0: |
| // No devices. |
| break; |
| case 1: |
| // Android device. |
| device_infos.push_back( |
| ConstructFakeUsbDevice<AndroidTraits>()->ClonePtr()); |
| break; |
| case 2: |
| // Android and non-android device. |
| device_infos.push_back( |
| ConstructFakeUsbDevice<AndroidTraits>()->ClonePtr()); |
| device_infos.push_back( |
| ConstructFakeUsbDevice<NonAndroidTraits>()->ClonePtr()); |
| break; |
| case 3: |
| // Non-android device. |
| device_infos.push_back( |
| ConstructFakeUsbDevice<NonAndroidTraits>()->ClonePtr()); |
| break; |
| } |
| step_++; |
| std::move(callback).Run(std::move(device_infos)); |
| } |
| |
| private: |
| int step_; |
| }; |
| |
| class DevToolsAndroidBridgeWarmUp |
| : public DevToolsAndroidBridge::DeviceCountListener { |
| public: |
| DevToolsAndroidBridgeWarmUp(base::Closure closure, |
| DevToolsAndroidBridge* adb_bridge) |
| : closure_(closure), adb_bridge_(adb_bridge) {} |
| |
| void DeviceCountChanged(int count) override { |
| adb_bridge_->RemoveDeviceCountListener(this); |
| closure_.Run(); |
| } |
| |
| base::Closure closure_; |
| DevToolsAndroidBridge* adb_bridge_; |
| }; |
| |
| class AndroidUsbDiscoveryTest : public InProcessBrowserTest { |
| protected: |
| AndroidUsbDiscoveryTest() |
| : scheduler_invoked_(0) { |
| } |
| |
| void SetUpOnMainThread() override { |
| adb_bridge_ = |
| DevToolsAndroidBridge::Factory::GetForProfile(browser()->profile()); |
| DCHECK(adb_bridge_); |
| adb_bridge_->set_task_scheduler_for_test(base::Bind( |
| &AndroidUsbDiscoveryTest::ScheduleDeviceCountRequest, |
| base::Unretained(this))); |
| |
| scoped_refptr<UsbDeviceProvider> provider = |
| new UsbDeviceProvider(browser()->profile()); |
| |
| AndroidDeviceManager::DeviceProviders providers; |
| providers.push_back(provider); |
| adb_bridge_->set_device_providers_for_test(providers); |
| runner_ = new content::MessageLoopRunner; |
| |
| // Set a fake USB device manager for AndroidUsbDevice. |
| usb_manager_ = CreateFakeUsbManager(); |
| DCHECK(usb_manager_); |
| device::mojom::UsbDeviceManagerPtrInfo manager_ptr_info; |
| usb_manager_->AddBinding(mojo::MakeRequest(&manager_ptr_info)); |
| adb_bridge_->set_usb_device_manager_for_test(std::move(manager_ptr_info)); |
| } |
| |
| void ScheduleDeviceCountRequest(const base::Closure& request) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| scheduler_invoked_++; |
| base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, request); |
| } |
| |
| virtual std::unique_ptr<FakeUsbDeviceManager> CreateFakeUsbManager() { |
| auto manager = std::make_unique<FakeAndroidUsbManager>(); |
| manager->AddDevice(ConstructFakeUsbDevice<AndroidTraits>()); |
| return manager; |
| } |
| |
| scoped_refptr<content::MessageLoopRunner> runner_; |
| std::unique_ptr<FakeUsbDeviceManager> usb_manager_; |
| DevToolsAndroidBridge* adb_bridge_; |
| int scheduler_invoked_; |
| }; |
| |
| class AndroidUsbCountTest : public AndroidUsbDiscoveryTest { |
| protected: |
| void SetUpOnMainThread() override { |
| AndroidUsbDiscoveryTest::SetUpOnMainThread(); |
| DevToolsAndroidBridgeWarmUp warmup(runner_->QuitClosure(), adb_bridge_); |
| adb_bridge_->AddDeviceCountListener(&warmup); |
| runner_->Run(); |
| runner_ = new content::MessageLoopRunner; |
| } |
| }; |
| |
| class AndroidUsbTraitsTest : public AndroidUsbDiscoveryTest { |
| protected: |
| std::unique_ptr<FakeUsbDeviceManager> CreateFakeUsbManager() override { |
| return std::make_unique<FakeUsbManagerForCheckingTraits>(); |
| } |
| }; |
| |
| class AndroidBreakingUsbTest : public AndroidUsbDiscoveryTest { |
| protected: |
| std::unique_ptr<FakeUsbDeviceManager> CreateFakeUsbManager() override { |
| auto manager = std::make_unique<FakeAndroidUsbManager>(); |
| manager->AddDevice(ConstructFakeUsbDevice<BreakingAndroidTraits>()); |
| return manager; |
| } |
| }; |
| |
| class AndroidNoConfigUsbTest : public AndroidUsbDiscoveryTest { |
| protected: |
| std::unique_ptr<FakeUsbDeviceManager> CreateFakeUsbManager() override { |
| auto manager = std::make_unique<FakeAndroidUsbManager>(); |
| manager->AddDevice(ConstructFakeUsbDevice<AndroidTraits>()); |
| manager->AddDevice(ConstructFakeUsbDevice<NoConfigTraits>()); |
| return manager; |
| } |
| }; |
| |
| class MockListListener : public DevToolsAndroidBridge::DeviceListListener { |
| public: |
| MockListListener(DevToolsAndroidBridge* adb_bridge, |
| const base::Closure& callback) |
| : adb_bridge_(adb_bridge), |
| callback_(callback) { |
| } |
| |
| void DeviceListChanged( |
| const DevToolsAndroidBridge::RemoteDevices& devices) override { |
| if (devices.size() > 0) { |
| for (const auto& device : devices) { |
| if (device->is_connected()) { |
| ASSERT_EQ(kDeviceModel, device->model()); |
| ASSERT_EQ(kDeviceSerial, device->serial()); |
| adb_bridge_->RemoveDeviceListListener(this); |
| callback_.Run(); |
| break; |
| } |
| } |
| } |
| } |
| |
| DevToolsAndroidBridge* adb_bridge_; |
| base::Closure callback_; |
| }; |
| |
| class MockCountListener : public DevToolsAndroidBridge::DeviceCountListener { |
| public: |
| explicit MockCountListener(DevToolsAndroidBridge* adb_bridge) |
| : adb_bridge_(adb_bridge), invoked_(0) {} |
| |
| void DeviceCountChanged(int count) override { |
| ++invoked_; |
| adb_bridge_->RemoveDeviceCountListener(this); |
| Shutdown(); |
| } |
| |
| void Shutdown() { base::RunLoop::QuitCurrentWhenIdleDeprecated(); } |
| |
| DevToolsAndroidBridge* adb_bridge_; |
| int invoked_; |
| }; |
| |
| class MockCountListenerWithReAdd : public MockCountListener { |
| public: |
| explicit MockCountListenerWithReAdd( |
| DevToolsAndroidBridge* adb_bridge) |
| : MockCountListener(adb_bridge), |
| readd_count_(2) { |
| } |
| |
| void DeviceCountChanged(int count) override { |
| ++invoked_; |
| adb_bridge_->RemoveDeviceCountListener(this); |
| if (readd_count_ > 0) { |
| readd_count_--; |
| adb_bridge_->AddDeviceCountListener(this); |
| adb_bridge_->RemoveDeviceCountListener(this); |
| adb_bridge_->AddDeviceCountListener(this); |
| } else { |
| Shutdown(); |
| } |
| } |
| |
| int readd_count_; |
| }; |
| |
| class MockCountListenerWithReAddWhileQueued : public MockCountListener { |
| public: |
| MockCountListenerWithReAddWhileQueued( |
| DevToolsAndroidBridge* adb_bridge) |
| : MockCountListener(adb_bridge), |
| readded_(false) { |
| } |
| |
| void DeviceCountChanged(int count) override { |
| ++invoked_; |
| if (!readded_) { |
| readded_ = true; |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&MockCountListenerWithReAddWhileQueued::ReAdd, |
| base::Unretained(this))); |
| } else { |
| adb_bridge_->RemoveDeviceCountListener(this); |
| Shutdown(); |
| } |
| } |
| |
| void ReAdd() { |
| adb_bridge_->RemoveDeviceCountListener(this); |
| adb_bridge_->AddDeviceCountListener(this); |
| } |
| |
| bool readded_; |
| }; |
| |
| class MockCountListenerForCheckingTraits : public MockCountListener { |
| public: |
| MockCountListenerForCheckingTraits( |
| DevToolsAndroidBridge* adb_bridge) |
| : MockCountListener(adb_bridge), |
| step_(0) { |
| } |
| void DeviceCountChanged(int count) override { |
| switch (step_) { |
| case 0: |
| // Check for 0 devices when no devices present. |
| EXPECT_EQ(0, count); |
| break; |
| case 1: |
| // Check for 1 device when only android device present. |
| EXPECT_EQ(1, count); |
| break; |
| case 2: |
| // Check for 1 device when android and non-android devices present. |
| EXPECT_EQ(1, count); |
| break; |
| case 3: |
| // Check for 0 devices when only non-android devices present. |
| EXPECT_EQ(0, count); |
| adb_bridge_->RemoveDeviceCountListener(this); |
| Shutdown(); |
| break; |
| default: |
| EXPECT_TRUE(false) << "Unknown step " << step_; |
| } |
| step_++; |
| } |
| |
| int step_; |
| }; |
| |
| } // namespace |
| |
| IN_PROC_BROWSER_TEST_F(AndroidUsbDiscoveryTest, TestDeviceDiscovery) { |
| MockListListener listener(adb_bridge_, runner_->QuitClosure()); |
| adb_bridge_->AddDeviceListListener(&listener); |
| runner_->Run(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AndroidBreakingUsbTest, TestDeviceBreaking) { |
| MockListListener listener(adb_bridge_, runner_->QuitClosure()); |
| adb_bridge_->AddDeviceListListener(&listener); |
| runner_->Run(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AndroidNoConfigUsbTest, TestDeviceNoConfig) { |
| MockListListener listener(adb_bridge_, runner_->QuitClosure()); |
| adb_bridge_->AddDeviceListListener(&listener); |
| runner_->Run(); |
| } |
| |
| // Test is flaky. See: http://crbug.com/883680 |
| IN_PROC_BROWSER_TEST_F(AndroidUsbCountTest, |
| DISABLED_TestNoMultipleCallsRemoveInCallback) { |
| MockCountListener listener(adb_bridge_); |
| adb_bridge_->AddDeviceCountListener(&listener); |
| runner_->Run(); |
| EXPECT_EQ(1, listener.invoked_); |
| EXPECT_EQ(listener.invoked_ - 1, scheduler_invoked_); |
| EXPECT_TRUE(base::MessageLoopCurrent::Get()->IsIdleForTesting()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AndroidUsbCountTest, |
| TestNoMultipleCallsRemoveAddInCallback) { |
| MockCountListenerWithReAdd listener(adb_bridge_); |
| adb_bridge_->AddDeviceCountListener(&listener); |
| runner_->Run(); |
| EXPECT_EQ(3, listener.invoked_); |
| EXPECT_EQ(listener.invoked_ - 1, scheduler_invoked_); |
| EXPECT_TRUE(base::MessageLoopCurrent::Get()->IsIdleForTesting()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AndroidUsbCountTest, |
| TestNoMultipleCallsRemoveAddOnStart) { |
| MockCountListener listener(adb_bridge_); |
| adb_bridge_->AddDeviceCountListener(&listener); |
| adb_bridge_->RemoveDeviceCountListener(&listener); |
| adb_bridge_->AddDeviceCountListener(&listener); |
| runner_->Run(); |
| EXPECT_EQ(1, listener.invoked_); |
| EXPECT_EQ(listener.invoked_ - 1, scheduler_invoked_); |
| EXPECT_TRUE(base::MessageLoopCurrent::Get()->IsIdleForTesting()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AndroidUsbCountTest, |
| TestNoMultipleCallsRemoveAddWhileQueued) { |
| MockCountListenerWithReAddWhileQueued listener(adb_bridge_); |
| adb_bridge_->AddDeviceCountListener(&listener); |
| runner_->Run(); |
| EXPECT_EQ(2, listener.invoked_); |
| EXPECT_EQ(listener.invoked_ - 1, scheduler_invoked_); |
| EXPECT_TRUE(base::MessageLoopCurrent::Get()->IsIdleForTesting()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AndroidUsbTraitsTest, TestDeviceCounting) { |
| MockCountListenerForCheckingTraits listener(adb_bridge_); |
| adb_bridge_->AddDeviceCountListener(&listener); |
| runner_->Run(); |
| } |