Skip to content

Commit

Permalink
Add tests (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrmarti committed Mar 10, 2023
1 parent 488e6b1 commit edff38a
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 8 deletions.
20 changes: 15 additions & 5 deletions src/spec-common/commonUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,24 +122,29 @@ export async function runCommandNoPty(options: {
}
});
const subs: Disposable[] = [];
p.exit.then(({ code }) => {
p.exit.then(({ code, signal }) => {
try {
const failed = !!code || !!signal;
subs.forEach(sub => sub.dispose());
const stdoutBuf = Buffer.concat(stdout);
const stderrBuf = Buffer.concat(stderr);
if (print === true || (code && print === 'onerror')) {
if (print === true || (failed && print === 'onerror')) {
output.write(stdoutBuf.toString().replace(/\r?\n/g, '\r\n'));
output.write(toErrorText(stderrBuf.toString()));
}
if (print && code) {
output.write(`Exit code ${code}`);
}
if (code) {
if (print && signal) {
output.write(`Process signal ${signal}`);
}
if (failed) {
reject({
message: `Command failed: ${cmd} ${(args || []).join(' ')}`,
stdout: stdoutBuf,
stderr: stderrBuf,
code
code,
signal,
});
} else {
resolve({
Expand Down Expand Up @@ -175,8 +180,9 @@ export async function runCommand(options: {
output: Log;
resolveOn?: RegExp;
onDidInput?: Event<string>;
stdin?: string;
}) {
const { ptyExec, cmd, args, cwd, env, output, resolveOn, onDidInput } = options;
const { ptyExec, cmd, args, cwd, env, output, resolveOn, onDidInput, stdin } = options;

const p = await ptyExec({
cmd,
Expand All @@ -189,6 +195,10 @@ export async function runCommand(options: {
return new Promise<{ cmdOutput: string }>((resolve, reject) => {
let cmdOutput = '';

if (stdin) {
p.write(stdin);
}

const subs = [
onDidInput && onDidInput(data => p.write(data)),
];
Expand Down
42 changes: 39 additions & 3 deletions src/test/cli.exec.base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import * as assert from 'assert';
import * as path from 'path';
import { BuildKitOption, commandMarkerTests, devContainerDown, devContainerStop, devContainerUp, pathExists, shellExec } from './testUtils';
import { BuildKitOption, commandMarkerTests, devContainerDown, devContainerStop, devContainerUp, pathExists, shellBufferExec, shellExec, shellPtyExec } from './testUtils';

const pkg = require('../../package.json');

Expand All @@ -31,8 +31,44 @@ export function describeTests1({ text, options }: BuildKitOption) {
beforeEach(async () => containerId = (await devContainerUp(cli, testFolder, options)).containerId);
afterEach(async () => await devContainerDown({ containerId }));
it('should execute successfully', async () => {
const res = await shellExec(`${cli} exec --workspace-folder ${testFolder} echo hi`);
assert.strictEqual(res.error, null);
const res = await shellBufferExec(`${cli} exec --workspace-folder ${testFolder} echo hi`);
assert.strictEqual(res.code, 0);
assert.equal(res.signal, undefined);
assert.strictEqual(res.stdout.toString(), 'hi\n');
});
it('should not run in a terminal', async () => {
const res = await shellBufferExec(`${cli} exec --workspace-folder ${testFolder} [ ! -t 1 ]`);
assert.strictEqual(res.code, 0);
assert.equal(res.signal, undefined);
});
it('should return exit code without terminal', async () => {
const res = await shellBufferExec(`${cli} exec --workspace-folder ${testFolder} sh -c 'exit 123'`);
assert.strictEqual(res.code, 123);
assert.equal(res.signal, undefined);
});
it('stream binary data', async () => {
const stdin = Buffer.alloc(256);
stdin.forEach((_, i) => stdin[i] = i);
const res = await shellBufferExec(`${cli} exec --workspace-folder ${testFolder} cat`, { stdin });
assert.strictEqual(res.code, 0);
assert.equal(res.signal, undefined);
assert.ok(res.stdout.equals(stdin), 'stdout does not match stdin: ' + res.stdout.toString('hex'));
});
it('should run in a terminal', async () => {
const res = await shellPtyExec(`${cli} exec --workspace-folder ${testFolder} [ -t 1 ]`);
assert.strictEqual(res.code, 0);
assert.equal(res.signal, undefined);
});
it('should return exit code without terminal', async () => {
const res = await shellPtyExec(`${cli} exec --workspace-folder ${testFolder} sh -c 'exit 123'`);
assert.strictEqual(res.code, 123);
assert.equal(res.signal, 0);
});
it('should connect stdin', async () => {
const res = await shellPtyExec(`${cli} exec --workspace-folder ${testFolder} sh`, { stdin: 'echo hi\nexit\n' });
assert.strictEqual(res.code, 0);
assert.equal(res.signal, undefined);
assert.match(res.cmdOutput, /^hi$/m);
});
});
describe(`with valid (image) config containing features [${text}]`, () => {
Expand Down
39 changes: 39 additions & 0 deletions src/test/testUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as assert from 'assert';
import * as cp from 'child_process';
import { loadNativeModule, plainExec, plainPtyExec, runCommand, runCommandNoPty } from '../spec-common/commonUtils';
import { SubstituteConfig } from '../spec-node/utils';
import { nullLog } from '../spec-utils/log';

export interface BuildKitOption {
text: string;
Expand Down Expand Up @@ -41,6 +43,43 @@ export function shellExec(command: string, options: cp.ExecOptions = {}, suppres
});
}

export interface BufferExecResult {
stdout: Buffer;
stderr: Buffer;
message? : string;
code?: number | null;
signal?: string | null;
}

export async function shellBufferExec(command: string, options: { stdin?: Buffer } = {}): Promise<BufferExecResult> {
const exec = await plainExec(undefined);
return runCommandNoPty({
exec,
cmd: '/bin/sh',
args: ['-c', command],
stdin: options.stdin,
output: nullLog,
}).then(res => ({ code: 0, ...res }), error => error);
}

export interface ExecPtyResult {
cmdOutput: string;
message? : string;
code?: number;
signal?: number;
}

export async function shellPtyExec(command: string, options: { stdin?: string } = {}): Promise<ExecPtyResult> {
const ptyExec = await plainPtyExec(undefined, loadNativeModule);
return runCommand({
ptyExec,
cmd: '/bin/sh',
args: ['-c', command],
stdin: options.stdin,
output: nullLog,
}).then(res => ({ code: 0, ...res }), error => error);
}

export async function devContainerUp(cli: string, workspaceFolder: string, options?: { cwd?: string; useBuildKit?: boolean; userDataFolder?: string; logLevel?: string; extraArgs?: string }): Promise<UpResult> {
const buildkitOption = (options?.useBuildKit ?? false) ? '' : ' --buildkit=never';
const userDataFolderOption = (options?.userDataFolder ?? false) ? ` --user-data-folder=${options?.userDataFolder}` : '';
Expand Down

0 comments on commit edff38a

Please sign in to comment.