Skip to content

Commit 1da8f6d

Browse files
author
Akos Kitta
committed
test: added compiler + build output path test
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
1 parent 9228dfb commit 1da8f6d

File tree

6 files changed

+329
-18
lines changed

6 files changed

+329
-18
lines changed

Diff for: arduino-ide-extension/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"build": "tsc && ncp ./src/node/cli-protocol/ ./lib/node/cli-protocol/ && yarn lint",
1818
"watch": "tsc -w",
1919
"test": "mocha \"./lib/test/**/*.test.js\"",
20+
"test:slow": "mocha \"./lib/test/**/*.slow-test.js\"",
2021
"test:watch": "mocha --watch --watch-files lib \"./lib/test/**/*.test.js\""
2122
},
2223
"dependencies": {

Diff for: arduino-ide-extension/src/node/core-client-provider.ts

-9
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ export class CoreClientProvider {
6363
new Emitter<CoreClientProvider.Client>();
6464
private readonly onClientReady = this.onClientReadyEmitter.event;
6565

66-
private ready = new Deferred<void>();
6766
private pending: Deferred<CoreClientProvider.Client> | undefined;
6867
private _client: CoreClientProvider.Client | undefined;
6968

@@ -135,14 +134,6 @@ export class CoreClientProvider {
135134
const client = await this.createClient(address);
136135
this.toDisposeOnCloseClient.pushAll([
137136
Disposable.create(() => client.client.close()),
138-
Disposable.create(() => {
139-
this.ready.reject(
140-
new Error(
141-
`Disposed. Creating a new gRPC core client on address ${address}.`
142-
)
143-
);
144-
this.ready = new Deferred();
145-
}),
146137
]);
147138
await this.initInstanceWithFallback(client);
148139
return this.useClient(client);

Diff for: arduino-ide-extension/src/node/sketches-service-impl.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
SketchContainer,
2020
SketchesError,
2121
} from '../common/protocol/sketches-service';
22-
import { NotificationServiceServerImpl } from './notification-service-server';
22+
import { NotificationServiceServer } from '../common/protocol';
2323
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
2424
import { CoreClientAware } from './core-client-provider';
2525
import {
@@ -77,8 +77,8 @@ export class SketchesServiceImpl
7777
@inject(ConfigServiceImpl)
7878
private readonly configService: ConfigServiceImpl;
7979

80-
@inject(NotificationServiceServerImpl)
81-
private readonly notificationService: NotificationServiceServerImpl;
80+
@inject(NotificationServiceServer)
81+
private readonly notificationService: NotificationServiceServer;
8282

8383
@inject(EnvVariablesServer)
8484
private readonly envVariableServer: EnvVariablesServer;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
import { CancellationTokenSource } from '@theia/core/lib/common/cancellation';
2+
import {
3+
CommandContribution,
4+
CommandRegistry,
5+
CommandService,
6+
} from '@theia/core/lib/common/command';
7+
import { bindContributionProvider } from '@theia/core/lib/common/contribution-provider';
8+
import { Disposable } from '@theia/core/lib/common/disposable';
9+
import { EnvVariablesServer as TheiaEnvVariablesServer } from '@theia/core/lib/common/env-variables';
10+
import { ILogger } from '@theia/core/lib/common/logger';
11+
import { isWindows } from '@theia/core/lib/common/os';
12+
import { waitForEvent } from '@theia/core/lib/common/promise-util';
13+
import { MockLogger } from '@theia/core/lib/common/test/mock-logger';
14+
import { BackendApplicationConfigProvider } from '@theia/core/lib/node/backend-application-config-provider';
15+
import { FileUri } from '@theia/core/lib/node/file-uri';
16+
import {
17+
Container,
18+
ContainerModule,
19+
injectable,
20+
} from '@theia/core/shared/inversify';
21+
import { expect } from 'chai';
22+
import {
23+
ArduinoDaemon,
24+
AttachedBoardsChangeEvent,
25+
AvailablePorts,
26+
BoardsPackage,
27+
BoardsService,
28+
ConfigService,
29+
ConfigState,
30+
CoreService,
31+
IndexUpdateDidCompleteParams,
32+
IndexUpdateDidFailParams,
33+
IndexUpdateParams,
34+
LibraryPackage,
35+
NotificationServiceClient,
36+
NotificationServiceServer,
37+
OutputMessage,
38+
ProgressMessage,
39+
ResponseService,
40+
Sketch,
41+
SketchesService,
42+
} from '../../common/protocol';
43+
import { ArduinoDaemonImpl } from '../../node/arduino-daemon-impl';
44+
import { BoardDiscovery } from '../../node/board-discovery';
45+
import { BoardsServiceImpl } from '../../node/boards-service-impl';
46+
import { ConfigServiceImpl } from '../../node/config-service-impl';
47+
import { CoreClientProvider } from '../../node/core-client-provider';
48+
import { CoreServiceImpl } from '../../node/core-service-impl';
49+
import { IsTempSketch } from '../../node/is-temp-sketch';
50+
import { MonitorManager } from '../../node/monitor-manager';
51+
import { MonitorService } from '../../node/monitor-service';
52+
import {
53+
MonitorServiceFactory,
54+
MonitorServiceFactoryOptions,
55+
} from '../../node/monitor-service-factory';
56+
import { SketchesServiceImpl } from '../../node/sketches-service-impl';
57+
import { EnvVariablesServer } from '../../node/theia/env-variables/env-variables-server';
58+
59+
const testTimeout = 30_000;
60+
const setupTimeout = 5 * 60 * 1_000; // five minutes
61+
const avr = 'arduino:avr';
62+
const uno = 'arduino:avr:uno';
63+
64+
describe('core-service-impl', () => {
65+
let container: Container;
66+
let toDispose: Disposable[];
67+
68+
before(() => {
69+
BackendApplicationConfigProvider.set({ configDirName: 'testArduinoIDE' });
70+
});
71+
72+
beforeEach(async function () {
73+
this.timeout(setupTimeout);
74+
toDispose = [];
75+
container = createContainer();
76+
await start(container, toDispose);
77+
});
78+
79+
afterEach(() => {
80+
let disposable = toDispose.pop();
81+
while (disposable) {
82+
try {
83+
disposable?.dispose();
84+
} catch {}
85+
disposable = toDispose.pop();
86+
}
87+
});
88+
89+
describe('compile', () => {
90+
it('should execute a command with the build path', async function () {
91+
this.timeout(testTimeout);
92+
const coreService = container.get<CoreService>(CoreService);
93+
const sketchesService = container.get<SketchesService>(SketchesService);
94+
const commandService =
95+
container.get<TestCommandRegistry>(TestCommandRegistry);
96+
const sketch = await sketchesService.createNewSketch();
97+
98+
await coreService.compile({
99+
fqbn: uno,
100+
sketch,
101+
optimizeForDebug: false,
102+
sourceOverride: {},
103+
verbose: true,
104+
});
105+
106+
const executedBuildDidCompleteCommands =
107+
commandService.executedCommands.filter(
108+
([command]) =>
109+
command === 'arduino.languageserver.notifyBuildDidComplete'
110+
);
111+
expect(executedBuildDidCompleteCommands.length).to.be.equal(1);
112+
const [, args] = executedBuildDidCompleteCommands[0];
113+
expect(args.length).to.be.equal(1);
114+
const arg = args[0];
115+
expect(typeof arg).to.be.equal('object');
116+
expect('buildOutputUri' in arg).to.be.true;
117+
expect(arg.buildOutputUri).to.be.not.undefined;
118+
119+
const tempBuildPaths = await sketchesService.tempBuildPath(sketch);
120+
if (isWindows) {
121+
expect(tempBuildPaths.length).to.be.greaterThan(1);
122+
} else {
123+
expect(tempBuildPaths.length).to.be.equal(1);
124+
}
125+
126+
const { buildOutputUri } = arg;
127+
const buildOutputPath = FileUri.fsPath(buildOutputUri).toString();
128+
expect(tempBuildPaths.includes(buildOutputPath)).to.be.true;
129+
});
130+
});
131+
});
132+
133+
async function start(
134+
container: Container,
135+
toDispose: Disposable[]
136+
): Promise<void> {
137+
const daemon = container.get<ArduinoDaemonImpl>(ArduinoDaemonImpl);
138+
const configService = container.get<ConfigServiceImpl>(ConfigServiceImpl);
139+
toDispose.push(Disposable.create(() => daemon.stop()));
140+
configService.onStart();
141+
daemon.onStart();
142+
await waitForEvent(daemon.onDaemonStarted, 10_000);
143+
const boardService = container.get<BoardsServiceImpl>(BoardsServiceImpl);
144+
const searchResults = await boardService.search({ query: avr });
145+
const platform = searchResults.find(({ id }) => id === avr);
146+
if (!platform) {
147+
throw new Error(`Could not find platform: ${avr}`);
148+
}
149+
await boardService.install({ item: platform });
150+
}
151+
152+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
153+
function createContainer(): Container {
154+
const container = new Container({ defaultScope: 'Singleton' });
155+
const module = new ContainerModule((bind) => {
156+
bind(CoreClientProvider).toSelf().inSingletonScope();
157+
bind(CoreServiceImpl).toSelf().inSingletonScope();
158+
bind(CoreService).toService(CoreServiceImpl);
159+
bind(BoardsServiceImpl).toSelf().inSingletonScope();
160+
bind(BoardsService).toService(BoardsServiceImpl);
161+
bind(TestResponseService).toSelf().inSingletonScope();
162+
bind(ResponseService).toService(TestResponseService);
163+
bind(MonitorManager).toSelf().inSingletonScope();
164+
bind(MonitorServiceFactory).toFactory(
165+
({ container }) =>
166+
(options: MonitorServiceFactoryOptions) => {
167+
const child = container.createChild();
168+
child
169+
.bind<MonitorServiceFactoryOptions>(MonitorServiceFactoryOptions)
170+
.toConstantValue({
171+
...options,
172+
});
173+
child.bind(MonitorService).toSelf();
174+
return child.get<MonitorService>(MonitorService);
175+
}
176+
);
177+
bind(EnvVariablesServer).toSelf().inSingletonScope();
178+
bind(TheiaEnvVariablesServer).toService(EnvVariablesServer);
179+
bind(ArduinoDaemonImpl).toSelf().inSingletonScope();
180+
bind(ArduinoDaemon).toService(ArduinoDaemonImpl);
181+
bind(MockLogger).toSelf().inSingletonScope();
182+
bind(ILogger).toService(MockLogger);
183+
bind(TestNotificationServiceServer).toSelf().inSingletonScope();
184+
bind(NotificationServiceServer).toService(TestNotificationServiceServer);
185+
bind(ConfigServiceImpl).toSelf().inSingletonScope();
186+
bind(ConfigService).toService(ConfigServiceImpl);
187+
bind(TestCommandRegistry).toSelf().inSingletonScope();
188+
bind(CommandRegistry).toService(TestCommandRegistry);
189+
bind(CommandService).toService(CommandRegistry);
190+
bindContributionProvider(bind, CommandContribution);
191+
bind(TestBoardDiscovery).toSelf().inSingletonScope();
192+
bind(BoardDiscovery).toService(TestBoardDiscovery);
193+
bind(IsTempSketch).toSelf().inSingletonScope();
194+
bind(SketchesServiceImpl).toSelf().inSingletonScope();
195+
bind(SketchesService).toService(SketchesServiceImpl);
196+
});
197+
container.load(module);
198+
return container;
199+
}
200+
201+
@injectable()
202+
class TestResponseService implements ResponseService {
203+
readonly outputMessages: OutputMessage[] = [];
204+
readonly progressMessages: ProgressMessage[] = [];
205+
206+
appendToOutput(message: OutputMessage): void {
207+
this.outputMessages.push(message);
208+
}
209+
reportProgress(message: ProgressMessage): void {
210+
this.progressMessages.push(message);
211+
}
212+
}
213+
214+
@injectable()
215+
class TestNotificationServiceServer implements NotificationServiceServer {
216+
readonly events: string[] = [];
217+
218+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
219+
disposeClient(client: NotificationServiceClient): void {
220+
this.events.push('disposeClient:');
221+
}
222+
notifyDidReinitialize(): void {
223+
this.events.push('notifyDidReinitialize:');
224+
}
225+
notifyIndexUpdateWillStart(params: IndexUpdateParams): void {
226+
this.events.push(`notifyIndexUpdateWillStart:${JSON.stringify(params)}`);
227+
}
228+
notifyIndexUpdateDidProgress(progressMessage: ProgressMessage): void {
229+
this.events.push(
230+
`notifyIndexUpdateDidProgress:${JSON.stringify(progressMessage)}`
231+
);
232+
}
233+
notifyIndexUpdateDidComplete(params: IndexUpdateDidCompleteParams): void {
234+
this.events.push(`notifyIndexUpdateDidComplete:${JSON.stringify(params)}`);
235+
}
236+
notifyIndexUpdateDidFail(params: IndexUpdateDidFailParams): void {
237+
this.events.push(`notifyIndexUpdateDidFail:${JSON.stringify(params)}`);
238+
}
239+
notifyDaemonDidStart(port: string): void {
240+
this.events.push(`notifyDaemonDidStart:${port}`);
241+
}
242+
notifyDaemonDidStop(): void {
243+
this.events.push('notifyDaemonDidStop:');
244+
}
245+
notifyConfigDidChange(event: ConfigState): void {
246+
this.events.push(`notifyConfigDidChange:${JSON.stringify(event)}`);
247+
}
248+
notifyPlatformDidInstall(event: { item: BoardsPackage }): void {
249+
this.events.push(`notifyPlatformDidInstall:${JSON.stringify(event)}`);
250+
}
251+
notifyPlatformDidUninstall(event: { item: BoardsPackage }): void {
252+
this.events.push(`notifyPlatformDidUninstall:${JSON.stringify(event)}`);
253+
}
254+
notifyLibraryDidInstall(event: {
255+
item: LibraryPackage | 'zip-install';
256+
}): void {
257+
this.events.push(`notifyLibraryDidInstall:${JSON.stringify(event)}`);
258+
}
259+
notifyLibraryDidUninstall(event: { item: LibraryPackage }): void {
260+
this.events.push(`notifyLibraryDidUninstall:${JSON.stringify(event)}`);
261+
}
262+
notifyAttachedBoardsDidChange(event: AttachedBoardsChangeEvent): void {
263+
this.events.push(`notifyAttachedBoardsDidChange:${JSON.stringify(event)}`);
264+
}
265+
notifyRecentSketchesDidChange(event: { sketches: Sketch[] }): void {
266+
this.events.push(`notifyRecentSketchesDidChange:${JSON.stringify(event)}`);
267+
}
268+
dispose(): void {
269+
this.events.push('dispose');
270+
}
271+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
272+
setClient(client: NotificationServiceClient | undefined): void {
273+
this.events.push('setClient:');
274+
}
275+
}
276+
277+
@injectable()
278+
class TestBoardDiscovery extends BoardDiscovery {
279+
mutableAvailablePorts: AvailablePorts = {};
280+
281+
override async start(): Promise<void> {
282+
// NOOP
283+
}
284+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
285+
override async stop(restart?: boolean): Promise<void> {
286+
// NOOP
287+
}
288+
override get availablePorts(): AvailablePorts {
289+
return this.mutableAvailablePorts;
290+
}
291+
}
292+
293+
@injectable()
294+
class TestCommandRegistry extends CommandRegistry {
295+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
296+
readonly executedCommands: [string, any[]][] = [];
297+
298+
override async executeCommand<T>(
299+
commandId: string,
300+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
301+
...args: any[]
302+
): Promise<T | undefined> {
303+
const { token } = new CancellationTokenSource();
304+
this.onWillExecuteCommandEmitter.fire({
305+
commandId,
306+
args,
307+
token,
308+
waitUntil: () => {
309+
// NOOP
310+
},
311+
});
312+
this.executedCommands.push([commandId, args]);
313+
this.onDidExecuteCommandEmitter.fire({ commandId, args });
314+
return undefined;
315+
}
316+
}

Diff for: electron/packager/index.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,13 @@
107107
`yarn --network-timeout 1000000 --cwd ${join(repoRoot, extension)}`,
108108
`Building and testing ${extension}`
109109
);
110+
exec(
111+
`yarn --network-timeout 1000000 --cwd ${join(
112+
repoRoot,
113+
extension
114+
)} test:slow`,
115+
`Executing slow tests ${extension}`
116+
);
110117
}
111118

112119
//------------------------+
@@ -434,12 +441,7 @@ ${fs
434441
* @param {BufferEncoding|undefined} [encoding="base64"]
435442
* @param {object|undefined} [options]
436443
*/
437-
function hashFile(
438-
file,
439-
algorithm = 'sha512',
440-
encoding = 'base64',
441-
options
442-
) {
444+
function hashFile(file, algorithm = 'sha512', encoding = 'base64', options) {
443445
const crypto = require('crypto');
444446
return new Promise((resolve, reject) => {
445447
const hash = crypto.createHash(algorithm);

0 commit comments

Comments
 (0)