1// Copyright (c) 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.h"
14#include "chrome/browser/extensions/extension_function_test_utils.h"
15#include "chrome/browser/extensions/extension_test_message_listener.h"
16#include "chrome/browser/ui/browser.h"
17#include "content/public/browser/browser_thread.h"
18#include "testing/gmock/include/gmock/gmock.h"
19
20using testing::_;
21using testing::Return;
22
23using content::BrowserThread;
24
25namespace {
26
27class SerialApiTest : public ExtensionApiTest {
28 public:
29  SerialApiTest() {}
30};
31
32}  // namespace
33
34namespace extensions {
35
36class FakeSerialGetPortsFunction : public AsyncExtensionFunction {
37 public:
38  virtual bool RunImpl() OVERRIDE {
39    base::ListValue* ports = new base::ListValue();
40    ports->Append(Value::CreateStringValue("/dev/fakeserial"));
41    ports->Append(Value::CreateStringValue("\\\\COM800\\"));
42    SetResult(ports);
43    SendResponse(true);
44    return true;
45  }
46 protected:
47  virtual ~FakeSerialGetPortsFunction() {}
48};
49
50class FakeEchoSerialConnection : public SerialConnection {
51 public:
52  explicit FakeEchoSerialConnection(
53      const std::string& port,
54      int bitrate,
55      const std::string& owner_extension_id)
56      : SerialConnection(port, bitrate, owner_extension_id),
57        opened_(true) {
58    Flush();
59    opened_ = false;
60  }
61
62  virtual ~FakeEchoSerialConnection() {
63  }
64
65  virtual bool Open() {
66    DCHECK(!opened_);
67    opened_ = true;
68    return true;
69  }
70
71  virtual void Close() {
72    DCHECK(opened_);
73  }
74
75  virtual void Flush() {
76    DCHECK(opened_);
77    buffer_.clear();
78  }
79
80  virtual int Read(scoped_refptr<net::IOBufferWithSize> io_buffer) {
81    DCHECK(io_buffer->data());
82
83    if (buffer_.empty()) {
84      return 0;
85    }
86    char *data = io_buffer->data();
87    int bytes_to_copy = io_buffer->size();
88    while (bytes_to_copy-- && !buffer_.empty()) {
89      *data++ = buffer_.front();
90      buffer_.pop_front();
91    }
92    return io_buffer->size();
93  }
94
95  virtual int Write(scoped_refptr<net::IOBuffer> io_buffer, int byte_count) {
96    DCHECK(io_buffer.get());
97    DCHECK_GE(byte_count, 0);
98
99    char *data = io_buffer->data();
100    int count = byte_count;
101    while (count--)
102      buffer_.push_back(*data++);
103    return byte_count;
104  }
105
106  MOCK_METHOD1(GetControlSignals, bool(ControlSignals &));
107  MOCK_METHOD1(SetControlSignals, bool(const ControlSignals &));
108
109 private:
110  bool opened_;
111  std::deque<char> buffer_;
112
113  DISALLOW_COPY_AND_ASSIGN(FakeEchoSerialConnection);
114};
115
116class FakeSerialOpenFunction : public SerialOpenFunction {
117 protected:
118  virtual SerialConnection* CreateSerialConnection(
119      const std::string& port,
120      int bitrate,
121      const std::string& owner_extension_id) OVERRIDE {
122    FakeEchoSerialConnection* serial_connection =
123        new FakeEchoSerialConnection(port, bitrate, owner_extension_id);
124    EXPECT_CALL(*serial_connection, GetControlSignals(_)).
125        Times(1).WillOnce(Return(true));
126    EXPECT_CALL(*serial_connection, SetControlSignals(_)).
127        Times(1).WillOnce(Return(true));
128    return serial_connection;
129  }
130  virtual bool DoesPortExist(const std::string& port) OVERRIDE {
131    return true;
132  }
133
134 protected:
135  virtual ~FakeSerialOpenFunction() {}
136};
137
138}  // namespace extensions
139
140ExtensionFunction* FakeSerialGetPortsFunctionFactory() {
141  return new extensions::FakeSerialGetPortsFunction();
142}
143
144ExtensionFunction* FakeSerialOpenFunctionFactory() {
145  return new extensions::FakeSerialOpenFunction();
146}
147
148// Disable SIMULATE_SERIAL_PORTS only if all the following are true:
149//
150// 1. You have an Arduino or compatible board attached to your machine and
151// properly appearing as the first virtual serial port ("first" is very loosely
152// defined as whichever port shows up in serial.getPorts). We've tested only
153// the Atmega32u4 Breakout Board and Arduino Leonardo; note that both these
154// boards are based on the Atmel ATmega32u4, rather than the more common
155// Arduino '328p with either FTDI or '8/16u2 USB interfaces. TODO: test more
156// widely.
157//
158// 2. Your user has permission to read/write the port. For example, this might
159// mean that your user is in the "tty" or "uucp" group on Ubuntu flavors of
160// Linux, or else that the port's path (e.g., /dev/ttyACM0) has global
161// read/write permissions.
162//
163// 3. You have uploaded a program to the board that does a byte-for-byte echo
164// on the virtual serial port at 57600 bps. An example is at
165// chrome/test/data/extensions/api_test/serial/api/serial_arduino_test.ino.
166//
167#define SIMULATE_SERIAL_PORTS (1)
168IN_PROC_BROWSER_TEST_F(SerialApiTest, SerialFakeHardware) {
169  ResultCatcher catcher;
170  catcher.RestrictToProfile(browser()->profile());
171
172#if SIMULATE_SERIAL_PORTS
173  ASSERT_TRUE(ExtensionFunctionDispatcher::OverrideFunction(
174      "serial.getPorts",
175      FakeSerialGetPortsFunctionFactory));
176  ASSERT_TRUE(ExtensionFunctionDispatcher::OverrideFunction(
177      "serial.open",
178      FakeSerialOpenFunctionFactory));
179#endif
180
181  ASSERT_TRUE(RunExtensionTest("serial/api")) << message_;
182}
183
184IN_PROC_BROWSER_TEST_F(SerialApiTest, SerialRealHardware) {
185  ResultCatcher catcher;
186  catcher.RestrictToProfile(browser()->profile());
187
188  ASSERT_TRUE(RunExtensionTest("serial/real_hardware")) << message_;
189}
190