pxt-common-packages
Version:
Microsoft MakeCode (PXT) common packages
137 lines (120 loc) • 3.87 kB
text/typescript
/**
* Various test event in the execution cycle
*/
enum TestEvent {
//% block="run setup"
RunSetUp = 0,
//% block="run teardown"
RunTearDown = 1,
//% block="test setup"
TestSetUp = 2,
//% block="test teardown"
TestTearDown = 3
}
/**
* A Unit tests framework
*/
//% weight=100 color=#0fbc11 icon=""
namespace tests {
class Test {
name: string;
handler: () => void;
errors: string[];
constructor(name: string, handler: () => void) {
this.name = name;
this.handler = handler;
this.errors = [];
}
run() {
// clear state
if (_runSetup)
_runSetup();
console.log(`> ${this.name}`)
this.handler()
if (this.errors.length)
console.log('')
// ensure clean state after test
if (_runTearDown)
_runTearDown();
}
}
let _tests: Test[] = undefined;
let _currentTest: Test = undefined;
let _runSetup: () => void = undefined;
let _runTearDown: () => void = undefined;
let _testSetUp: () => void = undefined;
let _testTearDown: () => void = undefined;
function run() {
if (!_tests) return;
if (_testSetUp)
_testSetUp();
const start = control.millis();
console.log(`${_tests.length} tests found`)
console.log(` `)
for (let i = 0; i < _tests.length; ++i) {
const t = _currentTest = _tests[i];
t.run();
_currentTest = undefined;
}
if (_testTearDown)
_testTearDown();
console.log(` `)
console.log(`${_tests.length} tests, ${_tests.map(t => t.errors.length).reduce((p, c) => p + c, 0)} errs in ${Math.ceil((control.millis() - start) / 1000)}s`)
}
/**
* Registers a test to run
*/
//% blockId=testtest block="test %name"
//% weight=100
export function test(name: string, handler: () => void): void {
if (!name || !handler) return;
if (!_tests) {
_tests = [];
control.runInParallel(function () {
// should run after on start
pause(100)
run()
})
}
_tests.push(new Test(name, handler));
}
/**
* Checks a boolean condition
*/
//% blockId=testAssert block="assert %message|%condition"
//% weight=80
//% blockGap=8
export function assert(message: string, condition: boolean) {
if (!condition) {
console.log(`!!! ${message || ''}`)
if (_currentTest)
_currentTest.errors.push(message);
}
}
/**
* Checks that 2 values are close to each other
* @param expected what the value should be
* @param actual what the value was
* @param tolerance the acceptable error margin, eg: 5
*/
//% blockId=testAssertClose block="assert %message|%expected|close to %actual|by %tolerance"
//% weight=79
//% inlineInputMode=inline
export function assertClose(name: string, expected: number, actual: number, tolerance: number) {
assert(`${name} ${expected} != ${actual} +-${tolerance}`, Math.abs(expected - actual) <= tolerance);
}
/**
* Registers code to be called at various points in the test execution
* @param handler
*/
//% blockGap=8
//% weight=10
export function onEvent(event: TestEvent, handler: () => void) {
switch(event) {
case TestEvent.RunSetUp: _runSetup = handler; break;
case TestEvent.RunTearDown: _runTearDown = handler; break;
case TestEvent.TestSetUp: _testSetUp = handler; break;
case TestEvent.TestTearDown: _testTearDown = handler; break;
}
}
}