Mike Gerwitz

Activist for User Freedom

aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Gerwitz <mike.gerwitz@rtspecialty.com>2018-02-16 12:19:37 -0500
committerMike Gerwitz <mike.gerwitz@rtspecialty.com>2018-02-19 15:21:14 -0500
commit47f0f4039b9c6d4a9898714dcca4692fd8af154a (patch)
tree2b92cae5fa2c0e57b474a4652aee2da7a7358513 /progtest/src
parent0c020b736d8d46f18e2e671d7b928e146db5572e (diff)
downloadtame-47f0f4039b9c6d4a9898714dcca4692fd8af154a.tar.gz
tame-47f0f4039b9c6d4a9898714dcca4692fd8af154a.tar.bz2
tame-47f0f4039b9c6d4a9898714dcca4692fd8af154a.zip
progtest: Initial working console runner
Diffstat (limited to 'progtest/src')
-rw-r--r--progtest/src/TestCase.js103
-rw-r--r--progtest/src/TestRunner.js153
-rw-r--r--progtest/src/reader/ConstResolver.js110
-rw-r--r--progtest/src/reader/DateResolver.js61
-rw-r--r--progtest/src/reader/TestReader.js40
-rw-r--r--progtest/src/reader/YamlTestReader.js79
-rw-r--r--progtest/src/reporter/ConsoleTestReporter.js183
-rw-r--r--progtest/src/reporter/NullTestReporter.js79
-rw-r--r--progtest/src/reporter/TestReporter.js68
9 files changed, 876 insertions, 0 deletions
diff --git a/progtest/src/TestCase.js b/progtest/src/TestCase.js
new file mode 100644
index 0000000..5ea39a3
--- /dev/null
+++ b/progtest/src/TestCase.js
@@ -0,0 +1,103 @@
+/**
+ * Test case
+ *
+ * Copyright (C) 2018 R-T Specialty, LLC.
+ *
+ * This file is part of TAME.
+ *
+ * TAME is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+"use strict";
+
+const { Class } = require( 'easejs' );
+
+
+module.exports = Class( 'TestCase',
+{
+ /**
+ * Test case data
+ *
+ * @type {Object} test case data
+ */
+ 'private _caseData': {},
+
+ get description()
+ {
+ return this._caseData.description || "";
+ },
+
+ get data()
+ {
+ return this._caseData.data || {};
+ },
+
+ get expect()
+ {
+ return this._caseData.expect || {};
+ },
+
+
+ constructor( case_data )
+ {
+ this._caseData = case_data;
+ },
+
+
+ 'public mapEachValue'( callback )
+ {
+ const [ new_data, new_expect ] = [ this.data, this.expect ].map( src =>
+ {
+ const new_src = {};
+
+ Object.keys( src ).forEach(
+ key => new_src[ key ] = this._visitDeep(
+ src[ key ],
+ callback
+ )
+ );
+
+ return new_src;
+ } );
+
+ return module.exports( {
+ description: this.description,
+ data: new_data,
+ expect: new_expect,
+ } );
+ },
+
+
+ /**
+ * Recursively resolve constants
+ *
+ * Only scalars and arrays are supported
+ *
+ * @param {number|Array} input scalar or array of inputs
+ * @param {Object} consts constant mapping
+ *
+ * @return {number|Array} resolved value(s)
+ */
+ 'private _visitDeep'( val, callback )
+ {
+ if ( Array.isArray( val ) )
+ {
+ return val.map(
+ x => this._visitDeep( x, callback )
+ );
+ }
+
+ return callback( val );
+ }
+} );
diff --git a/progtest/src/TestRunner.js b/progtest/src/TestRunner.js
new file mode 100644
index 0000000..9269018
--- /dev/null
+++ b/progtest/src/TestRunner.js
@@ -0,0 +1,153 @@
+/**
+ * Test case runner
+ *
+ * Copyright (C) 2018 R-T Specialty, LLC.
+ *
+ * This file is part of TAME.
+ *
+ * TAME is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+"use strict";
+
+const { Class } = require( 'easejs' );
+
+
+/**
+ * Run test cases and report results
+ */
+module.exports = Class( 'TestRunner',
+{
+ /**
+ * SUT
+ *
+ * @type {Program}
+ */
+ 'private _program': null,
+
+ /**
+ * Test reporter
+ *
+ * @type {TestReporter}
+ */
+ 'private _reporter': null,
+
+
+ /**
+ * Initialize runner for program PROGRAM
+ *
+ * @param {TestReporter} reporter test reporter
+ * @param {Program} program SUT
+ */
+ constructor( reporter, program )
+ {
+ // primitive check to guess whether this might be a program
+ if ( typeof program.rater !== 'function' )
+ {
+ throw TypeError( "program#rater is not a function" );
+ }
+
+ this._reporter = reporter;
+ this._program = program;
+ },
+
+
+ /**
+ * Run set of test cases
+ *
+ * @param {Array<TestCase>} dfns array of TestCases
+ *
+ * @return {Array<Object<desc,i,total,failures>>} results
+ */
+ 'public runTests'( dfns )
+ {
+ const total = dfns.length;
+
+ this._reporter.preRun( total );
+
+ const results = dfns.map(
+ ( test, i ) => this._runTest( test, i, total )
+ );
+
+ this._reporter.done( results );
+
+ return results;
+ },
+
+
+ /**
+ * Run individual test case
+ *
+ * @param {Object<TestCase>} _ source test case
+ * @param {number} test_i test index
+ * @param {number} total total number of tests
+ *
+ * @return {Object<desc,i,total,failures>} test results
+ */
+ 'private _runTest'( { description: desc, data, expect }, test_i, total )
+ {
+ // no input map---#rate uses params directly
+ const result = this._program.rater( data ).vars;
+
+ const cmp = Object.keys( expect ).map(
+ field => [
+ field,
+ this._deepCompare( expect[ field ], result[ field ] )
+ ]
+ );
+
+ const failures = cmp.filter( ( [ , ok ] ) => !ok )
+ .map( ( [ field ] ) => ( {
+ field: field,
+ expect: expect[ field ],
+ result: result[ field ],
+ } ) );
+
+ const succeeded = cmp.length - failures.length;
+
+ const result_data = {
+ desc: desc,
+ i: test_i,
+ total: cmp.length,
+ failures: failures,
+ };
+
+ this._reporter.testCaseResult( result_data, total );
+
+ return result_data;
+ },
+
+
+ /**
+ * Recursively compare values (scalar, array)
+ *
+ * @param {number|Array<number>} x first value
+ * @param {number|Array<number>} y second value
+ *
+ * @return {boolean} whether X deeply equals Y
+ */
+ 'private _deepCompare'( x, y )
+ {
+ // vector/matrix/etc
+ if ( Array.isArray( x ) )
+ {
+ return Array.isArray( y )
+ && ( x.length === y.length )
+ && x.every( ( xval, i ) => xval === y[ i ] );
+ }
+
+ // scalar
+ return x === y;
+ },
+} );
diff --git a/progtest/src/reader/ConstResolver.js b/progtest/src/reader/ConstResolver.js
new file mode 100644
index 0000000..b8861f6
--- /dev/null
+++ b/progtest/src/reader/ConstResolver.js
@@ -0,0 +1,110 @@
+/**
+ * Constant resolver for test case reader
+ *
+ * Copyright (C) 2018 R-T Specialty, LLC.
+ *
+ * This file is part of TAME.
+ *
+ * TAME is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+"use strict";
+
+
+const { Trait } = require( 'easejs' );
+const TestReader = require( './TestReader' );
+
+
+/**
+ * Resolve program constants by replacing them with their numeric value
+ *
+ * The result is a loaded YAML file that contains only numeric input. All
+ * non-numeric input is interpreted as a constant.
+ *
+ * Any non-numeric value is considered to be a constant, so it is important
+ * to perform all other data transformations before applying constant
+ * resolution.
+ */
+module.exports = Trait( 'ConstResolver' )
+ .implement( TestReader )
+ .extend(
+{
+ /**
+ * Program from which to load constants
+ *
+ * @type {Program}
+ */
+ 'private _program': null,
+
+
+ /**
+ * Initialize with program from which to load constants
+ *
+ * @param {Program} program source program
+ */
+ __mixin( program )
+ {
+ this._program = program;
+ },
+
+
+ /**
+ * Load test cases and resolve constants
+ *
+ * @param {*} src data source
+ *
+ * @return {Array<TestCase>} array of test cases
+ */
+ 'abstract override public loadCases'( yaml )
+ {
+ const data = this.__super( yaml );
+ const { consts } = this._program.rater;
+
+ return data.map(
+ testcase => testcase.mapEachValue(
+ value => this._resolve( value, consts )
+ )
+ );
+ },
+
+
+ /**
+ * Resolve constant
+ *
+ * If the constant is not known (via CONSTS), an error is thrown.
+ *
+ * @param {number|Array} input scalar or array of inputs
+ * @param {Object} consts constant mapping
+ *
+ * @throws {Error} if constant is unknown in CONSTS
+ *
+ * @return {number|Array} resolved value(s)
+ */
+ 'private _resolve'( input, consts )
+ {
+ // already a number, return as-is
+ if ( !isNaN( +input ) )
+ {
+ return input;
+ }
+
+ const result = consts[ input ];
+ if ( result === undefined )
+ {
+ throw Error( `unknown constant: ${input}` );
+ }
+
+ return result;
+ }
+} );
diff --git a/progtest/src/reader/DateResolver.js b/progtest/src/reader/DateResolver.js
new file mode 100644
index 0000000..319254e
--- /dev/null
+++ b/progtest/src/reader/DateResolver.js
@@ -0,0 +1,61 @@
+/**
+ * Date resolver for test case reader
+ *
+ * Copyright (C) 2018 R-T Specialty, LLC.
+ *
+ * This file is part of TAME.
+ *
+ * TAME is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+"use strict";
+
+
+const { Trait } = require( 'easejs' );
+const TestReader = require( './TestReader' );
+
+
+/**
+ * Resolve dates of the format MM/DD/YYYY to Unix timestamps
+ *
+ * This allows easily readable dates to be included in test cases without
+ * having to worry about Unix timestamps. For higher precision, Unix
+ * timestamps must be used.
+ */
+module.exports = Trait( 'DateResolver' )
+ .implement( TestReader )
+ .extend(
+{
+ /**
+ * Load test cases and resolve dates
+ *
+ * Dates will be replaced with Unix timestamps.
+ *
+ * @param {*} src data source
+ *
+ * @return {Array<TestCase>} array of test cases
+ */
+ 'abstract override public loadCases'( src )
+ {
+ const data = this.__super( src );
+
+ return data.map(
+ testcase => testcase.mapEachValue(
+ value => ( /^\d{2}\/\d{2}\/\d{4}$/.test( value ) )
+ ? ( ( new Date( value ) ).getTime() / 1000 )
+ : value
+ )
+ );
+ }
+} );
diff --git a/progtest/src/reader/TestReader.js b/progtest/src/reader/TestReader.js
new file mode 100644
index 0000000..717b12c
--- /dev/null
+++ b/progtest/src/reader/TestReader.js
@@ -0,0 +1,40 @@
+/**
+ * Test case reader
+ *
+ * Copyright (C) 2018 R-T Specialty, LLC.
+ *
+ * This file is part of TAME.
+ *
+ * TAME is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+"use strict";
+
+const { Interface } = require( 'easejs' );
+
+
+module.exports = Interface( 'TestReader',
+{
+ /**
+ * Load test cases from an implementation-defined data source SRC
+ *
+ * The produced object will be an array of cases, each containing a
+ * `description`, `data`, and `expect`.
+ *
+ * @param {*} src data source
+ *
+ * @return {Array<TestCase>} array of test cases
+ */
+ 'public loadCases': [ 'src' ],
+} );
diff --git a/progtest/src/reader/YamlTestReader.js b/progtest/src/reader/YamlTestReader.js
new file mode 100644
index 0000000..a28c1f3
--- /dev/null
+++ b/progtest/src/reader/YamlTestReader.js
@@ -0,0 +1,79 @@
+/**
+ * YAML test case reader
+ *
+ * Copyright (C) 2018 R-T Specialty, LLC.
+ *
+ * This file is part of TAME.
+ *
+ * TAME is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+"use strict";
+
+const { Class } = require( 'easejs' );
+const TestReader = require( './TestReader' );
+
+
+module.exports = Class( 'YamlTestReader' )
+ .implement( TestReader )
+ .extend(
+{
+ /**
+ * YAML parser
+ *
+ * @type {Object}
+ */
+ 'private _yamlParser': null,
+
+ /**
+ * TestCase constructor
+ *
+ * @type {function(Object)}
+ */
+ 'private _createTestCase': null,
+
+
+ /**
+ * Initialize with YAML parser
+ *
+ * The parser must conform to the API of `js-yaml`.
+ *
+ * @param {Object} yaml_parser YAML parser
+ * @param {function(Object)} test_case_ctor TestCase constructor
+ */
+ constructor( yaml_parser, test_case_ctor )
+ {
+ this._yamlParser = yaml_parser;
+ this._createTestCase = test_case_ctor;
+ },
+
+
+ /**
+ * Load test cases from a YAML string
+ *
+ * The produced object will be an array of cases, each containing a
+ * `description`, `data`, and `expect`.
+ *
+ * @param {string} src source YAML
+ *
+ * @return {Array<TestCase>} array of test cases
+ */
+ 'virtual public loadCases'( yaml )
+ {
+ const data = this._yamlParser.safeLoad( yaml )
+ .map( this._createTestCase );
+
+ return data;
+ },
+} );
diff --git a/progtest/src/reporter/ConsoleTestReporter.js b/progtest/src/reporter/ConsoleTestReporter.js
new file mode 100644
index 0000000..2a16e03
--- /dev/null
+++ b/progtest/src/reporter/ConsoleTestReporter.js
@@ -0,0 +1,183 @@
+/**
+ * Console test reporter
+ *
+ * Copyright (C) 2018 R-T Specialty, LLC.
+ *
+ * This file is part of TAME.
+ *
+ * TAME is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+"use strict";
+
+const { Class } = require( 'easejs' );
+
+
+/**
+ * Real-time reporting of test cases to the console
+ *
+ * Test cases will be output in a block of dots (success) or 'F's (failure),
+ * in a style similar to PHPUnit. If failures occur, they will be output to
+ * in more detail after all tests have run.
+ */
+module.exports = Class( 'ConsoleTestReporter',
+{
+ /**
+ * Standard out
+ *
+ * @type {Object} standard out
+ */
+ 'private _stdout': null,
+
+
+ /**
+ * Initialize reporter with target console
+ *
+ * STDOUT must follow Node.js' API.
+ *
+ * @param {Object} stdout standard out
+ */
+ constructor( stdout )
+ {
+ this._stdout = stdout;
+ },
+
+
+ /**
+ * Invoked before tests are run
+ *
+ * The only information provided here is the number of test cases to be
+ * run, which can be used to produce a progress indicator.
+ *
+ * @param {number} total number of test cases
+ *
+ * @return {undefined}
+ */
+ 'public preRun'( total )
+ {
+ // this reporter does nothing with this method
+ },
+
+
+ /**
+ * Invoked for each test case immediately after it has been run
+ *
+ * For the format of RESULT, see TestRunner.
+ *
+ * @param {Object} result test case result
+ *
+ * @return {undefined}
+ */
+ 'public testCaseResult'( result, total )
+ {
+ const { i, failures } = result;
+
+ const ind = ( failures.length === 0 )
+ ? '.'
+ : 'F';
+
+ const sep = ( i % 50 === 49 )
+ ? ` ${i+1}/${total}\n`
+ : '';
+
+ this._stdout.write( ind + sep );
+ },
+
+
+ /**
+ * Invoked after all test cases have been run
+ *
+ * RESULTS is an array containing each result that was previously
+ * reported to `#testCaseResult`.
+ *
+ * A final line will be output, preceded by an empty line, summarizing
+ * the number of tests, assertions, and failures for each.
+ *
+ * @param {Array<Object>} results all test results
+ *
+ * @return {undefined}
+ */
+ 'public done'( results )
+ {
+ this._outputFailureReport( results );
+ this._outputSummary( results );
+ },
+
+
+ /**
+ * For each failure, output each expected and resulting value
+ *
+ * Failures are prefixed with a 1-indexed number.
+ *
+ * @param {Object} result test case result}
+ *
+ * @return {undefined}
+ */
+ 'private _outputFailureReport'( results )
+ {
+ const report = results
+ .filter( ( { failures } ) => failures.length > 0 )
+ .map( this._reportTestFailure.bind( this ) )
+ .join( '\n' )
+
+ this._stdout.write( "\n\n" + report );
+ },
+
+
+ /**
+ * Generate report for test case failure
+ *
+ * @param {Object<i,desc,failures>} _ test case result data
+ *
+ * @return {string} report
+ */
+ 'private _reportTestFailure'( { i, desc, failures } )
+ {
+ return `[#${i+1}] ${desc}\n` +
+ failures.map( ( { field, expect, result } ) =>
+ ` ${field}:\n` +
+ ` expected: ` + JSON.stringify( expect ) + `\n` +
+ ` result: ` + JSON.stringify( result ) + `\n`
+ ).join( '' );
+ },
+
+
+ /**
+ * Output a line, preceded by an empty line, summarizing the number of
+ * tests, assertions, and failures for each
+ *
+ * @param {Array<Object>} results all test results
+ *
+ * @return {undefined}
+ */
+ 'private _outputSummary'( results )
+ {
+ const [ failed, afailed, acount ] = results.reduce(
+ ( [ failed, afailed, acount ], { failures, total } ) =>
+ [
+ ( failed + +( failures.length > 0 ) ),
+ ( afailed + failures.length ),
+ ( acount + total )
+ ],
+ [ 0, 0, 0 ]
+ );
+
+ const test_total = results.length;
+
+ this._stdout.write(
+ `\n${test_total} tests, ${failed} failed (` +
+ `${acount} assertions, ${afailed} failures)`
+ );
+ },
+} );
diff --git a/progtest/src/reporter/NullTestReporter.js b/progtest/src/reporter/NullTestReporter.js
new file mode 100644
index 0000000..d29fb05
--- /dev/null
+++ b/progtest/src/reporter/NullTestReporter.js
@@ -0,0 +1,79 @@
+/**
+ * Reporter that does nothing
+ *
+ * Copyright (C) 2018 R-T Specialty, LLC.
+ *
+ * This file is part of TAME.
+ *
+ * TAME is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+"use strict";
+
+const { Class } = require( 'easejs' );
+
+
+/**
+ * Reporter that does nothing
+ *
+ * This is useful if you want a fully background process.
+ */
+module.exports = Class( 'NullTestReporter',
+{
+ /**
+ * Invoked before tests are run
+ *
+ * The only information provided here is the number of test cases to be
+ * run, which can be used to produce a progress indicator.
+ *
+ * @param {number} total number of test cases
+ *
+ * @return {undefined}
+ */
+ 'public preRun'( total )
+ {
+ // this reporter does nothing with this method
+ },
+
+
+ /**
+ * Invoked for each test case immediately after it has been run
+ *
+ * For the format of RESULT, see TestRunner.
+ *
+ * @param {Object} result test case result
+ *
+ * @return {undefined}
+ */
+ 'public testCaseResult'( result, total )
+ {
+ // this reporter does nothing with this method
+ },
+
+
+ /**
+ * Invoked after all test cases have been run
+ *
+ * RESULTS is an array containing each result that was previously
+ * reported to `#testCaseResult`.
+ *
+ * @param {Array<Object>} results all test results
+ *
+ * @return {undefined}
+ */
+ 'public done'( results )
+ {
+ // this reporter does nothing with this method
+ },
+} );
diff --git a/progtest/src/reporter/TestReporter.js b/progtest/src/reporter/TestReporter.js
new file mode 100644
index 0000000..ee7ce8a
--- /dev/null
+++ b/progtest/src/reporter/TestReporter.js
@@ -0,0 +1,68 @@
+/**
+ * Test reporter
+ *
+ * Copyright (C) 2018 R-T Specialty, LLC.
+ *
+ * This file is part of TAME.
+ *
+ * TAME is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+"use strict";
+
+const { Interface } = require( 'easejs' );
+
+
+/**
+ * Real-time reporting of test cases
+ */
+module.exports = Interface( 'TestReporter',
+{
+ /**
+ * Invoked before tests are run
+ *
+ * The only information provided here is the number of test cases to be
+ * run, which can be used to produce a progress indicator.
+ *
+ * @param {number} total number of test cases
+ *
+ * @return {undefined}
+ */
+ 'public preRun': [ 'total' ],
+
+
+ /**
+ * Invoked for each test case immediately after it has been run
+ *
+ * For the format of RESULT, see TestRunner.
+ *
+ * @param {Object} result test case result
+ *
+ * @return {undefined}
+ */
+ 'public testCaseResult': [ 'result' ],
+
+
+ /**
+ * Invoked after all test cases have been run
+ *
+ * RESULTS is an array containing each result that was previously
+ * reported to `#testCaseResult`.
+ *
+ * @param {Array<Object>} results all test results
+ *
+ * @return {undefined}
+ */
+ 'public done': [ 'results' ],
+} );