// 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 "media/midi/midi_manager_usb.h"

#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <string>
#include <utility>

#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "media/midi/usb_midi_device.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace midi {

namespace {

using mojom::PortState;
using mojom::Result;

template<typename T, size_t N>
std::vector<T> ToVector(const T (&array)[N]) {
  return std::vector<T>(array, array + N);
}

class Logger {
 public:
  Logger() {}
  ~Logger() {}

  void AddLog(const std::string& message) { log_ += message; }
  std::string TakeLog() {
    std::string result;
    result.swap(log_);
    return result;
  }

 private:
  std::string log_;

  DISALLOW_COPY_AND_ASSIGN(Logger);
};

class FakeUsbMidiDevice : public UsbMidiDevice {
 public:
  explicit FakeUsbMidiDevice(Logger* logger) : logger_(logger) {}
  ~FakeUsbMidiDevice() override {}

  std::vector<uint8_t> GetDescriptors() override {
    logger_->AddLog("UsbMidiDevice::GetDescriptors\n");
    return descriptors_;
  }

  std::string GetManufacturer() override { return manufacturer_; }
  std::string GetProductName() override { return product_name_; }
  std::string GetDeviceVersion() override { return device_version_; }

  void Send(int endpoint_number, const std::vector<uint8_t>& data) override {
    logger_->AddLog("UsbMidiDevice::Send ");
    logger_->AddLog(base::StringPrintf("endpoint = %d data =",
                                       endpoint_number));
    for (size_t i = 0; i < data.size(); ++i)
      logger_->AddLog(base::StringPrintf(" 0x%02x", data[i]));
    logger_->AddLog("\n");
  }

  void SetDescriptors(const std::vector<uint8_t> descriptors) {
    descriptors_ = descriptors;
  }
  void SetManufacturer(const std::string& manufacturer) {
    manufacturer_ = manufacturer;
  }
  void SetProductName(const std::string& product_name) {
    product_name_ = product_name;
  }
  void SetDeviceVersion(const std::string& device_version) {
    device_version_ = device_version;
  }

 private:
  std::vector<uint8_t> descriptors_;
  std::string manufacturer_;
  std::string product_name_;
  std::string device_version_;
  Logger* logger_;

  DISALLOW_COPY_AND_ASSIGN(FakeUsbMidiDevice);
};

class FakeMidiManagerClient : public MidiManagerClient {
 public:
  explicit FakeMidiManagerClient(Logger* logger)
      : complete_start_session_(false),
        result_(Result::NOT_SUPPORTED),
        logger_(logger) {}
  ~FakeMidiManagerClient() override {}

  void AddInputPort(const MidiPortInfo& info) override {
    input_ports_.push_back(info);
  }

  void AddOutputPort(const MidiPortInfo& info) override {
    output_ports_.push_back(info);
  }

  void SetInputPortState(uint32_t port_index, PortState state) override {}

  void SetOutputPortState(uint32_t port_index, PortState state) override {}

  void CompleteStartSession(Result result) override {
    complete_start_session_ = true;
    result_ = result;
  }

  void ReceiveMidiData(uint32_t port_index,
                       const uint8_t* data,
                       size_t size,
                       double timestamp) override {
    logger_->AddLog("MidiManagerClient::ReceiveMidiData ");
    logger_->AddLog(
        base::StringPrintf("usb:port_index = %d data =", port_index));
    for (size_t i = 0; i < size; ++i)
      logger_->AddLog(base::StringPrintf(" 0x%02x", data[i]));
    logger_->AddLog("\n");
  }

  void AccumulateMidiBytesSent(size_t size) override {
    logger_->AddLog("MidiManagerClient::AccumulateMidiBytesSent ");
    // Windows has no "%zu".
    logger_->AddLog(base::StringPrintf("size = %u\n",
                                       static_cast<unsigned>(size)));
  }

  void Detach() override {}

  bool complete_start_session_;
  Result result_;
  MidiPortInfoList input_ports_;
  MidiPortInfoList output_ports_;

