blob: d34536114fe057fc197db662a90487798406a179 [file] [log] [blame]
// 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 "content/browser/media/midi_host.h"
#include <stddef.h>
#include <stdint.h>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "media/midi/midi_manager.h"
#include "media/midi/midi_service.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
using midi::mojom::PortState;
const uint8_t kNoteOn[] = {0x90, 0x3c, 0x7f};
enum MidiEventType {
DISPATCH_SEND_MIDI_DATA,
};
struct MidiEvent {
MidiEvent(MidiEventType in_type,
uint32_t in_port_index,
const std::vector<uint8_t>& in_data,
base::TimeTicks in_timestamp)
: type(in_type),
port_index(in_port_index),
data(in_data),
timestamp(in_timestamp) {}
MidiEventType type;
uint32_t port_index;
std::vector<uint8_t> data;
base::TimeTicks timestamp;
};
class FakeMidiManager : public midi::MidiManager {
public:
explicit FakeMidiManager(midi::MidiService* service)
: MidiManager(service), weak_factory_(this) {}
~FakeMidiManager() override = default;
base::WeakPtr<FakeMidiManager> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void DispatchSendMidiData(midi::MidiManagerClient* client,
uint32_t port_index,
const std::vector<uint8_t>& data,
base::TimeTicks timestamp) override {
events_.push_back(
MidiEvent(DISPATCH_SEND_MIDI_DATA, port_index, data, timestamp));
}
std::vector<MidiEvent> events_;
base::WeakPtrFactory<FakeMidiManager> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FakeMidiManager);
};
class FakeMidiManagerFactory : public midi::MidiService::ManagerFactory {
public:
FakeMidiManagerFactory() : weak_factory_(this) {}
~FakeMidiManagerFactory() override = default;
std::unique_ptr<midi::MidiManager> Create(
midi::MidiService* service) override {
std::unique_ptr<FakeMidiManager> manager =
std::make_unique<FakeMidiManager>(service);
manager_ = manager->GetWeakPtr();
return manager;
}
base::WeakPtr<FakeMidiManagerFactory> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
base::WeakPtr<FakeMidiManager> GetCreatedManager() { return manager_; }
private:
base::WeakPtr<FakeMidiManager> manager_;
base::WeakPtrFactory<FakeMidiManagerFactory> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FakeMidiManagerFactory);
};
class MidiHostForTesting : public MidiHost {
public:
MidiHostForTesting(int renderer_process_id, midi::MidiService* midi_service)
: MidiHost(renderer_process_id, midi_service) {}
~MidiHostForTesting() override {}
private:
DISALLOW_COPY_AND_ASSIGN(MidiHostForTesting);
};
class MidiSessionClientForTesting : public midi::mojom::MidiSessionClient {
public:
MidiSessionClientForTesting() = default;
~MidiSessionClientForTesting() override = default;
void AddInputPort(midi::mojom::PortInfoPtr info) override {}
void AddOutputPort(midi::mojom::PortInfoPtr info) override {}
void SetInputPortState(uint32_t port, PortState state) override {}
void SetOutputPortState(uint32_t port, PortState state) override {}
void SessionStarted(midi::mojom::Result result) override {}
void AcknowledgeSentData(uint32_t bytes) override {}
void DataReceived(uint32_t port,
const std::vector<uint8_t>& data,
base::TimeTicks timestamp) override {}
};
class MidiHostTest : public testing::Test {
public:
MidiHostTest() : data_(kNoteOn, kNoteOn + base::size(kNoteOn)), port_id_(0) {
browser_context_ = std::make_unique<TestBrowserContext>();
rph_ = std::make_unique<MockRenderProcessHost>(browser_context_.get());
std::unique_ptr<FakeMidiManagerFactory> factory =
std::make_unique<FakeMidiManagerFactory>();
factory_ = factory->GetWeakPtr();
service_ = std::make_unique<midi::MidiService>(std::move(factory));
host_ = std::make_unique<MidiHostForTesting>(rph_->GetID(), service_.get());
midi::mojom::MidiSessionClientPtr ptr;
midi::mojom::MidiSessionClientRequest request = mojo::MakeRequest(&ptr);
mojo::MakeStrongBinding(std::make_unique<MidiSessionClientForTesting>(),
std::move(request));
midi::mojom::MidiSessionRequest session_request =
mojo::MakeRequest(&session_);
host_->StartSession(std::move(session_request), std::move(ptr));
}
~MidiHostTest() override {
session_.reset();
service_->Shutdown();
RunLoopUntilIdle();
}
protected:
void AddOutputPort() {
const std::string id = base::StringPrintf("i-can-%d", port_id_++);
const std::string manufacturer("yukatan");
const std::string name("doki-doki-pi-pine");
const std::string version("3.14159265359");
PortState state = PortState::CONNECTED;
midi::mojom::PortInfo info(id, manufacturer, name, version, state);
host_->AddOutputPort(info);
}
void OnSendData(uint32_t port) {
host_->SendData(port, data_, base::TimeTicks());
}
size_t GetEventSize() const {
if (!factory_->GetCreatedManager())
return 0U;
return factory_->GetCreatedManager()->events_.size();
}
void CheckSendEventAt(size_t at, uint32_t port) {
base::WeakPtr<FakeMidiManager> manager = factory_->GetCreatedManager();
ASSERT_TRUE(manager);
EXPECT_EQ(DISPATCH_SEND_MIDI_DATA, manager->events_[at].type);
EXPECT_EQ(port, manager->events_[at].port_index);
EXPECT_EQ(data_, manager->events_[at].data);
EXPECT_EQ(base::TimeTicks(), manager->events_[at].timestamp);
}
void RunLoopUntilIdle() {
base::RunLoop run_loop;
run_loop.RunUntilIdle();
}
int GetNumberOfBadMessages() { return rph_->bad_msg_count(); }
private:
TestBrowserThreadBundle thread_bundle_;
std::unique_ptr<BrowserContext> browser_context_;
std::unique_ptr<MockRenderProcessHost> rph_;
std::vector<uint8_t> data_;
int32_t port_id_;
base::WeakPtr<FakeMidiManagerFactory> factory_;
std::unique_ptr<midi::MidiService> service_;
std::unique_ptr<MidiHostForTesting> host_;
midi::mojom::MidiSessionPtr session_;
DISALLOW_COPY_AND_ASSIGN(MidiHostTest);
};
} // namespace
// Test if sending data to out of range port is ignored.
TEST_F(MidiHostTest, OutputPortCheck) {
// Only one output port is available.
AddOutputPort();
// Sending data to port 0 should be delivered.
uint32_t port0 = 0;
OnSendData(port0);
RunLoopUntilIdle();
EXPECT_EQ(1U, GetEventSize());
CheckSendEventAt(0, port0);
// Sending data to port 1 should not be delivered.
uint32_t port1 = 1;
OnSendData(port1);
RunLoopUntilIdle();
EXPECT_EQ(1U, GetEventSize());
EXPECT_EQ(1, GetNumberOfBadMessages());
// Two output ports are available from now on.
AddOutputPort();
// Sending data to port 0 and 1 should be delivered now.
OnSendData(port0);
OnSendData(port1);
RunLoopUntilIdle();
EXPECT_EQ(3U, GetEventSize());
CheckSendEventAt(1, port0);
CheckSendEventAt(2, port1);
}
} // namespace content