progtest: Check inputs against known params

This aims to prevent needlessly wasted time debugging a non-working test
case, and to avoid writing incorrect test cases that happen to succeed even
though their inputs aren't properly defined.

For example, a common error is to use the name of a bucket field rather than
the name of the param that it maps to.

* progtest/src/TestRunner.js (_verifyKnownParams): New method.
  (_tryRun): Use it.
* progtest/test/TestRunnerTest.js: New test case.  Modify existing test
   cases to define used params.
* progtest/test/_stub/program.js (exports.rater.params): Declare used param.
master
Mike Gerwitz 2019-02-26 11:10:25 -05:00
parent 602a77443f
commit c062cc5a5c
3 changed files with 63 additions and 2 deletions

View File

@ -156,8 +156,8 @@ module.exports = Class( 'TestRunner',
/** /**
* Attempt test case, returning error on failure * Attempt test case, returning error on failure
* *
* If an error is thrown (e.g. terminating classification), it will be * If an error is thrown (e.g. terminating classification or unknown
* returned in place of the results. * input), then it will be returned in place of the results.
* *
* @param {Object} data input data * @param {Object} data input data
* *
@ -168,6 +168,8 @@ module.exports = Class( 'TestRunner',
// no input map---#rate uses params directly // no input map---#rate uses params directly
try try
{ {
this._verifyKnownParams( data );
return this._program.rater( data ).vars; return this._program.rater( data ).vars;
} }
catch( e ) catch( e )
@ -177,6 +179,33 @@ module.exports = Class( 'TestRunner',
}, },
/**
* Verify that all provided inputs match known params
*
* If a given input is not known for the rater for the current program,
* an Error will be thrown with a comma-delimited list of all unknown
* params.
*
* @param {Object} data input data
*
* @return {undefined}
*
* @throws Error when unknown input is found
*/
'private _verifyKnownParams'( data )
{
const params = this._program.rater.params || {};
const unknown = Object.keys( data )
.filter( param => params[ param ] === undefined );
if ( unknown.length > 0 )
{
throw Error( "Unknown params: " + unknown.join( ", " ) );
}
},
/** /**
* Recursively compare values (scalar, array) * Recursively compare values (scalar, array)
* *

View File

@ -41,6 +41,9 @@ describe( "TestRunner", () =>
} }
}; };
// `a' is a known param
program.rater.params = { a: {} };
const test_cases = [ const test_cases = [
{ {
description: "first", description: "first",
@ -118,6 +121,32 @@ describe( "TestRunner", () =>
} ); } );
it( "fails on unknown params", () =>
{
// no params at all are defined
const program = { rater: () => ( { vars: {} } ) };
const bad_test = {
description: 'bad param',
data: {
unknown_param_1: 0,
unknown_param_2: 0,
},
expect: {},
};
return Sut( NullTestReporter(), program )
.runTests( [ bad_test ] )
.then( ( [ result ] ) =>
{
expect( result.failures[ 0 ].result )
.to.contain( 'unknown_param_1' );
expect( result.failures[ 0 ].result )
.to.contain( 'unknown_param_2' );
} );
} );
it( "invokes reporter before, during, and after test cases", done => it( "invokes reporter before, during, and after test cases", done =>
{ {
let pre = false; let pre = false;

View File

@ -23,3 +23,6 @@ exports.rater = data =>
{ {
return { vars: { out: data.in } }; return { vars: { out: data.in } };
}; };
exports.rater.params = { in: {} };