1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <deque>
6#include <string>
7#include <vector>
8
9#include "base/memory/scoped_ptr.h"
10#include "chrome/browser/extensions/api/serial/serial_api.h"
11#include "chrome/browser/extensions/api/serial/serial_connection.h"
12#include "chrome/browser/extensions/extension_apitest.h"
13#include "chrome/browser/extensions/extension_function_test_utils.h"
14#include "chrome/browser/extensions/extension_test_message_listener.h"
15#include "chrome/browser/ui/browser.h"
16#include "chrome/common/extensions/api/serial.h"
17#include "content/public/browser/browser_thread.h"
18#include "extensions/browser/extension_function.h"
19#include "testing/gmock/include/gmock/gmock.h"
20
21using testing::_;
22using testing::Return;
23
24using content::BrowserThread;
25
26namespace {
27
28class SerialApiTest : public ExtensionApiTest {
29 public:
30  SerialApiTest() {}
31};
32
33}  // namespace
34
35namespace extensions {
36
37class FakeSerialGetDevicesFunction : public AsyncExtensionFunction {
38 public:
39  virtual bool RunAsync() OVERRIDE {
40    base::ListValue* devices = new base::ListValue();
41    base::DictionaryValue* device0 = new base::DictionaryValue();
42    device0->SetString("path", "/dev/fakeserial");
43    base::DictionaryValue* device1 = new base::DictionaryValue();
44    device1->SetString("path", "\\\\COM800\\");
45    devices->Append(device0);
46    devices->Append(device1);
47    SetResult(devices);
48    SendResponse(true);
49    return true;
50  }
51 protected:
52  virtual ~FakeSerialGetDevicesFunction() {}
53};
54
55class FakeEchoSerialConnection : public SerialConnection {
56 public:
57  explicit FakeEchoSerialConnection(
58      const std::string& port,
59      const std::string& owner_extension_id)
60      : SerialConnection(port, owner_extension_id),
61        opened_(false) {
62  }
63
64  virtual ~FakeEchoSerialConnection() {
65  }
66
67  virtual void Open(const OpenCompleteCallback& callback) {
68    DCHECK(!opened_);
69    opened_ = true;
70    callback.Run(true);
71  }
72
73  virtual bool Configure(const api::serial::ConnectionOptions& options) {
74    return true;
75  }
76
77  virtual void Close() {
78    DCHECK(opened_);
79  }
80
81  virtual bool Receive(const ReceiveCompleteCallback& callback) {
82    read_callback_ = callback;
83    return true;
84  }
85
86  virtual bool Send(const std::string& data,
87                    const SendCompleteCallback& callback) {
88    callback.Run(data.length(), api::serial::SEND_ERROR_NONE);
89    if (!read_callback_.is_null()) {
90      read_callback_.Run(data, api::serial::RECEIVE_ERROR_NONE);
91    }
92    return true;
93  }
94
95  virtual bool GetControlSignals(api::serial::DeviceControlSignals* signals)
96      const {
97    signals->dcd = true;
98    signals->cts = true;
99    signals->ri = true;
100    signals->dsr = true;
101    return true;
102  }
103
104  virtual bool GetInfo(api::serial::ConnectionInfo* info) const {
105    info->paused = false;
106    info->persistent = false;
107    info->buffer_size = 4096;
108    info->receive_timeout = 0;
109    info->send_timeout = 0;
110    info->bitrate.reset(new int(9600));
111    info->data_bits = api::serial::DATA_BITS_EIGHT;
112    info->parity_bit = api::serial::PARITY_BIT_NO;
113    info->stop_bits = api::serial::STOP_BITS_ONE;
114    info->cts_flow_control.reset(new bool(false));
115    return true;
116  }
117
118  MOCK_METHOD1(SetControlSignals, bool(const api::serial::HostControlSignals&));
119
120 private:
121  bool opened_;
122  ReceiveCompleteCallback read_callback_;
123
124  DISALLOW_COPY_AND_ASSIGN(FakeEchoSerialConnection);
125};
126
127class FakeSerialConnectFunction : public api::SerialConnectFunction {
128 protected:
129  virtual SerialConnection* CreateSerialConnection(
130      const std::string& port,
131      const std::string& owner_extension_id) const OVERRIDE {
132    FakeEchoSerialConnection* serial_connection =
133        new FakeEchoSerialConnection(port, owner_extension_id);
134    EXPECT_CALL(*serial_connection, SetControlSignals(_)).
135        Times(1).WillOnce(Return(true));
136    return serial_connection;
137  }
138
139 protected:
140  virtual ~FakeSerialConnectFunction() {}
141};
142
143}  // namespace extensions
144
145ExtensionFunction* FakeSerialGetDevicesFunctionFactory() {
146  return new extensions::FakeSerialGetDevicesFunction();
147}
148
149ExtensionFunction* FakeSerialConnectFunctionFactory() {
150  return new extensions::FakeSerialConnectFunction();
151}
152
153// Disable SIMULATE_SERIAL_PORTS only if all the following are true:
154//
155// 1. You have an Arduino or compatible board attached to your machine and
156// properly appearing as the first virtual serial port ("first" is very loosely
157// defined as whichever port shows up in serial.getPorts). We've tested only
158// the Atmega32u4 Breakout Board and Arduino Leonardo; note that both these
159// boards are based on the Atmel ATmega32u4, rather than the more common
160// Arduino '328p with either FTDI or '8/16u2 USB interfaces. TODO: test more
161// widely.
162//
163// 2. Your user has permission to read/write the port. For example, this might
164// mean that your user is in the "tty" or "uucp" group on Ubuntu flavors of
165// Linux, or else that the port's path (e.g., /dev/ttyACM0) has global
166// read/write permissions.
167//
168// 3. You have uploaded a program to the board that does a byte-for-byte echo
169// on the virtual serial port at 57600 bps. An example is at
170// chrome/test/data/extensions/api_test/serial/api/serial_arduino_test.ino.
171//
172#define SIMULATE_SERIAL_PORTS (1)
173IN_PROC_BROWSER_TEST_F(SerialApiTest, SerialFakeHardware) {
174  ResultCatcher catcher;
175  catcher.RestrictToProfile(browser()->profile());
176
177#if SIMULATE_SERIAL_PORTS
178  ASSERT_TRUE(extensions::ExtensionFunctionDispatcher::OverrideFunction(
179      "serial.getDevices", FakeSerialGetDevicesFunctionFactory));
180  ASSERT_TRUE(extensions::ExtensionFunctionDispatcher::OverrideFunction(
181      "serial.connect", FakeSerialConnectFunctionFactory));
182#endif
183
184  ASSERT_TRUE(RunExtensionTest("serial/api")) << message_;
185}
186
187IN_PROC_BROWSER_TEST_F(SerialApiTest, SerialRealHardware) {
188  ResultCatcher catcher;
189  catcher.RestrictToProfile(browser()->profile());
190
191  ASSERT_TRUE(RunExtensionTest("serial/real_hardware")) << message_;
192}
193