blob: 14fbabbd832071a7374667f181fcb56950a6c2e6 [file] [log] [blame]
// 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 <memory>
#include <string>
#include <utility>
#include "base/macros.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "content/public/browser/browser_thread.h"
#include "device/serial/serial_device_enumerator.h"
#include "device/serial/serial_service_impl.h"
#include "device/serial/test_serial_io_handler.h"
#include "extensions/browser/api/serial/serial_api.h"
#include "extensions/browser/api/serial/serial_connection.h"
#include "extensions/browser/api/serial/serial_service_factory.h"
#include "extensions/browser/extension_function.h"
#include "extensions/common/api/serial.h"
#include "extensions/common/switches.h"
#include "extensions/test/result_catcher.h"
#include "testing/gmock/include/gmock/gmock.h"
using testing::_;
using testing::Return;
namespace extensions {
namespace {
class FakeSerialGetDevicesFunction : public AsyncExtensionFunction {
public:
bool RunAsync() override {
std::unique_ptr<base::ListValue> devices(new base::ListValue());
std::unique_ptr<base::DictionaryValue> device0(new base::DictionaryValue());
device0->SetString("path", "/dev/fakeserial");
std::unique_ptr<base::DictionaryValue> device1(new base::DictionaryValue());
device1->SetString("path", "\\\\COM800\\");
devices->Append(std::move(device0));
devices->Append(std::move(device1));
SetResult(std::move(devices));
SendResponse(true);
return true;
}
protected:
~FakeSerialGetDevicesFunction() override {}
};
class FakeSerialDeviceEnumerator : public device::SerialDeviceEnumerator {
public:
~FakeSerialDeviceEnumerator() override {}
mojo::Array<device::serial::DeviceInfoPtr> GetDevices() override {
mojo::Array<device::serial::DeviceInfoPtr> devices;
device::serial::DeviceInfoPtr device0(device::serial::DeviceInfo::New());
device0->path = "/dev/fakeserialmojo";
device::serial::DeviceInfoPtr device1(device::serial::DeviceInfo::New());
device1->path = "\\\\COM800\\";
devices.push_back(std::move(device0));
devices.push_back(std::move(device1));
return devices;
}
};
class FakeEchoSerialIoHandler : public device::TestSerialIoHandler {
public:
FakeEchoSerialIoHandler() {
device_control_signals()->dcd = true;
device_control_signals()->cts = true;
device_control_signals()->ri = true;
device_control_signals()->dsr = true;
EXPECT_CALL(*this, SetControlSignals(_)).Times(1).WillOnce(Return(true));
}
static scoped_refptr<device::SerialIoHandler> Create() {
return new FakeEchoSerialIoHandler();
}
MOCK_METHOD1(SetControlSignals,
bool(const device::serial::HostControlSignals&));
protected:
~FakeEchoSerialIoHandler() override {}
private:
DISALLOW_COPY_AND_ASSIGN(FakeEchoSerialIoHandler);
};
class FakeSerialConnectFunction : public api::SerialConnectFunction {
protected:
SerialConnection* CreateSerialConnection(
const std::string& port,
const std::string& owner_extension_id) const override {
scoped_refptr<FakeEchoSerialIoHandler> io_handler =
new FakeEchoSerialIoHandler;
SerialConnection* serial_connection =
new SerialConnection(port, owner_extension_id);
serial_connection->SetIoHandlerForTest(io_handler);
return serial_connection;
}
protected:
~FakeSerialConnectFunction() override {}
};
class SerialApiTest : public ExtensionApiTest,
public testing::WithParamInterface<bool> {
public:
SerialApiTest() {}
void SetUpCommandLine(base::CommandLine* command_line) override {
ExtensionApiTest::SetUpCommandLine(command_line);
if (GetParam())
command_line->AppendSwitch(switches::kEnableMojoSerialService);
}
void TearDownOnMainThread() override {
SetSerialServiceFactoryForTest(nullptr);
ExtensionApiTest::TearDownOnMainThread();
}
protected:
base::Callback<void(mojo::InterfaceRequest<device::serial::SerialService>)>
serial_service_factory_;
};
ExtensionFunction* FakeSerialGetDevicesFunctionFactory() {
return new FakeSerialGetDevicesFunction();
}
ExtensionFunction* FakeSerialConnectFunctionFactory() {
return new FakeSerialConnectFunction();
}
void CreateTestSerialServiceOnFileThread(
mojo::InterfaceRequest<device::serial::SerialService> request) {
auto io_handler_factory = base::Bind(&FakeEchoSerialIoHandler::Create);
auto connection_factory = new device::SerialConnectionFactory(
io_handler_factory,
content::BrowserThread::GetMessageLoopProxyForThread(
content::BrowserThread::IO));
std::unique_ptr<device::SerialDeviceEnumerator> device_enumerator(
new FakeSerialDeviceEnumerator);
new device::SerialServiceImpl(
connection_factory, std::move(device_enumerator), std::move(request));
}
void CreateTestSerialService(
mojo::InterfaceRequest<device::serial::SerialService> request) {
content::BrowserThread::PostTask(
content::BrowserThread::FILE,
FROM_HERE,
base::Bind(&CreateTestSerialServiceOnFileThread, base::Passed(&request)));
}
} // namespace
// Disable SIMULATE_SERIAL_PORTS only if all the following are true:
//
// 1. You have an Arduino or compatible board attached to your machine and
// properly appearing as the first virtual serial port ("first" is very loosely
// defined as whichever port shows up in serial.getPorts). We've tested only
// the Atmega32u4 Breakout Board and Arduino Leonardo; note that both these
// boards are based on the Atmel ATmega32u4, rather than the more common
// Arduino '328p with either FTDI or '8/16u2 USB interfaces. TODO: test more
// widely.
//
// 2. Your user has permission to read/write the port. For example, this might
// mean that your user is in the "tty" or "uucp" group on Ubuntu flavors of
// Linux, or else that the port's path (e.g., /dev/ttyACM0) has global
// read/write permissions.
//
// 3. You have uploaded a program to the board that does a byte-for-byte echo
// on the virtual serial port at 57600 bps. An example is at
// chrome/test/data/extensions/api_test/serial/api/serial_arduino_test.ino.
//
#define SIMULATE_SERIAL_PORTS (1)
IN_PROC_BROWSER_TEST_P(SerialApiTest, SerialFakeHardware) {
ResultCatcher catcher;
catcher.RestrictToBrowserContext(browser()->profile());
#if SIMULATE_SERIAL_PORTS
if (GetParam()) {
serial_service_factory_ = base::Bind(&CreateTestSerialService);
SetSerialServiceFactoryForTest(&serial_service_factory_);
} else {
ASSERT_TRUE(ExtensionFunctionDispatcher::OverrideFunction(
"serial.getDevices", FakeSerialGetDevicesFunctionFactory));
ASSERT_TRUE(ExtensionFunctionDispatcher::OverrideFunction(
"serial.connect", FakeSerialConnectFunctionFactory));
}
#endif
ASSERT_TRUE(RunExtensionTest("serial/api")) << message_;
}
IN_PROC_BROWSER_TEST_P(SerialApiTest, SerialRealHardware) {
ResultCatcher catcher;
catcher.RestrictToBrowserContext(browser()->profile());
ASSERT_TRUE(RunExtensionTest("serial/real_hardware")) << message_;
}
INSTANTIATE_TEST_CASE_P(SerialApiTest, SerialApiTest, testing::Bool());
} // namespace extensions