Skip to content

Commit

Permalink
test: gRPC core client init integration test
Browse files Browse the repository at this point in the history
 - Copied the env-variable server from Theia and made it possible to
customize it for the tests. Each test has its own `data` folder.
 - Relaxed the primary package and library index error detection.
This should make the init error detection locale independent.
 - Kill the daemon process subtree when stopping the daemon.

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
  • Loading branch information
Akos Kitta authored and kittaakos committed May 3, 2023
1 parent 097c92d commit 51f69f6
Show file tree
Hide file tree
Showing 13 changed files with 596 additions and 91 deletions.
2 changes: 1 addition & 1 deletion arduino-ide-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"cpy": "^8.1.2",
"cross-fetch": "^3.1.5",
"dateformat": "^3.0.3",
"deepmerge": "2.0.1",
"deepmerge": "^4.2.2",
"electron-updater": "^4.6.5",
"fast-json-stable-stringify": "^2.1.0",
"fast-safe-stringify": "^2.1.1",
Expand Down
2 changes: 1 addition & 1 deletion arduino-ide-extension/src/browser/create/create-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ export class CreateApi {
const result = await resultProvider(response);
const parseEnd = performance.now();
console.debug(
`HTTP ${fetchCount} ${method} ${url} [fetch: ${(
`HTTP ${fetchCount} ${method}${url} [fetch: ${(
fetchEnd - fetchStart
).toFixed(2)} ms, parse: ${(parseEnd - parseStart).toFixed(
2
Expand Down
16 changes: 14 additions & 2 deletions arduino-ide-extension/src/node/arduino-daemon-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ArduinoDaemon, NotificationServiceServer } from '../common/protocol';
import { CLI_CONFIG } from './cli-config';
import { getExecPath } from './exec-util';
import { SettingsReader } from './settings-reader';
import { ProcessUtils } from '@theia/core/lib/node/process-utils';

@injectable()
export class ArduinoDaemonImpl
Expand All @@ -34,6 +35,9 @@ export class ArduinoDaemonImpl
@inject(SettingsReader)
private readonly settingsReader: SettingsReader;

@inject(ProcessUtils)
private readonly processUtils: ProcessUtils;

private readonly toDispose = new DisposableCollection();
private readonly onDaemonStartedEmitter = new Emitter<string>();
private readonly onDaemonStoppedEmitter = new Emitter<void>();
Expand Down Expand Up @@ -84,8 +88,16 @@ export class ArduinoDaemonImpl
).unref();

this.toDispose.pushAll([
Disposable.create(() => daemon.kill()),
Disposable.create(() => this.fireDaemonStopped()),
Disposable.create(() => {
if (daemon.pid) {
this.processUtils.terminateProcessTree(daemon.pid);
this.fireDaemonStopped();
} else {
throw new Error(
'The CLI Daemon process does not have a PID. IDE2 could not stop the CLI daemon.'
);
}
}),
]);
this.fireDaemonStarted(port);
this.onData('Daemon is running.');
Expand Down
6 changes: 5 additions & 1 deletion arduino-ide-extension/src/node/arduino-ide-backend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ import {
} from '../common/protocol/arduino-daemon';
import { ConfigServiceImpl } from './config-service-impl';
import { EnvVariablesServer as TheiaEnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { EnvVariablesServer } from './theia/env-variables/env-variables-server';
import {
ConfigDirUriProvider,
EnvVariablesServer,
} from './theia/env-variables/env-variables-server';
import { NodeFileSystemExt } from './node-filesystem-ext';
import {
FileSystemExt,
Expand Down Expand Up @@ -236,6 +239,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(DefaultWorkspaceServer).toSelf().inSingletonScope();
rebind(TheiaWorkspaceServer).toService(DefaultWorkspaceServer);

bind(ConfigDirUriProvider).toSelf().inSingletonScope();
bind(EnvVariablesServer).toSelf().inSingletonScope();
rebind(TheiaEnvVariablesServer).toService(EnvVariablesServer);

Expand Down
3 changes: 0 additions & 3 deletions arduino-ide-extension/src/node/core-client-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,6 @@ function isPrimaryPackageIndexMissingStatus(
{ directories: { data } }: DefaultCliConfig
): boolean {
const predicate = ({ message }: RpcStatus.AsObject) =>
message.includes('loading json index file') &&
message.includes(join(data, 'package_index.json'));
// https://github.com/arduino/arduino-cli/blob/f0245bc2da6a56fccea7b2c9ea09e85fdcc52cb8/arduino/cores/packagemanager/package_manager.go#L247
return evaluate(status, predicate);
Expand All @@ -551,8 +550,6 @@ function isLibraryIndexMissingStatus(
{ directories: { data } }: DefaultCliConfig
): boolean {
const predicate = ({ message }: RpcStatus.AsObject) =>
message.includes('index file') &&
message.includes('reading') &&
message.includes(join(data, 'library_index.json'));
// https://github.com/arduino/arduino-cli/blob/f0245bc2da6a56fccea7b2c9ea09e85fdcc52cb8/arduino/cores/packagemanager/package_manager.go#L247
return evaluate(status, predicate);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,112 @@
import { join } from 'path';
import { homedir } from 'os';
import { injectable } from '@theia/core/shared/inversify';
import { FileUri } from '@theia/core/lib/node/file-uri';
import {
EnvVariable,
EnvVariablesServer as TheiaEnvVariablesServer,
} from '@theia/core/lib/common/env-variables/env-variables-protocol';
import { isWindows } from '@theia/core/lib/common/os';
import URI from '@theia/core/lib/common/uri';
import { BackendApplicationConfigProvider } from '@theia/core/lib/node/backend-application-config-provider';
import { EnvVariablesServerImpl as TheiaEnvVariablesServerImpl } from '@theia/core/lib/node/env-variables/env-variables-server';
import { FileUri } from '@theia/core/lib/node/file-uri';
import {
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import { list as listDrives } from 'drivelist';
import { homedir } from 'os';
import { join } from 'path';

@injectable()
export class ConfigDirUriProvider {
private uri: URI | undefined;

configDirUri(): URI {
if (!this.uri) {
this.uri = FileUri.create(
join(homedir(), BackendApplicationConfigProvider.get().configDirName)
);
}
return this.uri;
}
}

// Copy-pasted from https://github.com/eclipse-theia/theia/blob/v1.31.1/packages/core/src/node/env-variables/env-variables-server.ts
// to simplify the binding of the config directory location for tests.
@injectable()
export class EnvVariablesServer extends TheiaEnvVariablesServerImpl {
protected override readonly configDirUri = Promise.resolve(
FileUri.create(
join(homedir(), BackendApplicationConfigProvider.get().configDirName)
).toString()
);
export class EnvVariablesServer implements TheiaEnvVariablesServer {
@inject(ConfigDirUriProvider)
private readonly configDirUriProvider: ConfigDirUriProvider;

private readonly envs: { [key: string]: EnvVariable } = {};
private readonly homeDirUri = FileUri.create(homedir()).toString();

constructor() {
const prEnv = process.env;
Object.keys(prEnv).forEach((key: string) => {
let keyName = key;
if (isWindows) {
keyName = key.toLowerCase();
}
this.envs[keyName] = { name: keyName, value: prEnv[key] };
});
}

@postConstruct()
protected init(): void {
console.log(
`Configuration directory URI: '${this.configDirUriProvider
.configDirUri()
.toString()}'`
);
}

async getExecPath(): Promise<string> {
return process.execPath;
}

async getVariables(): Promise<EnvVariable[]> {
return Object.keys(this.envs).map((key) => this.envs[key]);
}

async getValue(key: string): Promise<EnvVariable | undefined> {
if (isWindows) {
key = key.toLowerCase();
}
return this.envs[key];
}

async getConfigDirUri(): Promise<string> {
return this.configDirUriProvider.configDirUri().toString();
}

async getHomeDirUri(): Promise<string> {
return this.homeDirUri;
}

async getDrives(): Promise<string[]> {
const uris: string[] = [];
const drives = await listDrives();
for (const drive of drives) {
for (const mountpoint of drive.mountpoints) {
if (this.filterHiddenPartitions(mountpoint.path)) {
uris.push(FileUri.create(mountpoint.path).toString());
}
}
}
return uris;
}

/**
* Filters hidden and system partitions.
*/
private filterHiddenPartitions(path: string): boolean {
// OS X: This is your sleep-image. When your Mac goes to sleep it writes the contents of its memory to the hard disk. (https://bit.ly/2R6cztl)
if (path === '/private/var/vm') {
return false;
}
// Ubuntu: This system partition is simply the boot partition created when the computers mother board runs UEFI rather than BIOS. (https://bit.ly/2N5duHr)
if (path === '/boot/efi') {
return false;
}
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,16 @@ import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { Container } from '@theia/core/shared/inversify';
import { expect } from 'chai';
import { BoardSearch, BoardsService } from '../../common/protocol';
import {
configureBackendApplicationConfigProvider,
createBaseContainer,
startDaemon,
} from './test-bindings';
import { createBaseContainer, startDaemon } from './test-bindings';

describe('boards-service-impl', () => {
let boardService: BoardsService;
let toDispose: DisposableCollection;

before(async function () {
configureBackendApplicationConfigProvider();
this.timeout(20_000);
toDispose = new DisposableCollection();
const container = createContainer();
const container = await createContainer();
await start(container, toDispose);
boardService = container.get<BoardsService>(BoardsService);
});
Expand Down Expand Up @@ -94,7 +89,7 @@ describe('boards-service-impl', () => {
});
});

function createContainer(): Container {
async function createContainer(): Promise<Container> {
return createBaseContainer();
}

Expand Down
Loading

0 comments on commit 51f69f6

Please sign in to comment.