// 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 #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 media { namespace { template std::vector ToVector(const T (&array)[N]) { return std::vector(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) {} virtual ~FakeUsbMidiDevice() {} virtual std::vector GetDescriptor() OVERRIDE { logger_->AddLog("UsbMidiDevice::GetDescriptor\n"); return descriptor_; } virtual void Send(int endpoint_number, const std::vector& 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 SetDescriptor(const std::vector descriptor) { descriptor_ = descriptor; } private: std::vector descriptor_; Logger* logger_; DISALLOW_COPY_AND_ASSIGN(FakeUsbMidiDevice); }; class FakeMidiManagerClient : public MidiManagerClient { public: explicit FakeMidiManagerClient(Logger* logger) : complete_start_session_(false), result_(MIDI_NOT_SUPPORTED), logger_(logger) {} virtual ~FakeMidiManagerClient() {} virtual void CompleteStartSession(int client_id, MidiResult result) OVERRIDE { complete_start_session_ = true; result_ = result; } virtual void ReceiveMidiData(uint32 port_index, const uint8* data, size_t size, double timestamp) OVERRIDE { logger_->AddLog("MidiManagerClient::ReceiveMidiData "); logger_->AddLog(base::StringPrintf("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"); } virtual void AccumulateMidiBytesSent(size_t size) OVERRIDE { logger_->AddLog("MidiManagerClient::AccumulateMidiBytesSent "); // Windows has no "%zu". logger_->AddLog(base::StringPrintf("size = %u\n", static_cast(size))); } bool complete_start_session_; MidiResult result_; private: Logger* logger_; DISALLOW_COPY_AND_ASSIGN(FakeMidiManagerClient); }; class TestUsbMidiDeviceFactory : public UsbMidiDevice::Factory { public: TestUsbMidiDeviceFactory() {} virtual ~TestUsbMidiDeviceFactory() {} virtual void EnumerateDevices(UsbMidiDeviceDelegate* device, Callback callback) OVERRIDE { callback_ = callback; } Callback callback_; private: DISALLOW_COPY_AND_ASSIGN(TestUsbMidiDeviceFactory); }; class MidiManagerUsbForTesting : public MidiManagerUsb { public: explicit MidiManagerUsbForTesting( scoped_ptr device_factory) : MidiManagerUsb(device_factory.PassAs()) {} virtual ~MidiManagerUsbForTesting() {} void CallCompleteInitialization(MidiResult 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) { scoped_ptr factory(new TestUsbMidiDeviceFactory); factory_ = factory.get(); manager_.reset( new MidiManagerUsbForTesting(factory.PassAs())); } virtual ~MidiManagerUsbTest() { 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(), 0); } void Finalize() { manager_->EndSession(client_.get()); } bool IsInitializationCallbackInvoked() { return client_->complete_start_session_; } MidiResult GetInitializationResult() { return client_->result_; } void RunCallbackUntilCallbackInvoked( bool result, UsbMidiDevice::Devices* devices) { factory_->callback_.Run(result, devices); base::RunLoop run_loop; while (!client_->complete_start_session_) run_loop.RunUntilIdle(); } scoped_ptr manager_; scoped_ptr client_; // Owned by manager_. TestUsbMidiDeviceFactory* factory_; Logger logger_; private: scoped_ptr message_loop_; DISALLOW_COPY_AND_ASSIGN(MidiManagerUsbTest); }; TEST_F(MidiManagerUsbTest, Initialize) { scoped_ptr device(new FakeUsbMidiDevice(&logger_)); uint8 descriptor[] = { 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->SetDescriptor(ToVector(descriptor)); Initialize(); ScopedVector devices; devices.push_back(device.release()); EXPECT_FALSE(IsInitializationCallbackInvoked()); RunCallbackUntilCallbackInvoked(true, &devices); EXPECT_EQ(MIDI_OK, GetInitializationResult()); ASSERT_EQ(1u, manager_->input_ports().size()); ASSERT_EQ(2u, manager_->output_ports().size()); ASSERT_TRUE(manager_->input_stream()); std::vector keys = manager_->input_stream()->RegisteredJackKeysForTesting(); 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, keys.size()); EXPECT_EQ(2, keys[0].endpoint_number); EXPECT_EQ("UsbMidiDevice::GetDescriptor\n", logger_.TakeLog()); } TEST_F(MidiManagerUsbTest, InitializeFail) { Initialize(); EXPECT_FALSE(IsInitializationCallbackInvoked()); RunCallbackUntilCallbackInvoked(false, NULL); EXPECT_EQ(MIDI_INITIALIZATION_ERROR, GetInitializationResult()); } TEST_F(MidiManagerUsbTest, InitializeFailBecauseOfInvalidDescriptor) { scoped_ptr device(new FakeUsbMidiDevice(&logger_)); uint8 descriptor[] = {0x04}; device->SetDescriptor(ToVector(descriptor)); Initialize(); ScopedVector devices; devices.push_back(device.release()); EXPECT_FALSE(IsInitializationCallbackInvoked()); RunCallbackUntilCallbackInvoked(true, &devices); EXPECT_EQ(MIDI_INITIALIZATION_ERROR, GetInitializationResult()); EXPECT_EQ("UsbMidiDevice::GetDescriptor\n", logger_.TakeLog()); } TEST_F(MidiManagerUsbTest, Send) { scoped_ptr device(new FakeUsbMidiDevice(&logger_)); FakeMidiManagerClient client(&logger_); uint8 descriptor[] = { 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->SetDescriptor(ToVector(descriptor)); uint8 data[] = { 0x90, 0x45, 0x7f, 0xf0, 0x00, 0x01, 0xf7, }; Initialize(); ScopedVector devices; devices.push_back(device.release()); EXPECT_FALSE(IsInitializationCallbackInvoked()); RunCallbackUntilCallbackInvoked(true, &devices); EXPECT_EQ(MIDI_OK, GetInitializationResult()); ASSERT_EQ(2u, manager_->output_streams().size()); manager_->DispatchSendMidiData(&client, 1, ToVector(data), 0); EXPECT_EQ("UsbMidiDevice::GetDescriptor\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, Receive) { scoped_ptr device(new FakeUsbMidiDevice(&logger_)); uint8 descriptor[] = { 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->SetDescriptor(ToVector(descriptor)); uint8 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 devices; UsbMidiDevice* device_raw = device.get(); devices.push_back(device.release()); EXPECT_FALSE(IsInitializationCallbackInvoked()); RunCallbackUntilCallbackInvoked(true, &devices); EXPECT_EQ(MIDI_OK, GetInitializationResult()); manager_->ReceiveUsbMidiData(device_raw, 2, data, arraysize(data), base::TimeTicks()); Finalize(); EXPECT_EQ("UsbMidiDevice::GetDescriptor\n" "MidiManagerClient::ReceiveMidiData port_index = 0 " "data = 0x90 0x45 0x7f\n" "MidiManagerClient::ReceiveMidiData port_index = 0 " "data = 0xf0 0x00 0x01\n" "MidiManagerClient::ReceiveMidiData port_index = 0 data = 0xf7\n", logger_.TakeLog()); } } // namespace } // namespace media