added test framework (#113)

* added test framework

* added toString on motors

* enabling logs
This commit is contained in:
Peli de Halleux
2017-12-19 07:07:50 -08:00
committed by GitHub
parent 60bf3a17d3
commit 9e427898ae
10 changed files with 311 additions and 135 deletions

35
libs/tests/README.md Normal file
View File

@ -0,0 +1,35 @@
# tests
A unit test framework
## Defining tests
Tests are registered as event handlers. They will automatically run once ``on start`` is finished.
```blocks
tests.test("lgB set speed 10", () => {
motors.largeB.setSpeed(10);
loops.pause(100)
tests.assertClose("speedB", 10, motors.largeB.speed(), 2)
});
```
## Assertions
The library has various asserts that will register fault. Note that since exceptions are not available, assertion failure **do not** stop the program execution.
* **assert** checks a boolean condition
```blocks
tests.assert("speed positive", motors.largeB.speed() > 0)
```
* **assert close** checks that a numberical value is within a particular range
```blocks
tests.assertClose("speed", motors.largeB.speed(), 10, 2)
```
```package
tests
```

View File

@ -0,0 +1,9 @@
{
"tests": "Unit tests framework",
"tests.assert": "Checks a boolean condition",
"tests.assertClose": "Checks that 2 values are close to each other",
"tests.assertClose|param|actual": "what the value was",
"tests.assertClose|param|expected": "what the value should be",
"tests.assertClose|param|tolerance": "the acceptable error margin",
"tests.test": "Registers a test to run"
}

View File

@ -0,0 +1,6 @@
{
"tests.assert|block": "assert %message|%condition",
"tests.test|block": "test %name",
"tests|block": "tests",
"{id:category}Tests": "Tests"
}

14
libs/tests/pxt.json Normal file
View File

@ -0,0 +1,14 @@
{
"name": "tests",
"description": "A unit test library",
"files": [
"README.md",
"tests.ts"
],
"testFiles": [
],
"public": true,
"dependencies": {
"core": "file:../core"
}
}

89
libs/tests/tests.ts Normal file
View File

@ -0,0 +1,89 @@
/**
* 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 = [];
}
reset() {
motors.stopAllMotors();
motors.resetAllMotors();
}
run() {
// clear state
this.reset();
console.log(`# ${this.name}`)
this.handler()
// ensure clean state after test
this.reset();
}
}
let _tests: Test[] = undefined;
let _currentTest: Test = undefined;
function run() {
if (!_tests) return;
const start = control.millis();
console.sendToScreen();
console.log(`${_tests.length} tests`)
for (let i = 0; i < _tests.length; ++i) {
const t = _currentTest = _tests[i];
t.run();
_currentTest = undefined;
}
console.log(`${_tests.map(t => t.errors.length).reduce((p, c) => p + c, 0)} X, ${Math.ceil((control.millis() - start) / 1000)}s`)
}
/**
* Registers a test to run
*/
//% blockId=testtest block="test %name"
export function test(name: string, handler: () => void): void {
if (!name || !handler) return;
if (!_tests) {
_tests = [];
control.runInBackground(function () {
// should run after on start
loops.pause(100)
run()
})
}
_tests.push(new Test(name, handler));
}
/**
* Checks a boolean condition
*/
//% blockId=testAssert block="assert %message|%condition"
export function assert(message: string, condition: boolean) {
if (!condition) {
console.log(` X ${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
*/
export function assertClose(name: string, expected: number, actual: number, tolerance: number) {
assert(`${name} ${expected} != ${actual} +-${tolerance}`, Math.abs(expected - actual) <= tolerance);
}
}