Skip to content

Commit 2bca1e1

Browse files
committed
serial-service tests
1 parent 7d10e89 commit 2bca1e1

File tree

5 files changed

+261
-55
lines changed

5 files changed

+261
-55
lines changed

arduino-ide-extension/package.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,11 @@
1919
"test:watch": "mocha --watch --watch-files lib \"./lib/test/**/*.test.js\""
2020
},
2121
"dependencies": {
22-
"arduino-serial-plotter-webapp": "0.0.15",
2322
"@grpc/grpc-js": "^1.3.7",
2423
"@theia/application-package": "1.19.0",
2524
"@theia/core": "1.19.0",
2625
"@theia/editor": "1.19.0",
27-
"@theia/editor-preview": "1.19.0",
26+
"@theia/editor-preview": "1.19.0",
2827
"@theia/filesystem": "1.19.0",
2928
"@theia/git": "1.19.0",
3029
"@theia/keymaps": "1.19.0",
@@ -53,10 +52,10 @@
5352
"@types/ps-tree": "^1.1.0",
5453
"@types/react-select": "^3.0.0",
5554
"@types/react-tabs": "^2.3.2",
56-
"@types/sinon": "^7.5.2",
5755
"@types/temp": "^0.8.34",
5856
"@types/which": "^1.3.1",
5957
"ajv": "^6.5.3",
58+
"arduino-serial-plotter-webapp": "0.0.15",
6059
"async-mutex": "^0.3.0",
6160
"atob": "^2.1.2",
6261
"auth0-js": "^9.14.0",
@@ -97,6 +96,8 @@
9796
"@types/chai-string": "^1.4.2",
9897
"@types/mocha": "^5.2.7",
9998
"@types/react-window": "^1.8.5",
99+
"@types/sinon": "^10.0.6",
100+
"@types/sinon-chai": "^3.2.6",
100101
"chai": "^4.2.0",
101102
"chai-string": "^1.5.0",
102103
"decompress": "^4.2.0",
@@ -109,7 +110,8 @@
109110
"moment": "^2.24.0",
110111
"protoc": "^1.0.4",
111112
"shelljs": "^0.8.3",
112-
"sinon": "^9.0.1",
113+
"sinon": "^12.0.1",
114+
"sinon-chai": "^3.7.0",
113115
"typemoq": "^2.1.0",
114116
"uuid": "^3.2.1",
115117
"yargs": "^11.1.0"

arduino-ide-extension/src/node/serial/serial-service-impl.ts

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,6 @@ namespace ErrorWithCode {
6363

6464
@injectable()
6565
export class SerialServiceImpl implements SerialService {
66-
@named(SerialServiceName)
67-
@inject(ILogger)
68-
protected readonly logger: ILogger;
69-
70-
@inject(MonitorClientProvider)
71-
protected readonly serialClientProvider: MonitorClientProvider;
72-
73-
@inject(WebSocketService)
74-
protected readonly webSocketService: WebSocketService;
75-
7666
protected theiaFEClient?: SerialServiceClient;
7767
protected serialConfig?: SerialConfig;
7868

@@ -88,6 +78,18 @@ export class SerialServiceImpl implements SerialService {
8878

8979
uploadInProgress = false;
9080

81+
constructor(
82+
@inject(ILogger)
83+
@named(SerialServiceName)
84+
protected readonly logger: ILogger,
85+
86+
@inject(MonitorClientProvider)
87+
protected readonly serialClientProvider: MonitorClientProvider,
88+
89+
@inject(WebSocketService)
90+
protected readonly webSocketService: WebSocketService
91+
) {}
92+
9193
async isSerialPortOpen(): Promise<boolean> {
9294
return !!this.serialConnection;
9395
}
@@ -115,7 +117,6 @@ export class SerialServiceImpl implements SerialService {
115117
public async connectSerialIfRequired(): Promise<void> {
116118
if (this.uploadInProgress) return;
117119
const clients = await this.clientsAttached();
118-
this.logger.info(`WS clients: ${clients}`);
119120
clients > 0 ? await this.connect() : await this.disconnect();
120121
}
121122

@@ -144,7 +145,7 @@ export class SerialServiceImpl implements SerialService {
144145
this.webSocketService.sendMessage(JSON.stringify(msg));
145146
}
146147

147-
async connect(): Promise<Status> {
148+
private async connect(): Promise<Status> {
148149
if (!this.serialConfig) {
149150
return Status.CONFIG_MISSING;
150151
}
@@ -155,8 +156,6 @@ export class SerialServiceImpl implements SerialService {
155156
)} on port ${Port.toString(this.serialConfig.port)}...`
156157
);
157158

158-
// check if the board/port is available
159-
160159
if (this.serialConnection) {
161160
return Status.ALREADY_CONNECTED;
162161
}
@@ -275,7 +274,9 @@ export class SerialServiceImpl implements SerialService {
275274
`<<< Serial connection created for ${boardName} on port ${portName}.`
276275
);
277276
resolve(Status.OK);
277+
return;
278278
});
279+
resolve(Status.NOT_CONNECTED);
279280
return;
280281
}
281282
this.disconnect().then(() => resolve(Status.NOT_CONNECTED));
@@ -299,12 +300,14 @@ export class SerialServiceImpl implements SerialService {
299300
reason &&
300301
reason.code === SerialError.ErrorCodes.CLIENT_CANCEL
301302
) {
302-
return Status.OK;
303+
resolve(Status.OK);
304+
return;
303305
}
304306
this.logger.info('>>> Disposing serial connection...');
305307
if (!this.serialConnection) {
306308
this.logger.warn('<<< Not connected. Nothing to dispose.');
307-
return Status.NOT_CONNECTED;
309+
resolve(Status.NOT_CONNECTED);
310+
return;
308311
}
309312
const { duplex, config } = this.serialConnection;
310313

arduino-ide-extension/src/test/browser/serial-connection-manager.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@
100100
// return { dispose: () => {} };
101101
// });
102102

103+
// serialServiceClient
104+
// .setup((mock) => mock.onWebSocketChanged(It.isAny()))
105+
// .returns((h) => {
106+
// handleWebSocketChanged = h;
107+
// return { dispose: () => {} };
108+
// });
109+
103110
// serialService
104111
// .setup((m) => m.disconnect())
105112
// .returns(() => Promise.resolve(Status.OK));
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import { MonitorServiceClient } from './../../node/cli-protocol/cc/arduino/cli/monitor/v1/monitor_grpc_pb.d';
2+
import { SerialServiceImpl } from './../../node/serial/serial-service-impl';
3+
import { IMock, It, Mock } from 'typemoq';
4+
import { createSandbox } from 'sinon';
5+
import * as sinonChai from 'sinon-chai';
6+
import { expect, use } from 'chai';
7+
use(sinonChai);
8+
9+
import { ILogger } from '@theia/core/lib/common/logger';
10+
import { MonitorClientProvider } from '../../node/serial/monitor-client-provider';
11+
import { WebSocketService } from '../../node/web-socket/web-socket-service';
12+
import { Status } from '../../common/protocol';
13+
14+
describe.only('SerialServiceImpl', () => {
15+
let subject: SerialServiceImpl;
16+
17+
let logger: IMock<ILogger>;
18+
let serialClientProvider: IMock<MonitorClientProvider>;
19+
let webSocketService: IMock<WebSocketService>;
20+
21+
beforeEach(() => {
22+
logger = Mock.ofType<ILogger>();
23+
logger.setup((b) => b.info(It.isAnyString()));
24+
logger.setup((b) => b.warn(It.isAnyString()));
25+
logger.setup((b) => b.error(It.isAnyString()));
26+
27+
serialClientProvider = Mock.ofType<MonitorClientProvider>();
28+
webSocketService = Mock.ofType<WebSocketService>();
29+
30+
subject = new SerialServiceImpl(
31+
logger.object,
32+
serialClientProvider.object,
33+
webSocketService.object
34+
);
35+
});
36+
37+
context('when a serial connection is requested', () => {
38+
const sandbox = createSandbox();
39+
beforeEach(() => {
40+
subject.uploadInProgress = false;
41+
sandbox.spy(subject, 'disconnect');
42+
sandbox.spy(subject, 'updateWsConfigParam');
43+
});
44+
45+
afterEach(function () {
46+
sandbox.restore();
47+
});
48+
49+
context('and an upload is in progress', () => {
50+
beforeEach(async () => {
51+
subject.uploadInProgress = true;
52+
});
53+
54+
it('should not change the connection status', async () => {
55+
await subject.connectSerialIfRequired();
56+
expect(subject.disconnect).to.have.callCount(0);
57+
});
58+
});
59+
60+
context('and there is no upload in progress', () => {
61+
beforeEach(async () => {
62+
subject.uploadInProgress = false;
63+
});
64+
65+
context('and there are 0 attached ws clients', () => {
66+
it('should disconnect', async () => {
67+
await subject.connectSerialIfRequired();
68+
expect(subject.disconnect).to.have.been.calledOnce;
69+
});
70+
});
71+
72+
context('and there are > 0 attached ws clients', () => {
73+
beforeEach(() => {
74+
webSocketService
75+
.setup((b) => b.getConnectedClientsNumber())
76+
.returns(() => 1);
77+
});
78+
79+
it('should not call the disconenct', async () => {
80+
await subject.connectSerialIfRequired();
81+
expect(subject.disconnect).to.have.callCount(0);
82+
});
83+
});
84+
});
85+
});
86+
87+
context('when a disconnection is requested', () => {
88+
const sandbox = createSandbox();
89+
beforeEach(() => {});
90+
91+
afterEach(function () {
92+
sandbox.restore();
93+
});
94+
95+
context('and a serialConnection is not set', () => {
96+
it('should return a NOT_CONNECTED status', async () => {
97+
const status = await subject.disconnect();
98+
expect(status).to.be.equal(Status.NOT_CONNECTED);
99+
});
100+
});
101+
102+
context('and a serialConnection is set', async () => {
103+
beforeEach(async () => {
104+
sandbox.spy(subject, 'updateWsConfigParam');
105+
await subject.disconnect();
106+
});
107+
108+
it('should dispose the serialConnection', async () => {
109+
const serialConnectionOpen = await subject.isSerialPortOpen();
110+
expect(serialConnectionOpen).to.be.false;
111+
});
112+
113+
it('should call updateWsConfigParam with disconnected status', async () => {
114+
expect(subject.updateWsConfigParam).to.be.calledWith({
115+
connected: false,
116+
});
117+
});
118+
});
119+
});
120+
121+
context('when a new config is passed in', () => {
122+
const sandbox = createSandbox();
123+
beforeEach(async () => {
124+
subject.uploadInProgress = false;
125+
webSocketService
126+
.setup((b) => b.getConnectedClientsNumber())
127+
.returns(() => 1);
128+
129+
serialClientProvider
130+
.setup((b) => b.client())
131+
.returns(async () => {
132+
return {
133+
streamingOpen: () => {
134+
return {
135+
on: (str: string, cb: any) => {},
136+
write: (chunk: any, cb: any) => {},
137+
cancel: () => {},
138+
};
139+
},
140+
} as MonitorServiceClient;
141+
});
142+
143+
sandbox.spy(subject, 'disconnect');
144+
sandbox.spy(subject, 'updateWsConfigParam');
145+
146+
await subject.setSerialConfig({
147+
board: { name: 'test' },
148+
port: { address: 'test', protocol: 'test' },
149+
});
150+
});
151+
152+
afterEach(function () {
153+
sandbox.restore();
154+
});
155+
156+
it('should disconnect from previous connection', async () => {
157+
expect(subject.disconnect).to.be.called;
158+
});
159+
160+
it('should create the serialConnection', async () => {
161+
const serialConnectionOpen = await subject.isSerialPortOpen();
162+
expect(serialConnectionOpen).to.be.true;
163+
});
164+
});
165+
});

0 commit comments

Comments
 (0)