 private:
  Logger* logger_;

  DISALLOW_COPY_AND_ASSIGN(FakeMidiManagerClient);
};

class TestUsbMidiDeviceFactory : public UsbMidiDevice::Factory {
 public:
  TestUsbMidiDeviceFactory() {}
  ~TestUsbMidiDeviceFactory() override {}
  void EnumerateDevices(UsbMidiDeviceDelegate* device,
                        Callback callback) override {
    callback_ = callback;
  }

  Callback callback_;

 private:
  DISALLOW_COPY_AND_ASSIGN(TestUsbMidiDeviceFactory);
};

class MidiManagerUsbForTesting : public MidiManagerUsb {
 public:
  explicit MidiManagerUsbForTesting(
      std::unique_ptr<UsbMidiDevice::Factory> device_factory)
      : MidiManagerUsb(std::move(device_factory)) {}
  ~MidiManagerUsbForTesting() override {}

  void CallCompleteInitialization(Result result) {
    CompleteInitialization(result);
    base::RunLoop run_loop;
    run_loop.RunUntilIdle();
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(MidiManagerUsbForTesting);
};

class MidiManagerUsbTest : public ::testing::Test {
 public:
  MidiManagerUsbTest() : message_loop_(new base::MessageLoop) {
    std::unique_ptr<TestUsbMidiDeviceFactory> factory(
        new TestUsbMidiDeviceFactory);
    factory_ = factory.get();
    manager_.reset(new MidiManagerUsbForTesting(std::move(factory)));
  }
  ~MidiManagerUsbTest() override {
    manager_->Shutdown();
    base::RunLoop run_loop;
    run_loop.RunUntilIdle();

    std::string leftover_logs = logger_.TakeLog();
    if (!leftover_logs.empty()) {
      ADD_FAILURE() << "Log should be empty: " << leftover_logs;
    }
  }

 protected:
  void Initialize() {
    client_.reset(new FakeMidiManagerClient(&logger_));
    manager_->StartSession(client_.get());
  }

  void Finalize() {
    manager_->EndSession(client_.get());
  }

  bool IsInitializationCallbackInvoked() {
    return client_->complete_start_session_;
  }

  Result GetInitializationResult() { return client_->result_; }

  void RunCallbackUntilCallbackInvoked(
      bool result, UsbMidiDevice::Devices* devices) {
    factory_->callback_.Run(result, devices);
    while (!client_->complete_start_session_) {
      base::RunLoop run_loop;
      run_loop.RunUntilIdle();
    }
  }

  const MidiPortInfoList& input_ports() { return client_->input_ports_; }
  const MidiPortInfoList& output_ports() { return client_->output_ports_; }

  std::unique_ptr<MidiManagerUsbForTesting> manager_;
  std::unique_ptr<FakeMidiManagerClient> client_;
  // Owned by manager_.
  TestUsbMidiDeviceFactory* factory_;
  Logger logger_;

 private:
  std::unique_ptr<base::MessageLoop> message_loop_;

