From ab5382d28d5e92460c8d8c23eaf0e5a16b8a603c Mon Sep 17 00:00:00 2001 From: Nick Reynolds Date: Thu, 11 Apr 2013 12:18:53 -0400 Subject: [PATCH] Expands `load` function, experiments with loading assertions from tests. - Matches the behavior of `load` function in V8, SpiderMonkey, Rhino, etc. - Converts a few tests to load `tools/assertions.js` rather than define the assertion helpers within the test scripts themselves. Also a bit of hoop-jumping to get compatibility with Node.js which doesn't include/expose V8's `load` but does have `require`. - `load` works well when the script is run as a file, but there are still issues with using it from the REPL. --- src/eval.c | 3 +- src/runtime/runtime.c | 21 ++++++++---- test/test_array_global.js | 19 +---------- test/test_math.js | 14 +------- test/test_number_global.js | 6 ++-- test/tools/assertions.js | 65 ++++++++++++++++++++++++++++++++++++++ test/tools/harness.js | 16 +++++++++- test/tools/runner.js | 0 8 files changed, 100 insertions(+), 44 deletions(-) create mode 100644 test/tools/assertions.js mode change 100755 => 100644 test/tools/runner.js diff --git a/src/eval.c b/src/eval.c index 0ce9bb7..b094ff5 100644 --- a/src/eval.c +++ b/src/eval.c @@ -116,8 +116,7 @@ fh_var_dec_scan(js_val *ctx, ast_node *node) // Declare the variable and possibly convert it to an assignment if (node->type == NODE_VAR_DEC) { fh_var_dec(ctx, node, true); // true to ignore rhs - // Signals that it has been hoisted - node->val = 1; + node->val = 1; // Signals that it has been hoisted } // Recurse sub nodes, will hit the whole tree. diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index 6b24e37..32ad876 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -17,6 +17,7 @@ */ #include +#include #include "runtime.h" #include "lib/console.h" #include "lib/Math.h" @@ -94,16 +95,22 @@ global_gc(js_val *instance, js_args *args, eval_state *state) // load(filename) // -// Custom Flathead function. Execute the given file in the -// global scope. Parsing and evaluation occur at runtime. +// Execute the file with the given name in the global scope. Compatible +// with the function of the same name in V8, SpiderMonkey and Rhino. js_val * global_load(js_val *instance, js_args *args, eval_state *state) { - js_val *name = TO_STR(ARG(args, 0)); - - FILE *file = fopen(name->string.ptr, "r"); - fh_eval_file(file, fh->global, false); - + unsigned int i; + js_val *name; + for (i = 0; i < ARGLEN(args); i++) { + name = TO_STR(ARG(args, i)); + if (access(name->string.ptr, R_OK) == -1) + fh_error(state, E_ERROR, "File could not be read"); + + FILE *file = fopen(name->string.ptr, "r"); + fh_eval_file(file, fh->global, false); + fclose(file); + } return JSUNDEF(); } diff --git a/test/test_array_global.js b/test/test_array_global.js index b42f5c7..3a45bf2 100644 --- a/test/test_array_global.js +++ b/test/test_array_global.js @@ -1,24 +1,7 @@ // test_array_global.js // -------------------- -var assert = console.assert; - -var assertEquals = function(a, b) { - if (a !== b) - console.log(a + ' !== ' + b); - assert(a === b); -}; - -var assertArrayEquals = function(a, b) { - assert(a.length === b.length); - for (var i = 0; i < a.length; i++) { - assert(a[i] === b[i]); - } -}; - -var test = function(name, f) { - f(); -}; +(this.load || require)((this.load ? 'test' : '.') + '/tools/assertions.js'); // ---------------------------------------------------------------------------- diff --git a/test/test_math.js b/test/test_math.js index ad96e9b..f850e75 100644 --- a/test/test_math.js +++ b/test/test_math.js @@ -2,25 +2,13 @@ // ------------ // Tests for the Math global object, some adapted from v8's mjsunit. -var assert = console.assert; - -var assertEquals = function(a, b) { - assert(a === b); -}; - -var assertClose = function(a, b, tolerance) { - assert(a < (b + tolerance)); - assert(a > (b - tolerance)); -}; +(this.load || require)((this.load ? 'test' : '.') + '/tools/assertions.js'); var zero = function() { var x = 0.5; return (function() { return x - 0.5; }()); }; -var test = function(name, f) { - f(); -}; // ---------------------------------------------------------------------------- // Properties diff --git a/test/test_number_global.js b/test/test_number_global.js index 88f68ee..ad2dce6 100644 --- a/test/test_number_global.js +++ b/test/test_number_global.js @@ -38,8 +38,8 @@ test('Properties', function() { assert(Number.MAX_VALUE); assert(Number.MIN_VALUE); assert(isNaN(Number.NaN) && Number.NaN !== undefined); - assert(Number.NEGATIVE_INFINITY === -Infinity); - assert(Number.POSITIVE_INFINITY === Infinity); + assertEquals(-Infinity, Number.NEGATIVE_INFINITY); + assertEquals( Infinity, Number.POSITIVE_INFINITY); }); // TODO: Harmony Number.isNaN & Number.isFinite ? @@ -63,7 +63,7 @@ test('Number#toExponential([fracDigits])', function() { num = 0.000414723723471; // TODO: precision is off (though otherwise correct) - //assert(num.toExponential() === '4.14723723471e-4'); + // assert(num.toExponential() === '4.14723723471e-4'); }); test('Number#toFixed([digits])', function() { diff --git a/test/tools/assertions.js b/test/tools/assertions.js new file mode 100644 index 0000000..729136e --- /dev/null +++ b/test/tools/assertions.js @@ -0,0 +1,65 @@ +// assertions.js +// ------------- + +(function() { + + // If this is a Node.js require, we need to set up the assertion helpers on + // the GLOBAL object for compatability with the other implementations that + // use `load` to evaluate this file. + var root = this.GLOBAL || this; + + root.assert = console.assert; + + root.assertTrue = function(a) { assert(a); }; + + root.assertFalse = function(a) { assert(!a); }; + + root.assertEquals = function(a, b) { + if (a !== b) + console.log(a + ' !== ' + b); + assert(a === b); + }; + + root.assertArrayEquals = function(a, b) { + assert(a.length === b.length); + for (var i = 0; i < a.length; i++) { + assert(a[i] === b[i]); + } + }; + + root.assertRegExpEquals = function(a, b) { + assertEquals(a.source, b.source); + assertEquals(a.global, b.global); + assertEquals(a.multiline, b.multiline); + assertEquals(a.ignoreCase, b.ignoreCase); + }; + + root.assertEmptyRegExp = function(r, cmp) { + // The value of the 'source' prop seems to be implementation-specific + // when the 'pattern' constructor argument is undefined. + // + // Either '(?:)' or '' is acceptable. + if (!(r.source === '(?:)' || r.source === '')) + assert(false); + assertEquals(r.global, cmp.global); + assertEquals(r.multiline, cmp.multiline); + assertEquals(r.ignoreCase, cmp.ignoreCase); + }; + + root.assertBetween = function(x, min, max) { + assert(x > min && x < max); // exclusive + }; + + root.assertClose = function(a, b, tolerance) { + assert(a < (b + tolerance)); + assert(a > (b - tolerance)); + }; + + // For now this is just a way to structure tests, but later it may be used to + // generate reports. + root.test = function(name, f) { + f(); + }; + + root.assertionsLoaded = true; +})(); diff --git a/test/tools/harness.js b/test/tools/harness.js index 98459c9..0dcbf0b 100644 --- a/test/tools/harness.js +++ b/test/tools/harness.js @@ -1,8 +1,12 @@ +// harness.js +// ---------- +// Normalize testing between implementations. + + // Define a basic console for implementations that do not provide one. if (typeof console !== 'object') { console = { - log: function() { var args = Array.prototype.slice.call(arguments); for (var i = 0; i < args.length; i++) @@ -19,6 +23,16 @@ if (typeof console !== 'object') { if (!assertion) throw new Error("assertion failed"); } + }; +} + +// If this isn't available, the assertions.js script should be included as a +// command-line argument. Seems to be present in at least V8, SpiderMonkey and +// Rhino. +if (typeof load !== 'function') { + load = function(scriptName) { + if (!assertionsLoaded) + throw new Error("Missing assertion helpers. Include tools/assertions.js"); }; } diff --git a/test/tools/runner.js b/test/tools/runner.js old mode 100755 new mode 100644