Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions src/framework/Testee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {TestbedFactory} from '../testbeds/TestbedFactory';
import {Kind} from './scenario/Step';
import {SourceMapFactory} from '../sourcemap/SourceMapFactory';
import {TestScenario} from './scenario/TestScenario';
import {TestbedSpecification} from '../testbeds/TestbedSpecification';
import {OutofPlaceSpecification, PlatformType, TestbedSpecification} from '../testbeds/TestbedSpecification';
import {CompileOutput, CompilerFactory} from '../manage/Compiler';
import {WABT} from '../util/env';
import {Completion, expect, Result, ScenarioResult, SuiteResults} from './Reporter';
Expand Down Expand Up @@ -38,6 +38,11 @@ export function getValue(object: any, field: string): any {
return object;
}

export enum Target {
supervisor,
proxy
}

export class Testee { // TODO unified with testbed interface

/** The current state for each described test */
Expand All @@ -58,7 +63,9 @@ export class Testee { // TODO unified with testbed interface

public readonly name: string;

public testbed?: Testbed;
private testbed?: Testbed;

private proxy?: Testbed;

private current?: string; // current program

Expand All @@ -71,9 +78,25 @@ export class Testee { // TODO unified with testbed interface
this.framework = Framework.getImplementation();
}

public async initialize(program: string, args: string[]): Promise<Testee> {
public bed(target: Target = Target.supervisor): Testbed | void {
return target == Target.proxy ? this.proxy : this.testbed;
}

public async initialize(program: string, args: string[] = []): Promise<Testee> {
return new Promise(async (resolve, reject) => {
const testbed: Testbed | void = await this.connector.initialize(this.specification, program, args ?? []).catch((e) => {
if (this.specification.type === PlatformType.emu2emu) {
const spec = (this.specification as OutofPlaceSpecification).proxy;
const proxy: Testbed | void = await this.connector.initialize(spec, program, args ?? []).catch((e) => {
reject(e)
});
if (proxy) {
this.proxy = proxy;
await this.proxy.sendRequest(new SourceMap.Mapping(), Message.proxifyRequest);
args.push(`--proxy ${spec.options.port}`);
}
}

const testbed: Testbed | void = await this.connector.initialize(this.specification, program, args).catch((e) => {
reject(e)
});
if (testbed) {
Expand All @@ -84,6 +107,7 @@ export class Testee { // TODO unified with testbed interface
}

public async shutdown(): Promise<void> {
await this.proxy?.kill();
return this.testbed?.kill();
}

Expand Down Expand Up @@ -125,7 +149,7 @@ export class Testee { // TODO unified with testbed interface

let compiled: CompileOutput = await new CompilerFactory(WABT).pickCompiler(description.program).compile(description.program);
try {
await timeout<Object | void>(`uploading module`, testee.timeout, testee.testbed!.sendRequest(new SourceMap.Mapping(), Message.updateModule(compiled.file))).catch((e) => Promise.reject(e));
await timeout<Object | void>(`uploading module`, testee.timeout, testee.bed()!.sendRequest(new SourceMap.Mapping(), Message.updateModule(compiled.file))).catch((e) => Promise.reject(e));
testee.current = description.program;
} catch (e) {
await testee.initialize(description.program, description.args ?? []).catch((o) => Promise.reject(o));
Expand Down Expand Up @@ -161,7 +185,7 @@ export class Testee { // TODO unified with testbed interface
/** Perform the step and check if expectations were met */
await this.step(step.title, testee.timeout, async function () {
let result: Result = new Result(step.title, 'incomplete');
if (testee.testbed === undefined) {
if (testee.bed(step.target ?? Target.supervisor) === undefined) {
testee.states.set(description.title, result);
result.error('Cannot run test: no debugger connection.');
testee.states.set(description.title, result);
Expand All @@ -177,7 +201,7 @@ export class Testee { // TODO unified with testbed interface
} else {
actual = await testee.recoverable(testee, step.instruction.value, map,
(testee, req, map) => timeout<Object | void>(`sending instruction ${req.type}`, testee.timeout,
testee.testbed!.sendRequest(map, req)),
testee.bed(step.target ?? Target.supervisor)!.sendRequest(map, req)),
(testee) => testee.run(`Recover: re-initialize ${testee.testbed?.name}`, testee.connector.timeout, async function () {
await testee.initialize(description.program, description.args ?? []).catch((o) => {
return Promise.reject(o)
Expand Down
4 changes: 2 additions & 2 deletions src/framework/scenario/Actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ export function awaitBreakpoint(): Action<Breakpoint> {
try {
const breakpoint = breakpointHitParser(message);
// on success: remove listener + resolve
testee.testbed?.removeListener(TestbedEvents.OnMessage, breakpointListener);
testee.bed()?.removeListener(TestbedEvents.OnMessage, breakpointListener);
resolve(assertable(breakpoint));
} catch (e) {

}
}

// await breakpoint hit
testee.testbed?.on(TestbedEvents.OnMessage, breakpointListener)
testee.bed()?.on(TestbedEvents.OnMessage, breakpointListener)
});
}
};
Expand Down
13 changes: 10 additions & 3 deletions src/framework/scenario/Invoker.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import {Expectation, Expected, Instruction, Kind, Step} from './Step';
import {WASM} from '../../sourcemap/Wasm';
import {Exception, Message} from '../../messaging/Message';
import {Message} from '../../messaging/Message';
import {Target} from '../Testee';
import Value = WASM.Value;

export class Invoker implements Step {
readonly title: string;
readonly instruction: Instruction;
readonly expected?: Expectation[];
readonly target?: Target;

constructor(func: string, args: Value[], result: Value) {
this.title = `ASSERT: ${func} ${args.map(val => val.value).join(' ')} ${result.value}`;
constructor(func: string, args: Value[], result: Value, target?: Target) {
let prefix = "";
this.instruction = invoke(func, args);
this.expected = returns(result);
if (target !== undefined) {
this.target = target;
prefix = `${target === Target.supervisor ? '[supervisor] ' : '[proxy] '}`
}
this.title = `${prefix}CHECK: (${func} ${args.map(val => val.value).join(' ')}) returns ${result.value}`;
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/framework/scenario/Step.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Action} from './Actions';
import {Request} from '../../messaging/Message'
import {Target} from '../Testee';

export enum Description {
/** required properties */
Expand Down Expand Up @@ -41,5 +42,7 @@ export interface Step {

readonly instruction: Instruction;

target?: Target;

readonly expected?: Expectation[];
}
5 changes: 3 additions & 2 deletions src/manage/Uploader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {SubProcess} from '../bridge/SubProcess';
import {Connection} from '../bridge/Connection';
import {Serial} from '../bridge/Serial';
import {CompileOutput} from './Compiler';
import {TestbedSpecification, PlatformType, SerialOptions, SubprocessOptions} from '../testbeds/TestbedSpecification';
import {PlatformType, SerialOptions, SubprocessOptions, TestbedSpecification} from '../testbeds/TestbedSpecification';

enum UploaderEvents {
compiled = 'compiled',
Expand Down Expand Up @@ -38,11 +38,12 @@ export class UploaderFactory {
case PlatformType.arduino:
return new ArduinoUploader(this.arduino, args, specification.options as SerialOptions);
case PlatformType.emulator:
case PlatformType.emu2emu:
return new EmulatorUploader(this.emulator, args, specification.options as SubprocessOptions);
case PlatformType.debug:
return new EmulatorConnector(specification.options as SubprocessOptions)
}
throw new Error('Unsupported file type');
throw new Error('Unsupported platform type');
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/messaging/Message.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {WARDuino} from '../debug/WARDuino';
import {ackParser, breakpointParser, invokeParser, stateParser} from './Parsers';
import {ackParser, breakpointParser, identityParser, invokeParser, stateParser} from './Parsers';
import {Breakpoint} from '../debug/Breakpoint';
import {WASM} from '../sourcemap/Wasm';
import {write} from 'ieee754';
Expand Down Expand Up @@ -210,4 +210,9 @@ export namespace Message {
type: Interrupt.dumpCallbackmapping,
parser: stateParser
}

export const proxifyRequest: Request<string> = {
type: Interrupt.proxify,
parser: identityParser
};
}
1 change: 1 addition & 0 deletions src/testbeds/TestbedFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export class TestbedFactory {
case PlatformType.arduino:
return new Arduino(connection as Serial);
case PlatformType.emulator:
case PlatformType.emu2emu:
case PlatformType.debug:
return new Emulator(connection as SubProcess);
default:
Expand Down
17 changes: 17 additions & 0 deletions src/testbeds/TestbedSpecification.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export enum PlatformType {
arduino,
emulator,
emu2emu,
debug
}

Expand All @@ -18,12 +19,28 @@ export interface SubprocessOptions extends ConnectionOptions {
port: number
}

export interface SupervisorOptions extends ConnectionOptions {
port: number,
proxy: number
}

export interface TestbedSpecification {
readonly type: PlatformType;
readonly options: ConnectionOptions;
}

export class OutofPlaceSpecification implements TestbedSpecification {
public readonly type: PlatformType = PlatformType.emu2emu;
public readonly options: SupervisorOptions;

public readonly proxy: EmulatorSpecification;

constructor(supervisor: number, proxy: number) {
this.options = {port: supervisor, proxy: proxy};
this.proxy = new EmulatorSpecification(proxy);
}
}

export class EmulatorSpecification implements TestbedSpecification {
public readonly type: PlatformType;
public readonly options: SubprocessOptions;
Expand Down
28 changes: 28 additions & 0 deletions test/dummy.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

(module
;; Type declarations
(type $int32->int32->int32 (func (param i32 i32) (result i32)))
(type $int32->int32 (func (param i32) (result i32)))
(type $int32->void (func (param i32)))
(type $void->void (func))

;; Imports from the WARDuino VM
(import "env" "dummy" (func $env.dummy (type $int32->int32->int32)))
(import "env" "print_int" (func $env.print_int (type $int32->void)))

;; Memory
(memory 1)

;; Main function
(func $main (export "main") (type $void->void)
(call $env.dummy (i32.const 32) (i32.const 64))
(drop)
(call $env.print_int (i32.load (i32.const 32)))
)

(func $dummy (export "dummy") (type $int32->int32->int32)
(call $env.dummy (local.get 0) (local.get 1)))

(func $load (export "load") (type $int32->int32)
(i32.load (local.get 0)))
)
Loading