1 // Copyright 2014 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.
6 * Unit tests for the JS serial service client.
8 * These test that configuration and data are correctly transmitted between the
9 * client and the service. They are launched by
10 * extensions/renderer/api/serial/serial_api_unittest.cc.
13 var test = require('test').binding;
14 var serial = require('serial').binding;
15 var unittestBindings = require('test_environment_specific_bindings');
16 var utils = require('utils');
18 var timeoutManager = new unittestBindings.TimeoutManager();
19 timeoutManager.installGlobals();
23 var connectionId = null;
25 function connect(callback, options) {
26 options = options || {
27 name: 'test connection',
28 bufferSize: BUFFER_SIZE,
29 receiveTimeout: 12345,
33 serial.connect('device', options, test.callbackPass(function(connectionInfo) {
34 connectionId = connectionInfo.connectionId;
36 callback(connectionInfo);
40 // Sets a function to be called once when data is received. Returns a promise
41 // that will resolve once the hook is installed.
42 function addReceiveHook(callback) {
43 return requireAsync('serial_service').then(function(serialService) {
45 var dataReceived = serialService.Connection.prototype.onDataReceived_;
46 serialService.Connection.prototype.onDataReceived_ = function() {
47 var result = $Function.apply(dataReceived, this, arguments);
56 // Sets a function to be called once when a receive error is received. Returns a
57 // promise that will resolve once the hook is installed.
58 function addReceiveErrorHook(callback) {
59 return requireAsync('serial_service').then(function(serialService) {
61 var receiveError = serialService.Connection.prototype.onReceiveError_;
62 serialService.Connection.prototype.onReceiveError_ = function() {
63 var result = $Function.apply(receiveError, this, arguments);
72 function disconnect() {
73 serial.disconnect(connectionId, test.callbackPass(function(success) {
74 test.assertTrue(success);
79 function checkClientConnectionInfo(connectionInfo) {
80 test.assertFalse(connectionInfo.persistent);
81 test.assertEq('test connection', connectionInfo.name);
82 test.assertEq(12345, connectionInfo.receiveTimeout);
83 test.assertEq(6789, connectionInfo.sendTimeout);
84 test.assertEq(BUFFER_SIZE, connectionInfo.bufferSize);
85 test.assertFalse(connectionInfo.paused);
88 function checkServiceConnectionInfo(connectionInfo) {
89 test.assertEq(9600, connectionInfo.bitrate);
90 test.assertEq('eight', connectionInfo.dataBits);
91 test.assertEq('no', connectionInfo.parityBit);
92 test.assertEq('one', connectionInfo.stopBits);
93 test.assertFalse(connectionInfo.ctsFlowControl);
96 function checkConnectionInfo(connectionInfo) {
97 checkClientConnectionInfo(connectionInfo);
98 checkServiceConnectionInfo(connectionInfo);
101 function runReceiveErrorTest(expectedError) {
103 test.listenOnce(serial.onReceiveError, function(result) {
104 serial.getInfo(connectionId, test.callbackPass(function(connectionInfo) {
106 test.assertTrue(connectionInfo.paused);
108 test.assertEq(connectionId, result.connectionId);
109 test.assertEq(expectedError, result.error);
113 function runSendErrorTest(expectedError) {
115 var buffer = new ArrayBuffer(1);
116 serial.send(connectionId, buffer, test.callbackPass(function(sendInfo) {
118 test.assertEq(0, sendInfo.bytesSent);
119 test.assertEq(expectedError, sendInfo.error);
124 function sendData() {
126 var buffer = new ArrayBuffer(data.length);
127 var byteBuffer = new Int8Array(buffer);
128 for (var i = 0; i < data.length; i++) {
129 byteBuffer[i] = data.charCodeAt(i);
131 return utils.promise(serial.send, connectionId, buffer);
134 function checkReceivedData(result) {
136 test.assertEq(connectionId, result.connectionId);
137 test.assertEq(data.length, result.data.byteLength);
138 var resultByteBuffer = new Int8Array(result.data);
139 for (var i = 0; i < data.length; i++) {
140 test.assertEq(data.charCodeAt(i), resultByteBuffer[i]);
144 unittestBindings.exportTests([
145 // Test that getDevices correctly transforms the data returned by the
146 // SerialDeviceEnumerator.
147 function testGetDevices() {
148 serial.getDevices(test.callbackPass(function(devices) {
149 test.assertEq(3, devices.length);
150 test.assertEq(4, $Object.keys(devices[0]).length);
151 test.assertEq('device', devices[0].path);
152 test.assertEq(1234, devices[0].vendorId);
153 test.assertEq(5678, devices[0].productId);
154 test.assertEq('foo', devices[0].displayName);
155 test.assertEq(1, $Object.keys(devices[1]).length);
156 test.assertEq('another_device', devices[1].path);
157 test.assertEq(1, $Object.keys(devices[2]).length);
158 test.assertEq('', devices[2].path);
162 // Test that the correct error message is returned when an error occurs in
163 // connecting to the port. This test uses an IoHandler that fails to connect.
164 function testConnectFail() {
165 serial.connect('device',
166 test.callbackFail('Failed to connect to the port.'));
169 // Test that the correct error message is returned when an error occurs in
170 // calling getPortInfo after connecting to the port. This test uses an
171 // IoHandler that fails on calls to GetPortInfo.
172 function testGetInfoFailOnConnect() {
173 serial.connect('device',
174 test.callbackFail('Failed to connect to the port.'));
177 // Test that the correct error message is returned when an invalid bit-rate
178 // value is passed to connect.
179 function testConnectInvalidBitrate() {
180 serial.connect('device', {bitrate: -1}, test.callbackFail(
181 'Failed to connect to the port.'));
184 // Test that a successful connect returns the expected connection info.
185 function testConnect() {
186 connect(function(connectionInfo) {
188 checkConnectionInfo(connectionInfo);
192 // Test that a connection created with no options has the correct default
194 function testConnectDefaultOptions() {
195 connect(function(connectionInfo) {
197 test.assertEq(9600, connectionInfo.bitrate);
198 test.assertEq('eight', connectionInfo.dataBits);
199 test.assertEq('no', connectionInfo.parityBit);
200 test.assertEq('one', connectionInfo.stopBits);
201 test.assertFalse(connectionInfo.ctsFlowControl);
202 test.assertFalse(connectionInfo.persistent);
203 test.assertEq('', connectionInfo.name);
204 test.assertEq(0, connectionInfo.receiveTimeout);
205 test.assertEq(0, connectionInfo.sendTimeout);
206 test.assertEq(4096, connectionInfo.bufferSize);
210 // Test that a getInfo call correctly converts the service-side info from the
211 // Mojo format and returns both it and the client-side configuration.
212 function testGetInfo() {
214 serial.getInfo(connectionId,
215 test.callbackPass(function(connectionInfo) {
217 checkConnectionInfo(connectionInfo);
222 // Test that only client-side options are returned when the service fails a
223 // getInfo call. This test uses an IoHandler that fails GetPortInfo calls
224 // after the initial call during connect.
225 function testGetInfoFailToGetPortInfo() {
227 serial.getInfo(connectionId,
228 test.callbackPass(function(connectionInfo) {
230 checkClientConnectionInfo(connectionInfo);
231 test.assertFalse('bitrate' in connectionInfo);
232 test.assertFalse('dataBits' in connectionInfo);
233 test.assertFalse('parityBit' in connectionInfo);
234 test.assertFalse('stopBit' in connectionInfo);
235 test.assertFalse('ctsFlowControl' in connectionInfo);
240 // Test that getConnections returns an array containing the open connection.
241 function testGetConnections() {
243 serial.getConnections(test.callbackPass(function(connections) {
245 test.assertEq(1, connections.length);
246 checkConnectionInfo(connections[0]);
251 // Test that getControlSignals correctly converts the Mojo format result. This
252 // test uses an IoHandler that returns values matching the pattern being
254 function testGetControlSignals() {
257 function checkControlSignals(signals) {
261 serial.getControlSignals(
263 test.callbackPass(checkControlSignals));
265 test.assertEq(!!(calls & 1), signals.dcd);
266 test.assertEq(!!(calls & 2), signals.cts);
267 test.assertEq(!!(calls & 4), signals.ri);
268 test.assertEq(!!(calls & 8), signals.dsr);
271 serial.getControlSignals(connectionId,
272 test.callbackPass(checkControlSignals));
276 // Test that setControlSignals correctly converts to the Mojo format result.
277 // This test uses an IoHandler that returns values following the same table of
278 // values as |signalsValues|.
279 function testSetControlSignals() {
281 var signalsValues = [
286 {dtr: false, rts: false},
287 {dtr: true, rts: false},
289 {dtr: false, rts: true},
290 {dtr: true, rts: true},
293 function setControlSignals(success) {
294 if (calls == signalsValues.length) {
297 serial.setControlSignals(connectionId,
298 signalsValues[calls++],
299 test.callbackPass(setControlSignals));
301 test.assertTrue(success);
303 setControlSignals(true);
307 // Test that update correctly passes values to the service only for
308 // service-side options and that all update calls are reflected by the result
309 // of getInfo calls. This test uses an IoHandler that expects corresponding
310 // ConfigurePort calls.
311 function testUpdate() {
313 var optionsValues = [
314 {}, // SetPortOptions is called once during connection.
323 {ctsFlowControl: false},
324 {ctsFlowControl: true},
332 function checkInfo(info) {
333 for (var key in optionsValues[calls]) {
334 test.assertEq(optionsValues[calls][key], info[key]);
338 function setOptions() {
339 if (++calls == optionsValues.length) {
342 serial.update(connectionId,
343 optionsValues[calls],
344 test.callbackPass(function(success) {
345 serial.getInfo(connectionId, test.callbackPass(checkInfo));
346 test.assertTrue(success);
354 // Test that passing an invalid bit-rate reslts in an error.
355 function testUpdateInvalidBitrate() {
357 serial.update(connectionId,
359 test.callbackPass(function(success) {
361 test.assertFalse(success);
366 // Test flush. This test uses an IoHandler that counts the number of flush
368 function testFlush() {
370 serial.flush(connectionId, test.callbackPass(function(success) {
372 test.assertTrue(success);
377 // Test that setPaused values are reflected by the results returned by getInfo
379 function testSetPaused() {
381 serial.setPaused(connectionId, true, test.callbackPass(function() {
382 serial.getInfo(connectionId, test.callbackPass(function(info) {
383 serial.setPaused(connectionId, false, test.callbackPass(function() {
384 serial.getInfo(connectionId, test.callbackPass(function(info) {
385 test.assertFalse(info.paused);
389 test.assertTrue(info.paused);
395 // Test that a send and a receive correctly echoes data. This uses an
396 // IoHandler that echoes data sent to it.
397 function testEcho() {
399 sendData().then(test.callbackPass(function(sendInfo) {
400 test.assertEq(4, sendInfo.bytesSent);
401 test.assertEq(undefined, sendInfo.error);
403 test.listenOnce(serial.onReceive, function(result) {
404 checkReceivedData(result);
410 // Test that a send while another send is in progress returns a pending error.
411 function testSendDuringExistingSend() {
413 sendData().then(test.callbackPass(function(sendInfo) {
414 test.assertEq(4, sendInfo.bytesSent);
415 test.assertEq(undefined, sendInfo.error);
418 sendData().then(test.callbackPass(function(sendInfo) {
419 test.assertEq(0, sendInfo.bytesSent);
420 test.assertEq('pending', sendInfo.error);
425 // Test that a second send after the first finishes is successful. This uses
426 // an IoHandler that echoes data sent to it.
427 function testSendAfterSuccessfulSend() {
429 sendData().then(test.callbackPass(function(sendInfo) {
430 test.assertEq(4, sendInfo.bytesSent);
431 test.assertEq(undefined, sendInfo.error);
433 })).then(test.callbackPass(function(sendInfo) {
434 test.assertEq(4, sendInfo.bytesSent);
435 test.assertEq(undefined, sendInfo.error);
437 // Check that the correct data is echoed twice.
438 test.listenOnce(serial.onReceive, function(result) {
439 checkReceivedData(result);
440 test.listenOnce(serial.onReceive, function(result) {
441 checkReceivedData(result);
448 // Test that a second send after the first fails is successful. This uses an
449 // IoHandler that returns system_error for only the first send.
450 function testSendPartialSuccessWithError() {
452 sendData().then(test.callbackPass(function(sendInfo) {
453 test.assertEq(2, sendInfo.bytesSent);
454 test.assertEq('system_error', sendInfo.error);
456 })).then(test.callbackPass(function(sendInfo) {
457 test.assertEq(4, sendInfo.bytesSent);
458 test.assertEq(undefined, sendInfo.error);
464 // Test that a timed-out send returns a timeout error and that changing the
465 // send timeout during a send does not affect its timeout. This test uses an
466 // IoHandle that never completes sends.
467 function testSendTimeout() {
469 sendData().then(test.callbackPass(function(sendInfo) {
470 test.assertEq(0, sendInfo.bytesSent);
471 test.assertEq('timeout', sendInfo.error);
472 test.assertEq(5, timeoutManager.currentTime);
475 serial.update(connectionId, {sendTimeout: 10}, test.callbackPass(
476 timeoutManager.run.bind(timeoutManager, 1)));
477 }, {sendTimeout: 5});
480 // Test that a timed-out send returns a timeout error and that disabling the
481 // send timeout during a send does not affect its timeout. This test uses an
482 // IoHandle that never completes sends.
483 function testDisableSendTimeout() {
485 sendData().then(test.callbackPass(function(sendInfo) {
486 test.assertEq(0, sendInfo.bytesSent);
487 test.assertEq('timeout', sendInfo.error);
488 test.assertEq(6, timeoutManager.currentTime);
491 serial.update(connectionId, {sendTimeout: 0}, test.callbackPass(
492 timeoutManager.run.bind(timeoutManager, 1)));
493 }, {sendTimeout: 6});
496 // Test that data received while the connection is paused is queued and
497 // dispatched once the connection is unpaused.
498 function testPausedReceive() {
499 // Wait until the receive hook is installed, then start the test.
500 addReceiveHook(function() {
501 // Unpause the connection after the connection has queued the received
502 // data to ensure the queued data is dispatched when the connection is
504 serial.setPaused(connectionId, false, test.callbackPass());
505 // Check that setPaused(false) is idempotent.
506 serial.setPaused(connectionId, false, test.callbackPass());
509 // Check that setPaused(true) is idempotent.
510 serial.setPaused(connectionId, true, test.callbackPass());
511 serial.setPaused(connectionId, true, test.callbackPass());
514 test.listenOnce(serial.onReceive, function(result) {
515 checkReceivedData(result);
520 // Test that a receive error received while the connection is paused is queued
521 // and dispatched once the connection is unpaused.
522 function testPausedReceiveError() {
523 addReceiveErrorHook(function() {
524 // Unpause the connection after the connection has queued the receive
525 // error to ensure the queued error is dispatched when the connection is
527 serial.setPaused(connectionId, false, test.callbackPass());
528 }).then(test.callbackPass(function() {
530 serial.setPaused(connectionId, true, test.callbackPass());
534 test.listenOnce(serial.onReceiveError, function(result) {
535 serial.getInfo(connectionId, test.callbackPass(function(connectionInfo) {
537 test.assertTrue(connectionInfo.paused);
539 test.assertEq(connectionId, result.connectionId);
540 test.assertEq('device_lost', result.error);
542 serial.onReceive.addListener(function() {
543 test.fail('unexpected onReceive event');
547 // Test that receive timeouts trigger after the timeout time elapses and that
548 // changing the receive timeout does not affect a wait in progress.
549 function testReceiveTimeout() {
551 test.listenOnce(serial.onReceiveError, function(result) {
552 test.assertEq(connectionId, result.connectionId);
553 test.assertEq('timeout', result.error);
554 test.assertEq(20, timeoutManager.currentTime);
555 serial.getInfo(connectionId, test.callbackPass(
556 function(connectionInfo) {
557 test.assertFalse(connectionInfo.paused);
561 // Changing the timeout does not take effect until the current timeout
562 // expires or a receive completes.
563 serial.update(connectionId, {receiveTimeout: 10}, test.callbackPass(
564 timeoutManager.run.bind(timeoutManager, 1)));
565 }, {receiveTimeout: 20});
568 // Test that receive timeouts trigger after the timeout time elapses and that
569 // disabling the receive timeout does not affect a wait in progress.
570 function testDisableReceiveTimeout() {
572 test.listenOnce(serial.onReceiveError, function(result) {
573 test.assertEq(connectionId, result.connectionId);
574 test.assertEq('timeout', result.error);
575 test.assertEq(30, timeoutManager.currentTime);
576 serial.getInfo(connectionId, test.callbackPass(
577 function(connectionInfo) {
579 test.assertFalse(connectionInfo.paused);
582 // Disabling the timeout does not take effect until the current timeout
583 // expires or a receive completes.
584 serial.update(connectionId, {receiveTimeout: 0}, test.callbackPass(
585 timeoutManager.run.bind(timeoutManager, 1)));
586 }, {receiveTimeout: 30});
589 // Test that a receive error from the service is correctly dispatched. This
590 // test uses an IoHandler that only reports 'disconnected' receive errors.
591 function testReceiveErrorDisconnected() {
592 runReceiveErrorTest('disconnected');
595 // Test that a receive error from the service is correctly dispatched. This
596 // test uses an IoHandler that only reports 'device_lost' receive errors.
597 function testReceiveErrorDeviceLost() {
598 runReceiveErrorTest('device_lost');
601 // Test that a receive from error the service is correctly dispatched. This
602 // test uses an IoHandler that only reports 'system_error' receive errors.
603 function testReceiveErrorSystemError() {
604 runReceiveErrorTest('system_error');
607 // Test that a send error from the service is correctly returned as the send
608 // result. This test uses an IoHandler that only reports 'disconnected' send
610 function testSendErrorDisconnected() {
611 runSendErrorTest('disconnected');
614 // Test that a send error from the service is correctly returned as the send
615 // result. This test uses an IoHandler that only reports 'system_error' send
617 function testSendErrorSystemError() {
618 runSendErrorTest('system_error');
621 // Test that disconnect returns the correct error for a connection ID that
623 function testDisconnectUnknownConnectionId() {
624 serial.disconnect(-1, test.callbackFail('Serial connection not found.'));
627 // Test that getInfo returns the correct error for a connection ID that does
629 function testGetInfoUnknownConnectionId() {
630 serial.getInfo(-1, test.callbackFail('Serial connection not found.'));
633 // Test that update returns the correct error for a connection ID that does
635 function testUpdateUnknownConnectionId() {
636 serial.update(-1, {}, test.callbackFail('Serial connection not found.'));
639 // Test that setControlSignals returns the correct error for a connection ID
640 // that does not exist.
641 function testSetControlSignalsUnknownConnectionId() {
642 serial.setControlSignals(-1, {}, test.callbackFail(
643 'Serial connection not found.'));
646 // Test that getControlSignals returns the correct error for a connection ID
647 // that does not exist.
648 function testGetControlSignalsUnknownConnectionId() {
649 serial.getControlSignals(-1, test.callbackFail(
650 'Serial connection not found.'));
653 // Test that flush returns the correct error for a connection ID that does not
655 function testFlushUnknownConnectionId() {
656 serial.flush(-1, test.callbackFail('Serial connection not found.'));
659 // Test that setPaused returns the correct error for a connection ID that does
661 function testSetPausedUnknownConnectionId() {
663 -1, true, test.callbackFail('Serial connection not found.'));
665 -1, false, test.callbackFail('Serial connection not found.'));
668 // Test that send returns the correct error for a connection ID that does not
670 function testSendUnknownConnectionId() {
671 var buffer = new ArrayBuffer(1);
672 serial.send(-1, buffer, test.callbackFail('Serial connection not found.'));
674 ], test.runTests, exports);