added test framework (#113)
* added test framework * added toString on motors * enabling logs
This commit is contained in:
		@@ -1,94 +1,47 @@
 | 
			
		||||
```typescript
 | 
			
		||||
let errors: string[] = [];
 | 
			
		||||
let tachoB = 0;
 | 
			
		||||
let tachoC = 0;
 | 
			
		||||
 | 
			
		||||
function assert(name: string, condition: boolean) {
 | 
			
		||||
    if (!condition) {
 | 
			
		||||
        errors.push(name)
 | 
			
		||||
        brick.print("error:" + name, 0, 60)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function assertClose(name: string, expected: number, actual: number, tolerance = 5) {
 | 
			
		||||
    const diff = Math.abs(expected - actual);
 | 
			
		||||
    assert(name + ` ${expected}vs${actual}`, diff < tolerance);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function state(name: string, motor: motors.SingleMotor, line: number) {
 | 
			
		||||
    brick.print(`${name}: ${motor.speed()}%,${motor.angle()}deg`, 0, 12 * line)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function test(name: string, f: () => void, check?: () => void) {
 | 
			
		||||
    motors.stopAllMotors();
 | 
			
		||||
    motors.largeB.clearCount()
 | 
			
		||||
    motors.largeC.clearCount()
 | 
			
		||||
    motors.largeB.setBrake(false)
 | 
			
		||||
    motors.largeC.setBrake(false)
 | 
			
		||||
    motors.largeB.setReversed(false);
 | 
			
		||||
    motors.largeC.setReversed(false);
 | 
			
		||||
    loops.pause(500);
 | 
			
		||||
    tachoB = motors.largeB.angle()
 | 
			
		||||
    tachoC = motors.largeC.angle()
 | 
			
		||||
    brick.clearScreen()
 | 
			
		||||
    brick.print(name, 0, 0)
 | 
			
		||||
    state("B", motors.largeB, 1)
 | 
			
		||||
    state("C", motors.largeC, 2)
 | 
			
		||||
    f();
 | 
			
		||||
    loops.pause(3000);
 | 
			
		||||
    state("B", motors.largeB, 3)
 | 
			
		||||
    state("C", motors.largeC, 4)
 | 
			
		||||
    if (check)
 | 
			
		||||
        check()
 | 
			
		||||
    motors.stopAllMotors();
 | 
			
		||||
    loops.pause(1000);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
brick.buttonLeft.onEvent(ButtonEvent.Click, function () {
 | 
			
		||||
    test("lgBC set speed 25", () => {
 | 
			
		||||
        motors.largeBC.setSpeed(25)
 | 
			
		||||
    }, () => {
 | 
			
		||||
        assertClose("speedB", 25, motors.largeB.speed());
 | 
			
		||||
        assertClose("speedC", 25, motors.largeC.speed());
 | 
			
		||||
    });
 | 
			
		||||
tests.test("lgB set speed 10", () => {
 | 
			
		||||
    motors.largeB.setSpeed(10);
 | 
			
		||||
    loops.pause(100)
 | 
			
		||||
    tests.assertClose("speedB", 10, motors.largeB.speed(), 2)
 | 
			
		||||
});
 | 
			
		||||
tests.test("lgB set speed 25 (reversed)", () => {
 | 
			
		||||
    motors.largeB.setReversed(true)
 | 
			
		||||
    motors.largeB.setSpeed(25)
 | 
			
		||||
    loops.pause(100)
 | 
			
		||||
    tests.assertClose("speedB", -25, motors.largeB.speed(), 2)
 | 
			
		||||
});
 | 
			
		||||
tests.test("lgBC set speed 5", () => {
 | 
			
		||||
    motors.largeBC.setSpeed(5)
 | 
			
		||||
    loops.pause(100)
 | 
			
		||||
    tests.assertClose("speedB", 5, motors.largeB.speed(), 1);
 | 
			
		||||
    tests.assertClose("speedC", 5, motors.largeC.speed(), 1);
 | 
			
		||||
});
 | 
			
		||||
tests.test("lgBC steer 50% 2x", () => {
 | 
			
		||||
    motors.largeBC.setBrake(true)
 | 
			
		||||
    motors.largeBC.steer(50, 50, 1, MoveUnit.Rotations)
 | 
			
		||||
    loops.pause(1000)
 | 
			
		||||
    tests.assertClose("largeB", 360, motors.largeB.angle(), 5)
 | 
			
		||||
    motors.largeBC.setBrake(false)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
brick.buttonEnter.onEvent(ButtonEvent.Click, function () {
 | 
			
		||||
    test("lgB set speed 10", () => {
 | 
			
		||||
        motors.largeB.setSpeed(10)
 | 
			
		||||
    }, () => assertClose("speedB", 10, motors.largeB.speed()));
 | 
			
		||||
    test("lgB set speed 25 (reversed)", () => {
 | 
			
		||||
        motors.largeB.setReversed(true)
 | 
			
		||||
        motors.largeB.setSpeed(25)
 | 
			
		||||
    }, () => assertClose("speedB", -25, motors.largeB.speed()));
 | 
			
		||||
    test("lgBC set speed 5", () => {
 | 
			
		||||
        motors.largeBC.setSpeed(5)
 | 
			
		||||
    }, () => {
 | 
			
		||||
        assertClose("speedB", 5, motors.largeB.speed());
 | 
			
		||||
        assertClose("speedC", 5, motors.largeC.speed());
 | 
			
		||||
    });
 | 
			
		||||
    test("lgBC steer 50% 2x", () => {
 | 
			
		||||
        motors.largeBC.setBrake(true)
 | 
			
		||||
        motors.largeBC.steer(50, 50, 2, MoveUnit.Rotations)
 | 
			
		||||
    }, () => assertClose("largeB", 720, motors.largeB.angle()));
 | 
			
		||||
    test("lgBC steer 50% 500deg", () => {
 | 
			
		||||
        motors.largeBC.setBrake(true)
 | 
			
		||||
        motors.largeBC.steer(50, 50, 500, MoveUnit.Degrees)
 | 
			
		||||
    }, () => assertClose("largeB", 500, motors.largeB.angle()));
 | 
			
		||||
    test("lgBC steer 50% 2s", () => {
 | 
			
		||||
        motors.largeBC.setBrake(true)
 | 
			
		||||
        motors.largeBC.steer(50, 50, 2000, MoveUnit.MilliSeconds)
 | 
			
		||||
    })
 | 
			
		||||
    test("lgBC tank 50% 720deg", () => {
 | 
			
		||||
        motors.largeBC.setBrake(true)
 | 
			
		||||
        motors.largeBC.tank(50, 50, 720, MoveUnit.Degrees)
 | 
			
		||||
    }, () => assertClose("largeB", 720, motors.largeB.angle()));
 | 
			
		||||
 | 
			
		||||
    brick.clearScreen()
 | 
			
		||||
    brick.print(`${errors.length} errors`, 0, 0)
 | 
			
		||||
    let l = 1;
 | 
			
		||||
    for (const error of errors)
 | 
			
		||||
        brick.print(`error: ${error}`, 0, l++ * 12)
 | 
			
		||||
tests.test("lgBC steer 50% 500deg", () => {
 | 
			
		||||
    motors.largeBC.setBrake(true)
 | 
			
		||||
    motors.largeBC.steer(50, 50, 135, MoveUnit.Degrees)
 | 
			
		||||
    loops.pause(1000)
 | 
			
		||||
    tests.assertClose("largeB", 135, motors.largeB.angle(), 5)
 | 
			
		||||
});
 | 
			
		||||
tests.test("lgBC steer 50% 2s", () => {
 | 
			
		||||
    motors.largeBC.setBrake(true)
 | 
			
		||||
    motors.largeBC.steer(50, 50, 500, MoveUnit.MilliSeconds)
 | 
			
		||||
    loops.pause(1000)
 | 
			
		||||
})
 | 
			
		||||
tests.test("lgBC tank 50% 720deg", () => {
 | 
			
		||||
    motors.largeBC.setBrake(true)
 | 
			
		||||
    motors.largeBC.tank(50, 50, 180, MoveUnit.Degrees)
 | 
			
		||||
    loops.pause(1000)
 | 
			
		||||
    tests.assertClose("largeB", 180, motors.largeB.angle(), 5)
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```package
 | 
			
		||||
tests
 | 
			
		||||
```
 | 
			
		||||
@@ -43,7 +43,7 @@
 | 
			
		||||
  "brick.setStatusLight|param|pattern": "the lights pattern to use.",
 | 
			
		||||
  "brick.showImage": "Shows an image on screen",
 | 
			
		||||
  "brick.showImage|param|image": "image to draw",
 | 
			
		||||
  "console": "Reading and writing data to the console output.\r\n\r\nReading and writing data to the console output.",
 | 
			
		||||
  "console": "Reading and writing data to the console output.\n\nReading and writing data to the console output.",
 | 
			
		||||
  "console.addListener": "Adds a listener for the log messages",
 | 
			
		||||
  "console.log": "Write a line of text to the console output.",
 | 
			
		||||
  "console.logValue": "Write a name:value pair as a line of text to the console output.",
 | 
			
		||||
@@ -58,23 +58,24 @@
 | 
			
		||||
  "control.raiseEvent": "Announce that an event happened to registered handlers.",
 | 
			
		||||
  "control.raiseEvent|param|src": "ID of the Component that generated the event",
 | 
			
		||||
  "control.raiseEvent|param|value": "Component specific code indicating the cause of the event.",
 | 
			
		||||
  "motors.Motor.isReady": "Returns a value indicating if the motor is still running a previous command.",
 | 
			
		||||
  "motors.Motor.move": "Moves the motor by a number of rotations, degress or seconds",
 | 
			
		||||
  "motors.Motor.move|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
 | 
			
		||||
  "motors.Motor.move|param|unit": "the meaning of the value",
 | 
			
		||||
  "motors.Motor.move|param|value": "the move quantity, eg: 2",
 | 
			
		||||
  "motors.Motor.pauseUntilReady": "Pauses the execution until the previous command finished.",
 | 
			
		||||
  "motors.Motor.pauseUntilReady|param|timeOut": "optional maximum pausing time in milliseconds",
 | 
			
		||||
  "motors.Motor.reset": "Resets the motor(s).",
 | 
			
		||||
  "motors.Motor.setBrake": "Sets the automatic brake on or off when the motor is off",
 | 
			
		||||
  "motors.Motor.setBrake|param|brake": "a value indicating if the motor should break when off",
 | 
			
		||||
  "motors.Motor.setReversed": "Reverses the motor polarity",
 | 
			
		||||
  "motors.Motor.setSpeed": "Sets the speed of the motor.",
 | 
			
		||||
  "motors.Motor.setSpeed|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
 | 
			
		||||
  "motors.Motor.stop": "Stops the motor(s).",
 | 
			
		||||
  "motors.SingleMotor.angle": "Gets motor ration angle.",
 | 
			
		||||
  "motors.SingleMotor.clearCount": "Clears the motor count",
 | 
			
		||||
  "motors.SingleMotor.speed": "Gets motor actual speed.",
 | 
			
		||||
  "motors.Motor.angle": "Gets motor ration angle.",
 | 
			
		||||
  "motors.Motor.clearCount": "Clears the motor count",
 | 
			
		||||
  "motors.Motor.speed": "Gets motor actual speed.",
 | 
			
		||||
  "motors.Motor.toString": "Returns the status of the motor",
 | 
			
		||||
  "motors.MotorBase.isReady": "Returns a value indicating if the motor is still running a previous command.",
 | 
			
		||||
  "motors.MotorBase.move": "Moves the motor by a number of rotations, degress or seconds",
 | 
			
		||||
  "motors.MotorBase.move|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
 | 
			
		||||
  "motors.MotorBase.move|param|unit": "the meaning of the value",
 | 
			
		||||
  "motors.MotorBase.move|param|value": "the move quantity, eg: 2",
 | 
			
		||||
  "motors.MotorBase.pauseUntilReady": "Pauses the execution until the previous command finished.",
 | 
			
		||||
  "motors.MotorBase.pauseUntilReady|param|timeOut": "optional maximum pausing time in milliseconds",
 | 
			
		||||
  "motors.MotorBase.reset": "Resets the motor(s).",
 | 
			
		||||
  "motors.MotorBase.setBrake": "Sets the automatic brake on or off when the motor is off",
 | 
			
		||||
  "motors.MotorBase.setBrake|param|brake": "a value indicating if the motor should break when off",
 | 
			
		||||
  "motors.MotorBase.setReversed": "Reverses the motor polarity",
 | 
			
		||||
  "motors.MotorBase.setSpeed": "Sets the speed of the motor.",
 | 
			
		||||
  "motors.MotorBase.setSpeed|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
 | 
			
		||||
  "motors.MotorBase.stop": "Stops the motor(s).",
 | 
			
		||||
  "motors.SynchedMotorPair.steer": "Turns the motor and the follower motor by a number of rotations",
 | 
			
		||||
  "motors.SynchedMotorPair.steer|param|speed": "the speed from ``100`` full forward to ``-100`` full backward, eg: 50",
 | 
			
		||||
  "motors.SynchedMotorPair.steer|param|steering": "the ratio of power sent to the follower motor, from ``-100`` to ``100``",
 | 
			
		||||
@@ -84,7 +85,17 @@
 | 
			
		||||
  "motors.SynchedMotorPair.tank|param|speedRight": "the speed on the right motor, eg: 50",
 | 
			
		||||
  "motors.SynchedMotorPair.tank|param|unit": "@param speedLeft the speed on the left motor, eg: 50",
 | 
			
		||||
  "motors.SynchedMotorPair.tank|param|value": "the amount of movement, eg: 2",
 | 
			
		||||
  "motors.SynchedMotorPair.toString": "Returns the name(s) of the motor",
 | 
			
		||||
  "motors.mkCmd": "Allocates a message buffer",
 | 
			
		||||
  "motors.mkCmd|param|addSize": "required additional bytes",
 | 
			
		||||
  "motors.mkCmd|param|cmd": "command id",
 | 
			
		||||
  "motors.mkCmd|param|out": "ports",
 | 
			
		||||
  "motors.readPWM": "Sends and receives a message from the motors device",
 | 
			
		||||
  "motors.readPWM|param|buf": "message buffer",
 | 
			
		||||
  "motors.resetAllMotors": "Resets all motors",
 | 
			
		||||
  "motors.stopAllMotors": "Stops all motors",
 | 
			
		||||
  "motors.writePWM": "Sends a command to the motors device",
 | 
			
		||||
  "motors.writePWM|param|buf": "the command buffer",
 | 
			
		||||
  "output.createBuffer": "Create a new zero-initialized buffer.",
 | 
			
		||||
  "output.createBuffer|param|size": "number of bytes in the buffer",
 | 
			
		||||
  "screen.clear": "Clear screen and reset font to normal.",
 | 
			
		||||
 
 | 
			
		||||
@@ -47,13 +47,13 @@
 | 
			
		||||
  "console|block": "console",
 | 
			
		||||
  "control.raiseEvent|block": "raise event|from %src|with value %value",
 | 
			
		||||
  "control|block": "control",
 | 
			
		||||
  "motors.Motor.move|block": "move `icons.motorLarge` %motor|for %value|%unit|at %speed|%",
 | 
			
		||||
  "motors.Motor.pauseUntilReady|block": "%motor|pause until ready",
 | 
			
		||||
  "motors.Motor.setBrake|block": "set `icons.motorLarge` %motor|brake %brake",
 | 
			
		||||
  "motors.Motor.setReversed|block": "set `icons.motorLarge` %motor|reversed %reversed",
 | 
			
		||||
  "motors.Motor.setSpeed|block": "set speed of `icons.motorLarge` %motor|to %speed|%",
 | 
			
		||||
  "motors.SingleMotor.angle|block": "`icons.motorLarge` %motor|angle",
 | 
			
		||||
  "motors.SingleMotor.speed|block": "`icons.motorLarge` %motor|speed",
 | 
			
		||||
  "motors.Motor.angle|block": "`icons.motorLarge` %motor|angle",
 | 
			
		||||
  "motors.Motor.speed|block": "`icons.motorLarge` %motor|speed",
 | 
			
		||||
  "motors.MotorBase.move|block": "move `icons.motorLarge` %motor|for %value|%unit|at %speed|%",
 | 
			
		||||
  "motors.MotorBase.pauseUntilReady|block": "%motor|pause until ready",
 | 
			
		||||
  "motors.MotorBase.setBrake|block": "set `icons.motorLarge` %motor|brake %brake",
 | 
			
		||||
  "motors.MotorBase.setReversed|block": "set `icons.motorLarge` %motor|reversed %reversed",
 | 
			
		||||
  "motors.MotorBase.setSpeed|block": "set speed of `icons.motorLarge` %motor|to %speed|%",
 | 
			
		||||
  "motors.SynchedMotorPair.steer|block": "steer %chassis|%steering|%|at speed %speed|%|by %value|%unit",
 | 
			
		||||
  "motors.SynchedMotorPair.tank|block": "tank %chassis|left %speedLeft|%|right %speedRight|%|by %value|%unit",
 | 
			
		||||
  "motors.largeAB|block": "large A+B",
 | 
			
		||||
 
 | 
			
		||||
@@ -53,32 +53,56 @@ namespace motors {
 | 
			
		||||
        motorMM = control.mmap("/dev/lms_motor", MotorDataOff.Size * DAL.NUM_OUTPUTS, 0)
 | 
			
		||||
        if (!motorMM) control.fail("no motor file")
 | 
			
		||||
 | 
			
		||||
        resetMotors()
 | 
			
		||||
        resetAllMotors()
 | 
			
		||||
 | 
			
		||||
        let buf = output.createBuffer(1)
 | 
			
		||||
        const buf = output.createBuffer(1)
 | 
			
		||||
        buf[0] = DAL.opProgramStart
 | 
			
		||||
        writePWM(buf)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function writePWM(buf: Buffer): void {
 | 
			
		||||
    /**
 | 
			
		||||
     * Sends a command to the motors device
 | 
			
		||||
     * @param buf the command buffer
 | 
			
		||||
     */
 | 
			
		||||
    //%
 | 
			
		||||
    export function writePWM(buf: Buffer): void {
 | 
			
		||||
        init()
 | 
			
		||||
        pwmMM.write(buf)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function readPWM(buf: Buffer): number {
 | 
			
		||||
    /**
 | 
			
		||||
     * Sends and receives a message from the motors device
 | 
			
		||||
     * @param buf message buffer
 | 
			
		||||
     */
 | 
			
		||||
    //%
 | 
			
		||||
    export function readPWM(buf: Buffer): number {
 | 
			
		||||
        init()
 | 
			
		||||
        return pwmMM.read(buf);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function mkCmd(out: Output, cmd: number, addSize: number) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Allocates a message buffer
 | 
			
		||||
     * @param out ports
 | 
			
		||||
     * @param cmd command id
 | 
			
		||||
     * @param addSize required additional bytes
 | 
			
		||||
     */
 | 
			
		||||
    //%
 | 
			
		||||
    export function mkCmd(out: Output, cmd: number, addSize: number) {
 | 
			
		||||
        const b = output.createBuffer(2 + addSize)
 | 
			
		||||
        b.setNumber(NumberFormat.UInt8LE, 0, cmd)
 | 
			
		||||
        b.setNumber(NumberFormat.UInt8LE, 1, out)
 | 
			
		||||
        return b
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function resetMotors() {
 | 
			
		||||
        reset(Output.ALL)
 | 
			
		||||
    function outputToName(out: Output): string {
 | 
			
		||||
        let r = "";
 | 
			
		||||
        for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
 | 
			
		||||
            if (out & (1 << i)) {
 | 
			
		||||
                if (r.length > 0) r += "+";
 | 
			
		||||
                r += "ABCD"[i];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return r;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -92,9 +116,18 @@ namespace motors {
 | 
			
		||||
        writePWM(b)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resets all motors
 | 
			
		||||
     */
 | 
			
		||||
    //% group="Motion"
 | 
			
		||||
    export function resetAllMotors() {
 | 
			
		||||
        reset(Output.ALL)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //% fixedInstances
 | 
			
		||||
    export class Motor extends control.Component {
 | 
			
		||||
    export class MotorBase extends control.Component {
 | 
			
		||||
        protected _port: Output;
 | 
			
		||||
        protected _portName: string;
 | 
			
		||||
        protected _brake: boolean;
 | 
			
		||||
        private _initialized: boolean;
 | 
			
		||||
        private _init: () => void;
 | 
			
		||||
@@ -104,6 +137,7 @@ namespace motors {
 | 
			
		||||
        constructor(port: Output, init: () => void, setSpeed: (speed: number) => void, move: (steps: boolean, stepsOrTime: number, speed: number) => void) {
 | 
			
		||||
            super();
 | 
			
		||||
            this._port = port;
 | 
			
		||||
            this._portName = outputToName(this._port);
 | 
			
		||||
            this._brake = false;
 | 
			
		||||
            this._initialized = false;
 | 
			
		||||
            this._init = init;
 | 
			
		||||
@@ -244,7 +278,7 @@ namespace motors {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //% fixedInstances
 | 
			
		||||
    export class SingleMotor extends Motor {
 | 
			
		||||
    export class Motor extends MotorBase {
 | 
			
		||||
        private _large: boolean;
 | 
			
		||||
 | 
			
		||||
        constructor(port: Output, large: boolean) {
 | 
			
		||||
@@ -322,34 +356,42 @@ namespace motors {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Returns the status of the motor
 | 
			
		||||
         */
 | 
			
		||||
        //%
 | 
			
		||||
        toString(): string {
 | 
			
		||||
            return `${this._large ? "" : "M"}${this._portName} ${this.speed()}% ${this.angle()}>`;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //% whenUsed fixedInstance block="large A"
 | 
			
		||||
    export const largeA = new SingleMotor(Output.A, true);
 | 
			
		||||
    export const largeA = new Motor(Output.A, true);
 | 
			
		||||
 | 
			
		||||
    //% whenUsed fixedInstance block="large B"
 | 
			
		||||
    export const largeB = new SingleMotor(Output.B, true);
 | 
			
		||||
    export const largeB = new Motor(Output.B, true);
 | 
			
		||||
 | 
			
		||||
    //% whenUsed fixedInstance block="large C"
 | 
			
		||||
    export const largeC = new SingleMotor(Output.C, true);
 | 
			
		||||
    export const largeC = new Motor(Output.C, true);
 | 
			
		||||
 | 
			
		||||
    //% whenUsed fixedInstance block="large D"
 | 
			
		||||
    export const largeD = new SingleMotor(Output.D, true);
 | 
			
		||||
    export const largeD = new Motor(Output.D, true);
 | 
			
		||||
 | 
			
		||||
    //% whenUsed fixedInstance block="medium A"
 | 
			
		||||
    export const mediumA = new SingleMotor(Output.A, false);
 | 
			
		||||
    export const mediumA = new Motor(Output.A, false);
 | 
			
		||||
 | 
			
		||||
    //% whenUsed fixedInstance block="medium B"
 | 
			
		||||
    export const mediumB = new SingleMotor(Output.B, false);
 | 
			
		||||
    export const mediumB = new Motor(Output.B, false);
 | 
			
		||||
 | 
			
		||||
    //% whenUsed fixedInstance block="medium C"
 | 
			
		||||
    export const mediumC = new SingleMotor(Output.C, false);
 | 
			
		||||
    export const mediumC = new Motor(Output.C, false);
 | 
			
		||||
 | 
			
		||||
    //% whenUsed fixedInstance block="medium D"
 | 
			
		||||
    export const mediumD = new SingleMotor(Output.D, false);
 | 
			
		||||
    export const mediumD = new Motor(Output.D, false);
 | 
			
		||||
 | 
			
		||||
    //% fixedInstances
 | 
			
		||||
    export class SynchedMotorPair extends Motor {
 | 
			
		||||
    export class SynchedMotorPair extends MotorBase {
 | 
			
		||||
 | 
			
		||||
        constructor(ports: Output) {
 | 
			
		||||
            super(ports, () => this.__init(), (speed) => this.__setSpeed(speed), (steps, stepsOrTime, speed) => this.__move(steps, stepsOrTime, speed));
 | 
			
		||||
@@ -458,6 +500,20 @@ namespace motors {
 | 
			
		||||
            const steering = (speedRight * 100 / speedLeft) >> 0;
 | 
			
		||||
            this.steer(speedLeft, steering, value, unit);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Returns the name(s) of the motor
 | 
			
		||||
         */
 | 
			
		||||
        //%
 | 
			
		||||
        toString(): string {
 | 
			
		||||
            let r = outputToName(this._port);
 | 
			
		||||
            for (let i = 0; i < DAL.NUM_OUTPUTS; ++i) {
 | 
			
		||||
                if (this._port & (1 << i)) {
 | 
			
		||||
                    r += ` ${getMotorData(1 << i).actualSpeed}%`
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return r;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //% whenUsed fixedInstance block="large B+C"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								libs/tests/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								libs/tests/README.md
									
									
									
									
									
										Normal 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
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										9
									
								
								libs/tests/_locales/tests-jsdoc-strings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								libs/tests/_locales/tests-jsdoc-strings.json
									
									
									
									
									
										Normal 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"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								libs/tests/_locales/tests-strings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								libs/tests/_locales/tests-strings.json
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										14
									
								
								libs/tests/pxt.json
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										89
									
								
								libs/tests/tests.ts
									
									
									
									
									
										Normal 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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -15,7 +15,8 @@
 | 
			
		||||
        "libs/ultrasonic-sensor",
 | 
			
		||||
        "libs/infrared-sensor",
 | 
			
		||||
        "libs/gyro-sensor",
 | 
			
		||||
        "libs/ev3"
 | 
			
		||||
        "libs/ev3",
 | 
			
		||||
        "libs/tests"
 | 
			
		||||
    ],
 | 
			
		||||
    "simulator": {
 | 
			
		||||
        "autoRun": true,
 | 
			
		||||
@@ -52,8 +53,10 @@
 | 
			
		||||
    "serial": {
 | 
			
		||||
      "vendorId": "0x0694",
 | 
			
		||||
      "productId": "0x0005",
 | 
			
		||||
      "rawHID": true
 | 
			
		||||
    },
 | 
			
		||||
      "rawHID": true,
 | 
			
		||||
      "useEditor": true,
 | 
			
		||||
      "log": true
 | 
			
		||||
  },
 | 
			
		||||
    "runtime": {
 | 
			
		||||
        "mathBlocks": true,
 | 
			
		||||
        "loopsBlocks": true,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user