  DISALLOW_COPY_AND_ASSIGN(MidiManagerUsbTest);
};


TEST_F(MidiManagerUsbTest, Initialize) {
  std::unique_ptr<FakeUsbMidiDevice> device(new FakeUsbMidiDevice(&logger_));
  uint8_t descriptors[] = {
      0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x08, 0x86, 0x1a, 0x2d, 0x75,
      0x54, 0x02, 0x00, 0x02, 0x00, 0x01, 0x09, 0x02, 0x75, 0x00, 0x02, 0x01,
      0x00, 0x80, 0x30, 0x09, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
      0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01, 0x09, 0x04, 0x01,
      0x00, 0x02, 0x01, 0x03, 0x00, 0x00, 0x07, 0x24, 0x01, 0x00, 0x01, 0x51,
      0x00, 0x06, 0x24, 0x02, 0x01, 0x02, 0x00, 0x06, 0x24, 0x02, 0x01, 0x03,
      0x00, 0x06, 0x24, 0x02, 0x02, 0x06, 0x00, 0x09, 0x24, 0x03, 0x01, 0x07,
      0x01, 0x06, 0x01, 0x00, 0x09, 0x24, 0x03, 0x02, 0x04, 0x01, 0x02, 0x01,
      0x00, 0x09, 0x24, 0x03, 0x02, 0x05, 0x01, 0x03, 0x01, 0x00, 0x09, 0x05,
      0x02, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x06, 0x25, 0x01, 0x02, 0x02,
      0x03, 0x09, 0x05, 0x82, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x05, 0x25,
      0x01, 0x01, 0x07,
  };
  device->SetDescriptors(ToVector(descriptors));
  device->SetManufacturer("vendor1");
  device->SetProductName("device1");
  device->SetDeviceVersion("1.02");

  Initialize();
  ScopedVector<UsbMidiDevice> devices;
  devices.push_back(std::move(device));
  EXPECT_FALSE(IsInitializationCallbackInvoked());
  RunCallbackUntilCallbackInvoked(true, &devices);
  EXPECT_EQ(Result::OK, GetInitializationResult());

  ASSERT_EQ(1u, input_ports().size());
  EXPECT_EQ("usb:port-0-2", input_ports()[0].id);
  EXPECT_EQ("vendor1", input_ports()[0].manufacturer);
  EXPECT_EQ("device1", input_ports()[0].name);
  EXPECT_EQ("1.02", input_ports()[0].version);

  ASSERT_EQ(2u, output_ports().size());
  EXPECT_EQ("usb:port-0-0", output_ports()[0].id);
  EXPECT_EQ("vendor1", output_ports()[0].manufacturer);
  EXPECT_EQ("device1", output_ports()[0].name);
  EXPECT_EQ("1.02", output_ports()[0].version);
  EXPECT_EQ("usb:port-0-1", output_ports()[1].id);
  EXPECT_EQ("vendor1", output_ports()[1].manufacturer);
  EXPECT_EQ("device1", output_ports()[1].name);
  EXPECT_EQ("1.02", output_ports()[1].version);

  ASSERT_TRUE(manager_->input_stream());
  std::vector<UsbMidiJack> jacks = manager_->input_stream()->jacks();
  ASSERT_EQ(2u, manager_->output_streams().size());
  EXPECT_EQ(2u, manager_->output_streams()[0]->jack().jack_id);
  EXPECT_EQ(3u, manager_->output_streams()[1]->jack().jack_id);
  ASSERT_EQ(1u, jacks.size());
  EXPECT_EQ(2, jacks[0].endpoint_number());

  EXPECT_EQ("UsbMidiDevice::GetDescriptors\n", logger_.TakeLog());
}

TEST_F(MidiManagerUsbTest, InitializeMultipleDevices) {
  std::unique_ptr<FakeUsbMidiDevice> device1(new FakeUsbMidiDevice(&logger_));
  std::unique_ptr<FakeUsbMidiDevice> device2(new FakeUsbMidiDevice(&logger_));
  uint8_t descriptors[] = {
      0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x08, 0x86, 0x1a, 0x2d, 0x75,
      0x54, 0x02, 0x00, 0x02, 0x00, 0x01, 0x09, 0x02, 0x75, 0x00, 0x02, 0x01,
      0x00, 0x80, 0x30, 0x09, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
      0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01, 0x09, 0x04, 0x01,
      0x00, 0x02, 0x01, 0x03, 0x00, 0x00, 0x07, 0x24, 0x01, 0x00, 0x01, 0x51,
      0x00, 0x06, 0x24, 0x02, 0x01, 0x02, 0x00, 0x06, 0x24, 0x02, 0x01, 0x03,
      0x00, 0x06, 0x24, 0x02, 0x02, 0x06, 0x00, 0x09, 0x24, 0x03, 0x01, 0x07,
      0x01, 0x06, 0x01, 0x00, 0x09, 0x24, 0x03, 0x02, 0x04, 0x01, 0x02, 0x01,
      0x00, 0x09, 0x24, 0x03, 0x02, 0x05, 0x01, 0x03, 0x01, 0x00, 0x09, 0x05,
      0x02, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x06, 0x25, 0x01, 0x02, 0x02,
      0x03, 0x09, 0x05, 0x82, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x05, 0x25,
      0x01, 0x01, 0x07,
  };
  device1->SetDescriptors(ToVector(descriptors));
  device1->SetManufacturer("vendor1");
  device1->SetProductName("device1");
  device1->SetDeviceVersion("1.02");
  device2->SetDescriptors(ToVector(descriptors));
  device2->SetManufacturer("vendor2");
  device2->SetProductName("device2");
  device2->SetDeviceVersion("98.76");

  Initialize();
  ScopedVector<UsbMidiDevice> devices;
  devices.push_back(std::move(device1));
  devices.push_back(std::move(device2));
  EXPECT_FALSE(IsInitializationCallbackInvoked());
  RunCallbackUntilCallbackInvoked(true, &devices);
  EXPECT_EQ(Result::OK, GetInitializationResult());

  ASSERT_EQ(2u, input_ports().size());
  EXPECT_EQ("usb:port-0-2", input_ports()[0].id);
  EXPECT_EQ("vendor1", input_ports()[0].manufacturer);
  EXPECT_EQ("device1", input_ports()[0].name);
  EXPECT_EQ("1.02", input_ports()[0].version);
  EXPECT_EQ("usb:port-1-2", input_ports()[1].id);
  EXPECT_EQ("vendor2", input_ports()[1].manufacturer);
  EXPECT_EQ("device2", input_ports()[1].name);
  EXPECT_EQ("98.76", input_ports()[1].version);

  ASSERT_EQ(4u, output_ports().size());
  EXPECT_EQ("usb:port-0-0", output_ports()[0].id);
  EXPECT_EQ("vendor1", output_ports()[0].manufacturer);
  EXPECT_EQ("device1", output_ports()[0].name);
  EXPECT_EQ("1.02", output_ports()[0].version);
  EXPECT_EQ("usb:port-0-1", output_ports()[1].id);
  EXPECT_EQ("vendor1", output_ports()[1].manufacturer);
  EXPECT_EQ("device1", output_ports()[1].name);
  EXPECT_EQ("1.02", output_ports()[1].version);
  EXPECT_EQ("usb:port-1-0", output_ports()[2].id);
  EXPECT_EQ("vendor2", output_ports()[2].manufacturer);
  EXPECT_EQ("device2", output_ports()[2].name);
  EXPECT_EQ("98.76", output_ports()[2].version);
  EXPECT_EQ("usb:port-1-1", output_ports()[3].id);
  EXPECT_EQ("vendor2", output_ports()[3].manufacturer);
  EXPECT_EQ("device2", output_ports()[3].name);
  EXPECT_EQ("98.76", output_ports()[3].version);

  ASSERT_TRUE(manager_->input_stream());
  std::vector<UsbMidiJack> jacks = manager_->input_stream()->jacks();
  ASSERT_EQ(4u, manager_->output_streams().size());
  EXPECT_EQ(2u, manager_->output_streams()[0]->jack().jack_id);
  EXPECT_EQ(3u, manager_->output_streams()[1]->jack().jack_id);
  ASSERT_EQ(2u, jacks.size());
  EXPECT_EQ(2, jacks[0].endpoint_number());

  EXPECT_EQ(
      "UsbMidiDevice::GetDescriptors\n"
      "UsbMidiDevice::GetDescriptors\n",
      logger_.TakeLog());
}

TEST_F(MidiManagerUsbTest, InitializeFail) {
  Initialize();

  EXPECT_FALSE(IsInitializationCallbackInvoked());
  RunCallbackUntilCallbackInvoked(false, NULL);
  EXPECT_EQ(Result::INITIALIZATION_ERROR, GetInitializationResult());
}

TEST_F(MidiManagerUsbTest, InitializeFailBecauseOfInvalidDescriptors) {
  std::unique_ptr<FakeUsbMidiDevice> device(new FakeUsbMidiDevice(&logger_));
  uint8_t descriptors[] = {0x04};
  device->SetDescriptors(ToVector(descriptors));

  Initialize();
  ScopedVector<UsbMidiDevice> devices;
  devices.push_back(std::move(device));
  EXPECT_FALSE(IsInitializationCallbackInvoked());
  RunCallbackUntilCallbackInvoked(true, &devices);
  EXPECT_EQ(Result::INITIALIZATION_ERROR, GetInitializationResult());
  EXPECT_EQ("UsbMidiDevice::GetDescriptors\n", logger_.TakeLog());
}

TEST_F(MidiManagerUsbTest, Send) {
  Initialize();
  std::unique_ptr<FakeUsbMidiDevice> device(new FakeUsbMidiDevice(&logger_));
  uint8_t descriptors[] = {
      0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x08, 0x86, 0x1a, 0x2d, 0x75,
      0x54, 0x02, 0x00, 0x02, 0x00, 0x01, 0x09, 0x02, 0x75, 0x00, 0x02, 0x01,
      0x00, 0x80, 0x30, 0x09, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
      0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01, 0x09, 0x04, 0x01,
      0x00, 0x02, 0x01, 0x03, 0x00, 0x00, 0x07, 0x24, 0x01, 0x00, 0x01, 0x51,
      0x00, 0x06, 0x24, 0x02, 0x01, 0x02, 0x00, 0x06, 0x24, 0x02, 0x01, 0x03,
      0x00, 0x06, 0x24, 0x02, 0x02, 0x06, 0x00, 0x09, 0x24, 0x03, 0x01, 0x07,
      0x01, 0x06, 0x01, 0x00, 0x09, 0x24, 0x03, 0x02, 0x04, 0x01, 0x02, 0x01,
      0x00, 0x09, 0x24, 0x03, 0x02, 0x05, 0x01, 0x03, 0x01, 0x00, 0x09, 0x05,
      0x02, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x06, 0x25, 0x01, 0x02, 0x02,
      0x03, 0x09, 0x05, 0x82, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x05, 0x25,
      0x01, 0x01, 0x07,
  };

  device->SetDescriptors(ToVector(descriptors));
  uint8_t data[] = {
      0x90, 0x45, 0x7f, 0xf0, 0x00, 0x01, 0xf7,
  };

  ScopedVector<UsbMidiDevice> devices;
  devices.push_back(std::move(device));
  EXPECT_FALSE(IsInitializationCallbackInvoked());
  RunCallbackUntilCallbackInvoked(true, &devices);
  EXPECT_EQ(Result::OK, GetInitializationResult());
  ASSERT_EQ(2u, manager_->output_streams().size());

  manager_->DispatchSendMidiData(client_.get(), 1, ToVector(data), 0);
  // Since UsbMidiDevice::Send is posted as a task, RunLoop should run to
  // invoke the task.
  base::RunLoop run_loop;
  run_loop.RunUntilIdle();
  EXPECT_EQ("UsbMidiDevice::GetDescriptors\n"
            "UsbMidiDevice::Send endpoint = 2 data = "
            "0x19 0x90 0x45 0x7f "
            "0x14 0xf0 0x00 0x01 "
            "0x15 0xf7 0x00 0x00\n"
            "MidiManagerClient::AccumulateMidiBytesSent size = 7\n",
            logger_.TakeLog());
}

TEST_F(MidiManagerUsbTest, SendFromCompromizedRenderer) {
  std::unique_ptr<FakeUsbMidiDevice> device(new FakeUsbMidiDevice(&logger_));
  uint8_t descriptors[] = {
      0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x08, 0x86, 0x1a, 0x2d, 0x75,
      0x54, 0x02, 0x00, 0x02, 0x00, 0x01, 0x09, 0x02, 0x75, 0x00, 0x02, 0x01,
      0x00, 0x80, 0x30, 0x09, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
      0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01, 0x09, 0x04, 0x01,
      0x00, 0x02, 0x01, 0x03, 0x00, 0x00, 0x07, 0x24, 0x01, 0x00, 0x01, 0x51,
      0x00, 0x06, 0x24, 0x02, 0x01, 0x02, 0x00, 0x06, 0x24, 0x02, 0x01, 0x03,
      0x00, 0x06, 0x24, 0x02, 0x02, 0x06, 0x00, 0x09, 0x24, 0x03, 0x01, 0x07,
      0x01, 0x06, 0x01, 0x00, 0x09, 0x24, 0x03, 0x02, 0x04, 0x01, 0x02, 0x01,
      0x00, 0x09, 0x24, 0x03, 0x02, 0x05, 0x01, 0x03, 0x01, 0x00, 0x09, 0x05,
      0x02, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x06, 0x25, 0x01, 0x02, 0x02,
      0x03, 0x09, 0x05, 0x82, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x05, 0x25,
      0x01, 0x01, 0x07,
  };

  device->SetDescriptors(ToVector(descriptors));
  uint8_t data[] = {
      0x90, 0x45, 0x7f, 0xf0, 0x00, 0x01, 0xf7,
  };

  Initialize();
  ScopedVector<UsbMidiDevice> devices;
  devices.push_back(std::move(device));
  EXPECT_FALSE(IsInitializationCallbackInvoked());
  RunCallbackUntilCallbackInvoked(true, &devices);
  EXPECT_EQ(Result::OK, GetInitializationResult());
  ASSERT_EQ(2u, manager_->output_streams().size());
  EXPECT_EQ("UsbMidiDevice::GetDescriptors\n", logger_.TakeLog());

  // The specified port index is invalid. The manager must ignore the request.
  manager_->DispatchSendMidiData(client_.get(), 99, ToVector(data), 0);
  EXPECT_EQ("", logger_.TakeLog());

  // The specified port index is invalid. The manager must ignore the request.
  manager_->DispatchSendMidiData(client_.get(), 2, ToVector(data), 0);
  EXPECT_EQ("", logger_.TakeLog());
}

TEST_F(MidiManagerUsbTest, Receive) {
  std::unique_ptr<FakeUsbMidiDevice> device(new FakeUsbMidiDevice(&logger_));
  uint8_t descriptors[] = {
      0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x08, 0x86, 0x1a, 0x2d, 0x75,
      0x54, 0x02, 0x00, 0x02, 0x00, 0x01, 0x09, 0x02, 0x75, 0x00, 0x02, 0x01,
      0x00, 0x80, 0x30, 0x09, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
      0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01, 0x09, 0x04, 0x01,
      0x00, 0x02, 0x01, 0x03, 0x00, 0x00, 0x07, 0x24, 0x01, 0x00, 0x01, 0x51,
      0x00, 0x06, 0x24, 0x02, 0x01, 0x02, 0x00, 0x06, 0x24, 0x02, 0x01, 0x03,
      0x00, 0x06, 0x24, 0x02, 0x02, 0x06, 0x00, 0x09, 0x24, 0x03, 0x01, 0x07,
      0x01, 0x06, 0x01, 0x00, 0x09, 0x24, 0x03, 0x02, 0x04, 0x01, 0x02, 0x01,
      0x00, 0x09, 0x24, 0x03, 0x02, 0x05, 0x01, 0x03, 0x01, 0x00, 0x09, 0x05,
      0x02, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x06, 0x25, 0x01, 0x02, 0x02,
      0x03, 0x09, 0x05, 0x82, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x05, 0x25,
      0x01, 0x01, 0x07,
  };

  device->SetDescriptors(ToVector(descriptors));
  uint8_t data[] = {
      0x09, 0x90, 0x45, 0x7f, 0x04, 0xf0, 0x00,
      0x01, 0x49, 0x90, 0x88, 0x99,  // This data should be ignored (CN = 4).
      0x05, 0xf7, 0x00, 0x00,
  };

  Initialize();
  ScopedVector<UsbMidiDevice> devices;
  UsbMidiDevice* device_raw = device.get();
  devices.push_back(std::move(device));
  EXPECT_FALSE(IsInitializationCallbackInvoked());
  RunCallbackUntilCallbackInvoked(true, &devices);
  EXPECT_EQ(Result::OK, GetInitializationResult());

  manager_->ReceiveUsbMidiData(device_raw, 2, data, arraysize(data),
                               base::TimeTicks());
  Finalize();

  EXPECT_EQ(
      "UsbMidiDevice::GetDescriptors\n"
      "MidiManagerClient::ReceiveMidiData usb:port_index = 0 "
      "data = 0x90 0x45 0x7f\n"
      "MidiManagerClient::ReceiveMidiData usb:port_index = 0 "
      "data = 0xf0 0x00 0x01\n"
      "MidiManagerClient::ReceiveMidiData usb:port_index = 0 data = 0xf7\n",
      logger_.TakeLog());
}

TEST_F(MidiManagerUsbTest, AttachDevice) {
  uint8_t descriptors[] = {
      0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x08, 0x86, 0x1a, 0x2d, 0x75,
      0x54, 0x02, 0x00, 0x02, 0x00, 0x01, 0x09, 0x02, 0x75, 0x00, 0x02, 0x01,
      0x00, 0x80, 0x30, 0x09, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
      0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01, 0x09, 0x04, 0x01,
      0x00, 0x02, 0x01, 0x03, 0x00, 0x00, 0x07, 0x24, 0x01, 0x00, 0x01, 0x51,
      0x00, 0x06, 0x24, 0x02, 0x01, 0x02, 0x00, 0x06, 0x24, 0x02, 0x01, 0x03,
      0x00, 0x06, 0x24, 0x02, 0x02, 0x06, 0x00, 0x09, 0x24, 0x03, 0x01, 0x07,
      0x01, 0x06, 0x01, 0x00, 0x09, 0x24, 0x03, 0x02, 0x04, 0x01, 0x02, 0x01,
      0x00, 0x09, 0x24, 0x03, 0x02, 0x05, 0x01, 0x03, 0x01, 0x00, 0x09, 0x05,
      0x02, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x06, 0x25, 0x01, 0x02, 0x02,
      0x03, 0x09, 0x05, 0x82, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x05, 0x25,
      0x01, 0x01, 0x07,
  };

  Initialize();
  ScopedVector<UsbMidiDevice> devices;
  EXPECT_FALSE(IsInitializationCallbackInvoked());
  RunCallbackUntilCallbackInvoked(true, &devices);
  EXPECT_EQ(Result::OK, GetInitializationResult());

  ASSERT_EQ(0u, input_ports().size());
  ASSERT_EQ(0u, output_ports().size());
  ASSERT_TRUE(manager_->input_stream());
  std::vector<UsbMidiJack> jacks = manager_->input_stream()->jacks();
  ASSERT_EQ(0u, manager_->output_streams().size());
  ASSERT_EQ(0u, jacks.size());
  EXPECT_EQ("", logger_.TakeLog());

  std::unique_ptr<FakeUsbMidiDevice> new_device(
      new FakeUsbMidiDevice(&logger_));
  new_device->SetDescriptors(ToVector(descriptors));
  manager_->OnDeviceAttached(std::move(new_device));

  ASSERT_EQ(1u, input_ports().size());
  ASSERT_EQ(2u, output_ports().size());
  ASSERT_TRUE(manager_->input_stream());
  jacks = manager_->input_stream()->jacks();
  ASSERT_EQ(2u, manager_->output_streams().size());
  EXPECT_EQ(2u, manager_->output_streams()[0]->jack().jack_id);
  EXPECT_EQ(3u, manager_->output_streams()[1]->jack().jack_id);
  ASSERT_EQ(1u, jacks.size());
  EXPECT_EQ(2, jacks[0].endpoint_number());
  EXPECT_EQ("UsbMidiDevice::GetDescriptors\n", logger_.TakeLog());
}

}  // namespace

}  // namespace midi
