text: // 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 <set>
  #include <string>
  
  #include "base/bind.h"
  #include "base/macros.h"
  #include "base/memory/scoped_ptr.h"
  #include "base/message_loop/message_loop.h"
  #include "base/run_loop.h"
  #include "base/thread_task_runner_handle.h"
  #include "device/core/device_client.h"
  #include "device/devices_app/usb/device_impl.h"
  #include "device/devices_app/usb/device_manager_impl.h"
  #include "device/devices_app/usb/fake_permission_provider.h"
  #include "device/usb/mock_usb_device.h"
  #include "device/usb/mock_usb_device_handle.h"
  #include "device/usb/mock_usb_service.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  using ::testing::Invoke;
  using ::testing::_;
  
  namespace device {
  namespace usb {
  
  namespace {
  
  class TestDeviceClient : public DeviceClient {
   public:
    TestDeviceClient() {}
    ~TestDeviceClient() override {}
  
    MockUsbService& mock_usb_service() { return mock_usb_service_; }
  
   private:
    // DeviceClient implementation:
    UsbService* GetUsbService() override { return &mock_usb_service_; }
  
    MockUsbService mock_usb_service_;
  };
  
  class USBDeviceManagerImplTest : public testing::Test {
   public:
    USBDeviceManagerImplTest()
        : device_client_(new TestDeviceClient),
          message_loop_(new base::MessageLoop) {}
    ~USBDeviceManagerImplTest() override {}
  
   protected:
    MockUsbService& mock_usb_service() {
      return device_client_->mock_usb_service();
    }
  
    DeviceManagerPtr ConnectToDeviceManager() {
      PermissionProviderPtr permission_provider;
      permission_provider_.Bind(mojo::GetProxy(&permission_provider));
      DeviceManagerPtr device_manager;
      new DeviceManagerImpl(mojo::GetProxy(&device_manager),
                            permission_provider.Pass(),
                            base::ThreadTaskRunnerHandle::Get());
      return device_manager.Pass();
    }
  
   private:
    FakePermissionProvider permission_provider_;
    scoped_ptr<TestDeviceClient> device_client_;
    scoped_ptr<base::MessageLoop> message_loop_;
  };
  
  void ExpectDevicesAndThen(const std::set<std::string>& expected_guids,
                            const base::Closure& continuation,
                            mojo::Array<DeviceInfoPtr> results) {
    EXPECT_EQ(expected_guids.size(), results.size());
    std::set<std::string> actual_guids;
    for (size_t i = 0; i < results.size(); ++i)
      actual_guids.insert(results[i]->guid);
    EXPECT_EQ(expected_guids, actual_guids);
    continuation.Run();
  }
  
  void ExpectDeviceChangesAndThen(
      const std::set<std::string>& expected_added_guids,
      const std::set<std::string>& expected_removed_guids,
      const base::Closure& continuation,
      DeviceChangeNotificationPtr results) {
    EXPECT_EQ(expected_added_guids.size(), results->devices_added.size());
    std::set<std::string> actual_added_guids;
    for (size_t i = 0; i < results->devices_added.size(); ++i)
      actual_added_guids.insert(results->devices_added[i]->guid);
    EXPECT_EQ(expected_added_guids, actual_added_guids);
    EXPECT_EQ(expected_removed_guids.size(), results->devices_removed.size());
    std::set<std::string> actual_removed_guids;
    for (size_t i = 0; i < results->devices_removed.size(); ++i)
      actual_removed_guids.insert(results->devices_removed[i]->guid);
    EXPECT_EQ(expected_removed_guids, actual_removed_guids);
    continuation.Run();
  }
  
  void ExpectDeviceInfoAndThen(const std::string& expected_guid,
                               const base::Closure& continuation,
                               DeviceInfoPtr device_info) {
    ASSERT_TRUE(device_info);
    EXPECT_EQ(expected_guid, device_info->guid);
    continuation.Run();
  }
  
  }  // namespace
  
  // Test basic GetDevices functionality to ensure that all mock devices are
  // returned by the service.
  TEST_F(USBDeviceManagerImplTest, GetDevices) {
    scoped_refptr<MockUsbDevice> device0 =
        new MockUsbDevice(0x1234, 0x5678, "ACME", "Frobinator", "ABCDEF");
    scoped_refptr<MockUsbDevice> device1 =
        new MockUsbDevice(0x1234, 0x5679, "ACME", "Frobinator+", "GHIJKL");
    scoped_refptr<MockUsbDevice> device2 =
        new MockUsbDevice(0x1234, 0x567a, "ACME", "Frobinator Mk II", "MNOPQR");
  
    mock_usb_service().AddDevice(device0);
    mock_usb_service().AddDevice(device1);
    mock_usb_service().AddDevice(device2);
  
    DeviceManagerPtr device_manager = ConnectToDeviceManager();
  
    EnumerationOptionsPtr options = EnumerationOptions::New();
    options->filters = mojo::Array<DeviceFilterPtr>::New(1);
    options->filters[0] = DeviceFilter::New();
    options->filters[0]->has_vendor_id = true;
    options->filters[0]->vendor_id = 0x1234;
  
    std::set<std::string> guids;
    guids.insert(device0->guid());
    guids.insert(device1->guid());
    guids.insert(device2->guid());
  
    base::RunLoop loop;
    device_manager->GetDevices(
        options.Pass(),
        base::Bind(&ExpectDevicesAndThen, guids, loop.QuitClosure()));
    loop.Run();
  }
  
  // Test requesting a single Device by GUID.
  TEST_F(USBDeviceManagerImplTest, GetDevice) {
    scoped_refptr<MockUsbDevice> mock_device =
        new MockUsbDevice(0x1234, 0x5678, "ACME", "Frobinator", "ABCDEF");
  
    mock_usb_service().AddDevice(mock_device);
  
    DeviceManagerPtr device_manager = ConnectToDeviceManager();
  
    {
      base::RunLoop loop;
      DevicePtr device;
      device_manager->GetDevice(mock_device->guid(), mojo::GetProxy(&device));
      device->GetDeviceInfo(base::Bind(&ExpectDeviceInfoAndThen,
                                       mock_device->guid(), loop.QuitClosure()));
      loop.Run();
    }
  
    DevicePtr bad_device;
    device_manager->GetDevice("not a real guid", mojo::GetProxy(&bad_device));
  
    {
      base::RunLoop loop;
      bad_device.set_connection_error_handler(loop.QuitClosure());
      loop.Run();
    }
  }
  
  // Test requesting device enumeration updates with GetDeviceChanges.
  TEST_F(USBDeviceManagerImplTest, GetDeviceChanges) {
    scoped_refptr<MockUsbDevice> device0 =
        new MockUsbDevice(0x1234, 0x5678, "ACME", "Frobinator", "ABCDEF");
    scoped_refptr<MockUsbDevice> device1 =
        new MockUsbDevice(0x1234, 0x5679, "ACME", "Frobinator+", "GHIJKL");
    scoped_refptr<MockUsbDevice> device2 =
        new MockUsbDevice(0x1234, 0x567a, "ACME", "Frobinator Mk II", "MNOPQR");
    scoped_refptr<MockUsbDevice> device3 =
        new MockUsbDevice(0x1234, 0x567b, "ACME", "Frobinator Xtreme", "STUVWX");
  
    mock_usb_service().AddDevice(device0);
  
    DeviceManagerPtr device_manager = ConnectToDeviceManager();
  
    {
      // Call GetDevices once to make sure the device manager is up and running
      // or else we could end up waiting forever for device changes as the next
      // block races with the ServiceThreadHelper startup.
      std::set<std::string> guids;
      guids.insert(device0->guid());
      base::RunLoop loop;
      device_manager->GetDevices(
          nullptr, base::Bind(&ExpectDevicesAndThen, guids, loop.QuitClosure()));
      loop.Run();
    }
  
    mock_usb_service().AddDevice(device1);
    mock_usb_service().AddDevice(device2);
    mock_usb_service().RemoveDevice(device1);
  
    {
      std::set<std::string> added_guids;
      std::set<std::string> removed_guids;
      added_guids.insert(device2->guid());
      base::RunLoop loop;
      device_manager->GetDeviceChanges(base::Bind(&ExpectDeviceChangesAndThen,
                                                  added_guids, removed_guids,
                                                  loop.QuitClosure()));
      loop.Run();
    }
  
    mock_usb_service().RemoveDevice(device0);
    mock_usb_service().RemoveDevice(device2);
    mock_usb_service().AddDevice(device3);
  
    {
      std::set<std::string> added_guids;
      std::set<std::string> removed_guids;
      added_guids.insert(device3->guid());
      removed_guids.insert(device0->guid());
      removed_guids.insert(device2->guid());
      base::RunLoop loop;
      device_manager->GetDeviceChanges(base::Bind(&ExpectDeviceChangesAndThen,
                                                  added_guids, removed_guids,
                                                  loop.QuitClosure()));
      loop.Run();
    }
  }
  
  }  // namespace usb
  }  // namespace device
  