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
19 changes: 8 additions & 11 deletions src/framework/Framework.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import {HybridScheduler, Scheduler} from './Scheduler';
import {TestScenario} from './scenario/TestScenario';

import {TestbedSpecification} from '../testbeds/TestbedSpecification';
import {Reporter, SuiteResults} from './Reporter';
import {Reporter, SuiteResults} from '../reporter/Reporter';

import {StyleType} from '../reporter';
import {styling} from '../reporter/Style';

export interface Suite {

Expand All @@ -20,12 +23,6 @@ export interface TesteeOptions {
connectionTimout?: number;
}

export enum OutputStyle {
silent, // TODO
plain,
github
}

export class Suite {
public title: string;
public scenarios: TestScenario[] = [];
Expand Down Expand Up @@ -61,11 +58,11 @@ export class Framework {

public runs: number = 1;

private outputStyle: OutputStyle = OutputStyle.plain;
private outputStyle: StyleType = StyleType.plain;

private scheduled: Suite[] = [];

public readonly reporter: Reporter = new Reporter();
public readonly reporter: Reporter = new Reporter(styling(this.outputStyle));

private constructor() {
}
Expand All @@ -78,11 +75,11 @@ export class Framework {
return this.scheduled;
}

public style(style: OutputStyle): void {
public style(style: StyleType): void {
this.outputStyle = style;
}

public styling(): OutputStyle {
public styling(): StyleType {
return this.outputStyle;
}

Expand Down
3 changes: 2 additions & 1 deletion src/framework/Testee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import {TestScenario} from './scenario/TestScenario';
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';
import {Completion, expect, ScenarioResult, SuiteResults} from '../reporter/Reporter';
import {WASM} from '../sourcemap/Wasm';
import {DummyProxy} from '../testbeds/Emulator';
import {Result} from '../reporter/Result';

export function timeout<T>(label: string, time: number, promise: Promise<T>): Promise<T> {
if (time === 0) {
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export * from './framework/scenario/Step';
export * from './framework/scenario/Invoker';
export * from './testbeds/TestbedSpecification';
export * from './debug/Breakpoint';
export * from './reporter/index';

export const latch = Framework.getImplementation();
183 changes: 31 additions & 152 deletions src/framework/Reporter.ts → src/reporter/Reporter.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import {OutputStyle, Suite} from './Framework';
import {Behaviour, Description, Step} from './scenario/Step';
import {getValue, Testee} from './Testee';
import {blue, bold, green, red, yellow, reset, inverse, grey} from 'ansi-colors';
import {Archiver} from './Archiver';
import {TestScenario} from './scenario/TestScenario';
import {Suite} from '../framework/Framework';
import {Step} from '../framework/scenario/Step';
import {getValue, Testee} from '../framework/Testee';
import {blue, bold, green, inverse, red, yellow} from 'ansi-colors';
import {Archiver} from '../framework/Archiver';
import {TestScenario} from '../framework/scenario/TestScenario';
import {version} from '../../package.json';
import {indent} from '../util/printing';
import {Result} from './Result';
import {Verbosity} from './index';
import {Style} from './Style';

export enum Completion {
uncommenced = 'not started', // test hasn't started
Expand All @@ -15,20 +19,6 @@ export enum Completion {
skipped = 'skipped' // test has failing dependencies
}

export enum Verbosity {
none,
minimal,
short,
normal,
more,
all,
debug
}

function indent(level: number, size: number = 2): string {
return ' '.repeat(level * size);
}

export function expect(step: Step, actual: Object | void, previous?: Object): Result {
const result: Result = new Result(step.title, '');
result.completion = Completion.succeeded;
Expand Down Expand Up @@ -133,153 +123,49 @@ export class ScenarioResult {
}
}

export class Result {
public completion: Completion; // completion status of the step
public name: string; // name of the step
public description: string;

constructor(name: string, description: string, completion?: Completion) {
this.name = name;
this.description = description;
this.completion = completion ?? Completion.uncommenced;
}

report(level: number) {
console.log(reset(`${indent(level)}${this}`));
}

toString(): string {
switch (this.completion) {
case Completion.succeeded:
return `${bold(inverse(green(' PASS ')))} ${this.name}`;
case Completion.uncommenced:
return `${bold(inverse(yellow(' SKIP ')))} ${this.name}`;
case Completion.error:
case Completion.failed:
default:
return `${bold(inverse(red(' FAIL ')))} ${this.name}\n ${red(this.completion)}${red(this.description)}`;

}
}

error(description: string) {
this.completion = Completion.error;
this.description = description;
}

public expectPrimitive<T>(actual: T, expected: T): void {
// this.completion = deepEqual(actual, expected) ? Completion.succeeded : Completion.failed;
if (deepEqual(actual, expected)) {
this.completion = Completion.succeeded;
} else {
this.completion = Completion.failed;
this.description = `Expected ${bold(`${expected}`)} got ${bold(`${actual}`)}`;
}
}

public expectDescription<T>(actual: T, value: Description): void {
if ((value === Description.defined && actual !== undefined) ||
value === Description.notDefined && actual === undefined) {
this.completion = Completion.succeeded;
} else {
this.completion = Completion.failed;
this.description = value === Description.defined ? 'Should exist' : 'Unexpected field';
}
}

public expectComparison<T>(state: Object | void, actual: T, comparator: (state: Object, value: T) => boolean, message?: string): void {
if (state === undefined) {
this.completion = Completion.failed;
this.description = `Got unexpected ${state}`;
return;
}

if (comparator(state, actual)) {
this.completion = Completion.succeeded;
} else {
this.completion = Completion.failed;
this.description = 'custom comparator failed';
}
}

public expectBehaviour(actual: any, previous: any, behaviour: Behaviour): void {
switch (behaviour) {
case Behaviour.unchanged:
if (deepEqual(actual, previous)) {
this.completion = Completion.succeeded;
} else {
this.completion = Completion.failed;
this.description = `Expected ${actual} to equal ${previous}`
}
break;
case Behaviour.changed:
if (!deepEqual(actual, previous)) {
this.completion = Completion.succeeded;
} else {
this.completion = Completion.failed;
this.description = `Expected ${actual} to be different from ${previous}`
}
break;
case Behaviour.increased:
if (actual > previous) {
this.completion = Completion.succeeded;
} else {
this.completion = Completion.failed;
this.description = `Expected ${actual} to be greater than ${previous}`
}
break;
case Behaviour.decreased:
if (actual < previous) {
this.completion = Completion.succeeded;
} else {
this.completion = Completion.failed;
this.description = `Expected ${actual} to be less than ${previous}`
}
break;
}
}
}

// const r Result = expect(e: Expected) <-- replaces the function in Testee todo

export class Reporter {
private output: string = '';

private indentationLevel: number = 2;

private style: Style;

private suites: SuiteResults[] = [];

private archiver: Archiver;

private verbosity: Verbosity = Verbosity.short;

constructor() {
constructor(style: Style) {
this.style = style;
this.archiver = new Archiver(`${process.env.TESTFILE?.replace('.asserts.wast', '.wast') ?? 'suite'}.${Date.now()}.log`);
this.archiver.set('date', new Date(Date.now()).toISOString());
}

private indent(override?: number) {
return indent(override ?? this.indentationLevel);
return indent(override ?? this.indentationLevel, this.style.indentation);
}

general() {
console.log(this.indent() + blue(bold('● latch')) + bold(' General information'));
console.log(this.indent() + this.style.colors.highlight(this.style.bullet) + this.style.colors.highlight('latch') + this.style.emph(' General information'));
// console.log(blue(`${this.indent()}===================`));
console.log(this.indent() + ' '.repeat(2) + bold('version') + ' '.repeat(5) + version);
console.log(this.indent() + ' '.repeat(2) + bold('archive') + ' '.repeat(5) + this.archiver.archive);
console.log(this.indent() + ' '.repeat(2) + this.style.emph('version') + ' '.repeat(5) + version);
console.log(this.indent() + ' '.repeat(2) + this.style.emph('archive') + ' '.repeat(5) + this.archiver.archive);
console.log();
}

report(suiteResult: SuiteResults) {
this.suites.push(suiteResult);
const status = (suiteResult.error ? bold(inverse(red(' ERROR '))) :
(suiteResult.passing() ? bold(inverse(green(' PASSED '))) : bold(inverse(red(' FAIL ')))));
console.log(this.indent() + blue(bold('● suite')) + ` ${bold(suiteResult.title())}${(this.verbosity === Verbosity.minimal) ? ' ' + status : ''}`);
const status = (suiteResult.error ? this.style.colors.error(this.style.labels.error) :
(suiteResult.passing() ? this.style.colors.success(this.style.labels.suiteSuccess) : this.style.colors.failure(this.style.labels.failure)));
console.log(this.indent() + this.style.colors.highlight(this.style.bullet) + this.style.colors.highlight('suite') + ` ${this.style.emph(suiteResult.title())}${(this.verbosity === Verbosity.minimal) ? ' ' + status : ''}`);
if (this.verbosity > Verbosity.minimal) {
console.log(this.indent() + ' '.repeat(2) + bold('testbed') + ' '.repeat(5) + suiteResult.testee.name);
console.log(this.indent() + ' '.repeat(2) + bold('scenarios') + ' '.repeat(3) + suiteResult.scenarios.length);
console.log(this.indent() + ' '.repeat(2) + bold('actions') + ' '.repeat(5) + suiteResult.suite.scenarios.flatMap((scenario) => scenario.steps ?? []).flat().length); //.reduce((total, count) => total + count));
console.log(this.indent() + ' '.repeat(2) + bold('status') + ' '.repeat(6) + status);
console.log(this.indent() + ' '.repeat(2) + this.style.emph('testbed') + ' '.repeat(5) + suiteResult.testee.name);
console.log(this.indent() + ' '.repeat(2) + this.style.emph('scenarios') + ' '.repeat(3) + suiteResult.scenarios.length);
console.log(this.indent() + ' '.repeat(2) + this.style.emph('actions') + ' '.repeat(5) + suiteResult.suite.scenarios.flatMap((scenario) => scenario.steps ?? []).flat().length); //.reduce((total, count) => total + count));
console.log(this.indent() + ' '.repeat(2) + this.style.emph('status') + ' '.repeat(6) + status);
}
console.log();
if (this.verbosity >= Verbosity.normal) {
Expand Down Expand Up @@ -310,7 +196,7 @@ export class Reporter {
this.archiver.set('skipped scenarios', skipped);
this.archiver.set('failed scenarios', failing);

console.log(this.indent() + blue(bold('● results')) + bold(' Overview'));
console.log(this.indent() + this.style.colors.highlight(this.style.bullet) + this.style.colors.highlight('results') + this.style.emph(' Overview'));
console.log();
this.indentationLevel += 1;

Expand All @@ -331,22 +217,19 @@ export class Reporter {

const len: number = 12;
const pss = [`${sc} passing`, `${passing} passing`, `${psa} passing`]
console.log(this.indent() + bold('Test suites:') + ' '.repeat(len - pss[0].length) + bold((sc === tl ? green : red)(pss[0])) + `, ${tl} total` + bold(` (${time.toFixed(0)}ms)`));
console.log(this.indent() + this.style.emph('Test suites:') + ' '.repeat(len - pss[0].length) + this.style.emph((sc === tl ? green : red)(pss[0])) + `, ${tl} total` + this.style.emph(` (${time.toFixed(0)}ms)`));
if (this.verbosity > Verbosity.minimal) {
console.log(this.indent() + bold('Scenarios:') +
' '.repeat(2 + len - pss[1].length) + bold((passing === scs.length ? green : red)(pss[1])) +
(skipped > 0 ? ', ' + bold(yellow(`${skipped} skipped`)) : '') + `, ${scs.length} total`);
console.log(this.indent() + bold('Actions:') + ' '.repeat(4 + len - pss[2].length) + bold((passing === scs.length ? green : red)(pss[2])) + (timeouts > 0 ? `, ${timeouts} timeouts` : '') + `, ${total} total`);
console.log(this.indent() + this.style.emph('Scenarios:') +
' '.repeat(2 + len - pss[1].length) + this.style.emph((passing === scs.length ? green : red)(pss[1])) +
(skipped > 0 ? ', ' + this.style.emph(yellow(`${skipped} skipped`)) : '') + `, ${scs.length} total`);
console.log(this.indent() + this.style.emph('Actions:') + ' '.repeat(4 + len - pss[2].length) + this.style.emph((passing === scs.length ? green : red)(pss[2])) + (timeouts > 0 ? `, ${timeouts} timeouts` : '') + `, ${total} total`);
}
this.indentationLevel -= 1;

console.log();
this.archiver.write();
}

style(style: OutputStyle) {
}

info(text: string) {
this.output += `info: ${text}\n`;
}
Expand All @@ -367,7 +250,3 @@ export class Reporter {
this.output += ` ${result.toString()}\n`;
}
}

function deepEqual(a: any, b: any): boolean {
return a === b || (isNaN(a) && isNaN(b));
}
Loading