From 65e35b6dd26d06edc94968bf70b598294976ca48 Mon Sep 17 00:00:00 2001 From: Rob Wu Date: Thu, 9 Jul 2015 11:03:34 +0200 Subject: [PATCH] Fix preprocessor: nesting, error & tests Features / bug fixes in the preprocessor: - Add word boundary after regex for preprocessor token matching. Previously, when you mistakenly used "#ifdef" instead of "#if", the line would be parsed as a preprocessor directive (because "#ifdef" starts with "#if"), but without condition (because "def" does not start with a space). Consequently, the condition would always be false and anything between "#ifdef" and "#endif" would not be included. - Add validation and error reporting everywhere, to aid debugging. - Support nested comments (by accounting for the whole stack of conditions, instead of only the current one). - Add #elif preprocessor command. Could be used as follows: //#if !FEATURE_ENABLED //#error FEATURE_ENABLED must be set //#endif - Add #error preprocessor command. - Add end-of-line word boundary after "-->" in the comment trimmer. Otherwise the pattern would also match "-->" in the middle of a line, and incorrectly convert something like "while(i-->0)" to "while(i0)". Code health: - Add unit tests for the preprocessor (run external/builder/test.js). - Fix broken link to MDN (resolved to DXR). - Refactor to use STATE_* names instead of magic numbers (the original meaning of the numbers is preserved, with one exception). - State 3 has been split in two states, to distinguish between being in an #if and #else. This is needed to ensure that #else cannot be started without an #if. --- external/builder/builder.js | 124 ++++++++++++++---- .../fixtures/confusing-comment-expected.js | 4 + .../builder/fixtures/confusing-comment.js | 6 + external/builder/fixtures/elif-expected.js | 1 + external/builder/fixtures/elif.js | 4 + external/builder/fixtures/else-expected.js | 1 + external/builder/fixtures/else.js | 3 + external/builder/fixtures/error-expected.js | 1 + .../builder/fixtures/error-false-expected.js | 2 + external/builder/fixtures/error-false.js | 5 + external/builder/fixtures/error.js | 5 + .../builder/fixtures/expand-expected.html | 1 + external/builder/fixtures/expand.html | 1 + .../builder/fixtures/if-empty-expected.js | 1 + external/builder/fixtures/if-empty.js | 6 + .../if-false-elif-false-else-expected.js | 2 + .../fixtures/if-false-elif-false-else.js | 8 ++ .../if-false-elif-true-else-expected.js | 2 + .../fixtures/if-false-elif-true-else.js | 8 ++ .../fixtures/if-false-else-expected.js | 2 + external/builder/fixtures/if-false-else.js | 6 + .../builder/fixtures/if-nested-expected.js | 6 + external/builder/fixtures/if-nested.js | 19 +++ .../builder/fixtures/if-true-else-expected.js | 2 + external/builder/fixtures/if-true-else.js | 6 + .../builder/fixtures/if-unclosed-expected.js | 1 + external/builder/fixtures/if-unclosed.js | 3 + .../builder/fixtures/include-expected.html | 5 + .../include-non-existent-expected.html | 1 + .../fixtures/include-non-existent.html | 2 + external/builder/fixtures/include.html | 5 + .../builder/fixtures/js-comment-expected.js | 4 + external/builder/fixtures/js-comment.js | 6 + .../fixtures/undefined-define-expected.js | 5 + external/builder/fixtures/undefined-define.js | 6 + .../fixtures/unsupported-ifdef-expected.js | 1 + .../builder/fixtures/unsupported-ifdef.js | 5 + external/builder/test.js | 54 ++++++++ 38 files changed, 299 insertions(+), 25 deletions(-) create mode 100644 external/builder/fixtures/confusing-comment-expected.js create mode 100644 external/builder/fixtures/confusing-comment.js create mode 100644 external/builder/fixtures/elif-expected.js create mode 100644 external/builder/fixtures/elif.js create mode 100644 external/builder/fixtures/else-expected.js create mode 100644 external/builder/fixtures/else.js create mode 100644 external/builder/fixtures/error-expected.js create mode 100644 external/builder/fixtures/error-false-expected.js create mode 100644 external/builder/fixtures/error-false.js create mode 100644 external/builder/fixtures/error.js create mode 100644 external/builder/fixtures/expand-expected.html create mode 100644 external/builder/fixtures/expand.html create mode 100644 external/builder/fixtures/if-empty-expected.js create mode 100644 external/builder/fixtures/if-empty.js create mode 100644 external/builder/fixtures/if-false-elif-false-else-expected.js create mode 100644 external/builder/fixtures/if-false-elif-false-else.js create mode 100644 external/builder/fixtures/if-false-elif-true-else-expected.js create mode 100644 external/builder/fixtures/if-false-elif-true-else.js create mode 100644 external/builder/fixtures/if-false-else-expected.js create mode 100644 external/builder/fixtures/if-false-else.js create mode 100644 external/builder/fixtures/if-nested-expected.js create mode 100644 external/builder/fixtures/if-nested.js create mode 100644 external/builder/fixtures/if-true-else-expected.js create mode 100644 external/builder/fixtures/if-true-else.js create mode 100644 external/builder/fixtures/if-unclosed-expected.js create mode 100644 external/builder/fixtures/if-unclosed.js create mode 100644 external/builder/fixtures/include-expected.html create mode 100644 external/builder/fixtures/include-non-existent-expected.html create mode 100644 external/builder/fixtures/include-non-existent.html create mode 100644 external/builder/fixtures/include.html create mode 100644 external/builder/fixtures/js-comment-expected.js create mode 100644 external/builder/fixtures/js-comment.js create mode 100644 external/builder/fixtures/undefined-define-expected.js create mode 100644 external/builder/fixtures/undefined-define.js create mode 100644 external/builder/fixtures/unsupported-ifdef-expected.js create mode 100644 external/builder/fixtures/unsupported-ifdef.js create mode 100644 external/builder/test.js diff --git a/external/builder/builder.js b/external/builder/builder.js index e85022348f9d78..cee1700cdde22e 100644 --- a/external/builder/builder.js +++ b/external/builder/builder.js @@ -10,16 +10,32 @@ var fs = require('fs'), vm = require('vm'); /** - * A simple preprocessor that is based on the firefox preprocessor - * see (https://developer.mozilla.org/en/Build/Text_Preprocessor). The main - * difference is that this supports a subset of the commands and it supports - * preproccesor commands in html style comments. - * Currently Supported commands: + * A simple preprocessor that is based on the Firefox preprocessor + * (https://dxr.mozilla.org/mozilla-central/source/build/docs/preprocessor.rst). + * The main difference is that this supports a subset of the commands and it + * supports preprocessor commands in HTML-style comments. + * + * Currently supported commands: * - if + * - elif * - else * - endif * - include * - expand + * - error + * + * Every #if must be closed with an #endif. Nested conditions are supported. + * + * Within an #if or #else block, one level of comment tokens is stripped. This + * allows us to write code that can run even without preprocessing. For example: + * + * //#if SOME_RARE_CONDITION + * // // Decrement by one + * // --i; + * //#else + * // // Increment by one. + * ++i; + * //#endif */ function preprocess(inFilename, outFilename, defines) { // TODO make this really read line by line. @@ -37,10 +53,28 @@ function preprocess(inFilename, outFilename, defines) { function(line) { out += line + '\n'; }); + function evaluateCondition(code) { + if (!code || !code.trim()) { + throw new Error('No JavaScript expression given at ' + loc()); + } + try { + return vm.runInNewContext(code, defines); + } catch (e) { + throw new Error('Could not evaluate "' + code + '" at ' + loc() + '\n' + + e.name + ': ' + e.message); + } + } function include(file) { var realPath = fs.realpathSync(inFilename); var dir = path.dirname(realPath); - preprocess(path.join(dir, file), writeLine, defines); + try { + preprocess(path.join(dir, file), writeLine, defines); + } catch (e) { + if (e.code === 'ENOENT') { + throw new Error('Failed to include "' + file + '" at ' + loc()); + } + throw e; // Some other error + } } function expand(line) { line = line.replace(/__[\w]+__/g, function(variable) { @@ -53,52 +87,92 @@ function preprocess(inFilename, outFilename, defines) { writeLine(line); } - var s, state = 0, stack = []; + // not inside if or else (process lines) + var STATE_NONE = 0; + // inside if, condition false (ignore until #else or #endif) + var STATE_IF_FALSE = 1; + // inside else, #if was false, so #else is true (process lines until #endif) + var STATE_ELSE_TRUE = 2; + // inside if, condition true (process lines until #else or #endif) + var STATE_IF_TRUE = 3; + // inside else, #if was true, so #else is false (ignore lines until #endif) + var STATE_ELSE_FALSE = 4; + + var line; + var state = STATE_NONE; + var stack = []; var control = - /^(?:\/\/|)?$)?/; + /* jshint -W101 */ + /^(?:\/\/|)?$)?/; + /* jshint +W101 */ var lineNumber = 0; - while ((s = readLine()) !== null) { + var loc = function() { + return fs.realpathSync(inFilename) + ':' + lineNumber; + }; + while ((line = readLine()) !== null) { ++lineNumber; - var m = control.exec(s); + var m = control.exec(line); if (m) { switch (m[1]) { case 'if': stack.push(state); - try { - state = vm.runInNewContext(m[2], defines) ? 3 : 1; - } catch (e) { - console.error('Could not evalute line \'' + m[2] + '\' at ' + - fs.realpathSync(inFilename) + ':' + lineNumber); - throw e; + state = evaluateCondition(m[2]) ? STATE_IF_TRUE : STATE_IF_FALSE; + break; + case 'elif': + if (state === STATE_IF_TRUE) { + state = STATE_ELSE_FALSE; + } else if (state === STATE_IF_FALSE) { + state = evaluateCondition(m[2]) ? STATE_IF_TRUE : STATE_IF_FALSE; + } else if (state === STATE_ELSE_TRUE || state === STATE_ELSE_FALSE) { + throw new Error('Found #elif after #else at ' + loc()); + } else if (state !== STATE_IF_FALSE) { + throw new Error('Found #elif without matching #if at ' + loc()); } break; case 'else': - state = state === 1 ? 3 : 2; + if (state === STATE_IF_TRUE) { + state = STATE_ELSE_FALSE; + } else if (state === STATE_IF_FALSE) { + state = STATE_ELSE_TRUE; + } else { + throw new Error('Found #else without matching #if at ' + loc()); + } break; case 'endif': + if (state === STATE_NONE) { + throw new Error('Found #endif without #if at ' + loc()); + } state = stack.pop(); break; case 'expand': - if (state === 0 || state === 3) { + if (state !== STATE_IF_FALSE && state !== STATE_ELSE_FALSE) { expand(m[2]); } break; case 'include': - if (state === 0 || state === 3) { + if (state !== STATE_IF_FALSE && state !== STATE_ELSE_FALSE) { include(m[2]); } break; + case 'error': + if (state !== STATE_IF_FALSE && state !== STATE_ELSE_FALSE) { + throw new Error('Found #error ' + m[2] + ' at ' + loc()); + } + break; } } else { - if (state === 0) { - writeLine(s); - } else if (state === 3) { - writeLine(s.replace(/^\/\/|^/g, ' ')); + if (state === STATE_NONE) { + writeLine(line); + } else if ((state === STATE_IF_TRUE || state === STATE_ELSE_TRUE) && + stack.indexOf(STATE_IF_FALSE) === -1 && + stack.indexOf(STATE_ELSE_FALSE) === -1) { + writeLine(line.replace(/^\/\/|^$/g, ' ')); } } } - if (state !== 0 || stack.length !== 0) { - throw new Error('Missing endif in preprocessor.'); + if (state !== STATE_NONE || stack.length !== 0) { + throw new Error('Missing #endif in preprocessor for ' + + fs.realpathSync(inFilename)); } if (typeof outFilename !== 'function') { fs.writeFileSync(outFilename, out); diff --git a/external/builder/fixtures/confusing-comment-expected.js b/external/builder/fixtures/confusing-comment-expected.js new file mode 100644 index 00000000000000..018f08436a6652 --- /dev/null +++ b/external/builder/fixtures/confusing-comment-expected.js @@ -0,0 +1,4 @@ +'use strict'; +var i = 0; +while(i-->0) { +} diff --git a/external/builder/fixtures/confusing-comment.js b/external/builder/fixtures/confusing-comment.js new file mode 100644 index 00000000000000..ac626b90633e0c --- /dev/null +++ b/external/builder/fixtures/confusing-comment.js @@ -0,0 +1,6 @@ +'use strict'; +//#if TRUE +var i = 0; +while(i-->0) { +} +//#endif diff --git a/external/builder/fixtures/elif-expected.js b/external/builder/fixtures/elif-expected.js new file mode 100644 index 00000000000000..c2ef779da866b3 --- /dev/null +++ b/external/builder/fixtures/elif-expected.js @@ -0,0 +1 @@ +//Error: Found #elif without matching #if at __filename:2 diff --git a/external/builder/fixtures/elif.js b/external/builder/fixtures/elif.js new file mode 100644 index 00000000000000..3900de2bc2983c --- /dev/null +++ b/external/builder/fixtures/elif.js @@ -0,0 +1,4 @@ +'use strict'; +//#elif TRUE +var a; +//#endif diff --git a/external/builder/fixtures/else-expected.js b/external/builder/fixtures/else-expected.js new file mode 100644 index 00000000000000..a623ae5d390335 --- /dev/null +++ b/external/builder/fixtures/else-expected.js @@ -0,0 +1 @@ +//Error: Found #else without matching #if at __filename:2 diff --git a/external/builder/fixtures/else.js b/external/builder/fixtures/else.js new file mode 100644 index 00000000000000..f28b328d8b7eea --- /dev/null +++ b/external/builder/fixtures/else.js @@ -0,0 +1,3 @@ +'use strict'; +//#else +//#endif diff --git a/external/builder/fixtures/error-expected.js b/external/builder/fixtures/error-expected.js new file mode 100644 index 00000000000000..7e061ad1f19465 --- /dev/null +++ b/external/builder/fixtures/error-expected.js @@ -0,0 +1 @@ +//Error: Found #error "Some Error" at __filename:3 diff --git a/external/builder/fixtures/error-false-expected.js b/external/builder/fixtures/error-false-expected.js new file mode 100644 index 00000000000000..0220a0031b0e74 --- /dev/null +++ b/external/builder/fixtures/error-false-expected.js @@ -0,0 +1,2 @@ +'use strict'; +var a; diff --git a/external/builder/fixtures/error-false.js b/external/builder/fixtures/error-false.js new file mode 100644 index 00000000000000..e5b8153dad018c --- /dev/null +++ b/external/builder/fixtures/error-false.js @@ -0,0 +1,5 @@ +'use strict'; +//#if FALSE +//#error "Some Error" +//#endif +var a; diff --git a/external/builder/fixtures/error.js b/external/builder/fixtures/error.js new file mode 100644 index 00000000000000..facd0e86e0dde6 --- /dev/null +++ b/external/builder/fixtures/error.js @@ -0,0 +1,5 @@ +'use strict'; +//#if TRUE +//#error "Some Error" +//#endif +var b; diff --git a/external/builder/fixtures/expand-expected.html b/external/builder/fixtures/expand-expected.html new file mode 100644 index 00000000000000..89cc081779f002 --- /dev/null +++ b/external/builder/fixtures/expand-expected.html @@ -0,0 +1 @@ +prefixtruesuffix diff --git a/external/builder/fixtures/expand.html b/external/builder/fixtures/expand.html new file mode 100644 index 00000000000000..a2247ea3d4330e --- /dev/null +++ b/external/builder/fixtures/expand.html @@ -0,0 +1 @@ + diff --git a/external/builder/fixtures/if-empty-expected.js b/external/builder/fixtures/if-empty-expected.js new file mode 100644 index 00000000000000..5dd4f66a2135e6 --- /dev/null +++ b/external/builder/fixtures/if-empty-expected.js @@ -0,0 +1 @@ +//Error: No JavaScript expression given at __filename:2 diff --git a/external/builder/fixtures/if-empty.js b/external/builder/fixtures/if-empty.js new file mode 100644 index 00000000000000..81ad761e5c0d5b --- /dev/null +++ b/external/builder/fixtures/if-empty.js @@ -0,0 +1,6 @@ +'use strict'; +//#if +var a; +//#else +var b; +//#endif diff --git a/external/builder/fixtures/if-false-elif-false-else-expected.js b/external/builder/fixtures/if-false-elif-false-else-expected.js new file mode 100644 index 00000000000000..724f9bd9721369 --- /dev/null +++ b/external/builder/fixtures/if-false-elif-false-else-expected.js @@ -0,0 +1,2 @@ +'use strict'; +var c; diff --git a/external/builder/fixtures/if-false-elif-false-else.js b/external/builder/fixtures/if-false-elif-false-else.js new file mode 100644 index 00000000000000..e3f6aca936a4b9 --- /dev/null +++ b/external/builder/fixtures/if-false-elif-false-else.js @@ -0,0 +1,8 @@ +'use strict'; +//#if FALSE +var a; +//#elif FALSE +var b; +//#else +var c; +//#endif diff --git a/external/builder/fixtures/if-false-elif-true-else-expected.js b/external/builder/fixtures/if-false-elif-true-else-expected.js new file mode 100644 index 00000000000000..fe04f495869587 --- /dev/null +++ b/external/builder/fixtures/if-false-elif-true-else-expected.js @@ -0,0 +1,2 @@ +'use strict'; +var b; diff --git a/external/builder/fixtures/if-false-elif-true-else.js b/external/builder/fixtures/if-false-elif-true-else.js new file mode 100644 index 00000000000000..2a37d2fba4c93a --- /dev/null +++ b/external/builder/fixtures/if-false-elif-true-else.js @@ -0,0 +1,8 @@ +'use strict'; +//#if FALSE +var a; +//#elif TRUE +var b; +//#else +var c; +//#endif diff --git a/external/builder/fixtures/if-false-else-expected.js b/external/builder/fixtures/if-false-else-expected.js new file mode 100644 index 00000000000000..fe04f495869587 --- /dev/null +++ b/external/builder/fixtures/if-false-else-expected.js @@ -0,0 +1,2 @@ +'use strict'; +var b; diff --git a/external/builder/fixtures/if-false-else.js b/external/builder/fixtures/if-false-else.js new file mode 100644 index 00000000000000..831ca99452f255 --- /dev/null +++ b/external/builder/fixtures/if-false-else.js @@ -0,0 +1,6 @@ +'use strict'; +//#if FALSE +var a; +//#else +var b; +//#endif diff --git a/external/builder/fixtures/if-nested-expected.js b/external/builder/fixtures/if-nested-expected.js new file mode 100644 index 00000000000000..7dd77df9ef5a0a --- /dev/null +++ b/external/builder/fixtures/if-nested-expected.js @@ -0,0 +1,6 @@ +'use strict'; +var a; + +var b; + +var d; diff --git a/external/builder/fixtures/if-nested.js b/external/builder/fixtures/if-nested.js new file mode 100644 index 00000000000000..d49180a47aa32a --- /dev/null +++ b/external/builder/fixtures/if-nested.js @@ -0,0 +1,19 @@ +'use strict'; +//#if TRUE +var a; + +//#if TRUE +var b; +//#else +var c; +//#endif + +var d; +//#else +var e; +//#if TRUE +var f; +//#endif + +var g; +//#endif diff --git a/external/builder/fixtures/if-true-else-expected.js b/external/builder/fixtures/if-true-else-expected.js new file mode 100644 index 00000000000000..0220a0031b0e74 --- /dev/null +++ b/external/builder/fixtures/if-true-else-expected.js @@ -0,0 +1,2 @@ +'use strict'; +var a; diff --git a/external/builder/fixtures/if-true-else.js b/external/builder/fixtures/if-true-else.js new file mode 100644 index 00000000000000..c4ed6cb1f7cabb --- /dev/null +++ b/external/builder/fixtures/if-true-else.js @@ -0,0 +1,6 @@ +'use strict'; +//#if TRUE +var a; +//#else +var b; +//#endif diff --git a/external/builder/fixtures/if-unclosed-expected.js b/external/builder/fixtures/if-unclosed-expected.js new file mode 100644 index 00000000000000..0ad021009a75b1 --- /dev/null +++ b/external/builder/fixtures/if-unclosed-expected.js @@ -0,0 +1 @@ +//Error: Missing #endif in preprocessor for __filename diff --git a/external/builder/fixtures/if-unclosed.js b/external/builder/fixtures/if-unclosed.js new file mode 100644 index 00000000000000..b74a4fb55c607e --- /dev/null +++ b/external/builder/fixtures/if-unclosed.js @@ -0,0 +1,3 @@ +'use strict'; +//#if TRUE +var a; diff --git a/external/builder/fixtures/include-expected.html b/external/builder/fixtures/include-expected.html new file mode 100644 index 00000000000000..0c0f019a6a4aed --- /dev/null +++ b/external/builder/fixtures/include-expected.html @@ -0,0 +1,5 @@ + diff --git a/external/builder/fixtures/include-non-existent-expected.html b/external/builder/fixtures/include-non-existent-expected.html new file mode 100644 index 00000000000000..c9a0b5b1c33ed8 --- /dev/null +++ b/external/builder/fixtures/include-non-existent-expected.html @@ -0,0 +1 @@ +//Error: Failed to include "some file that does not exist" at __filename:2 diff --git a/external/builder/fixtures/include-non-existent.html b/external/builder/fixtures/include-non-existent.html new file mode 100644 index 00000000000000..9b892ab324074e --- /dev/null +++ b/external/builder/fixtures/include-non-existent.html @@ -0,0 +1,2 @@ + + diff --git a/external/builder/fixtures/include.html b/external/builder/fixtures/include.html new file mode 100644 index 00000000000000..44352b514b5b65 --- /dev/null +++ b/external/builder/fixtures/include.html @@ -0,0 +1,5 @@ + diff --git a/external/builder/fixtures/js-comment-expected.js b/external/builder/fixtures/js-comment-expected.js new file mode 100644 index 00000000000000..a0b6ada86c2896 --- /dev/null +++ b/external/builder/fixtures/js-comment-expected.js @@ -0,0 +1,4 @@ +'use strict'; + //var a; + var b; +var c; diff --git a/external/builder/fixtures/js-comment.js b/external/builder/fixtures/js-comment.js new file mode 100644 index 00000000000000..361f9e832aebc8 --- /dev/null +++ b/external/builder/fixtures/js-comment.js @@ -0,0 +1,6 @@ +'use strict'; +//#if TRUE +////var a; +//var b; +var c; +//#endif diff --git a/external/builder/fixtures/undefined-define-expected.js b/external/builder/fixtures/undefined-define-expected.js new file mode 100644 index 00000000000000..a85fcf9d3a9c31 --- /dev/null +++ b/external/builder/fixtures/undefined-define-expected.js @@ -0,0 +1,5 @@ +//Error: Could not evaluate "notdefined" at __filename:2 +//ReferenceError: evalmachine.:1 +//notdefined +//^ +//notdefined is not defined diff --git a/external/builder/fixtures/undefined-define.js b/external/builder/fixtures/undefined-define.js new file mode 100644 index 00000000000000..a59f4165e61bf8 --- /dev/null +++ b/external/builder/fixtures/undefined-define.js @@ -0,0 +1,6 @@ +'use strict'; +//#if notdefined +var a; +//#else +var b; +//#endif diff --git a/external/builder/fixtures/unsupported-ifdef-expected.js b/external/builder/fixtures/unsupported-ifdef-expected.js new file mode 100644 index 00000000000000..7b9907b537196d --- /dev/null +++ b/external/builder/fixtures/unsupported-ifdef-expected.js @@ -0,0 +1 @@ +//Error: Found #endif without #if at __filename:4 diff --git a/external/builder/fixtures/unsupported-ifdef.js b/external/builder/fixtures/unsupported-ifdef.js new file mode 100644 index 00000000000000..b3c1d817567cd9 --- /dev/null +++ b/external/builder/fixtures/unsupported-ifdef.js @@ -0,0 +1,5 @@ +'use strict'; +//#ifdef TRUE +//ifdef should not be recognized +//#endif +var a; diff --git a/external/builder/test.js b/external/builder/test.js new file mode 100644 index 00000000000000..274c68f603797e --- /dev/null +++ b/external/builder/test.js @@ -0,0 +1,54 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* jshint node:true */ +/* globals cat, cd, echo, ls */ +'use strict'; + +require('shelljs/make'); + +var builder = require('./builder'); +var fs = require('fs'); +var path = require('path'); + +var errors = 0; + +cd(__dirname); +cd('fixtures'); +ls('*-expected.*').forEach(function(expectationFilename) { + var inFilename = expectationFilename.replace('-expected', ''); + var expectation = cat(expectationFilename).trim() + .replace(/__filename/g, fs.realpathSync(inFilename)); + var outLines = []; + + var outFilename = function(line) { + outLines.push(line); + }; + var defines = { + TRUE: true, + FALSE: false, + }; + var out; + try { + builder.preprocess(inFilename, outFilename, defines); + out = outLines.join('\n').trim(); + } catch (e) { + out = ('Error: ' + e.message).replace(/^/gm, '//'); + } + if (out !== expectation) { + echo('Assertion failed for ' + inFilename); + echo('--------------------------------------------------'); + echo('EXPECTED:'); + echo(expectation); + echo('--------------------------------------------------'); + echo('ACTUAL'); + echo(out); + echo('--------------------------------------------------'); + echo(); + } +}); + +if (errors) { + echo('Found ' + errors + ' expectation failures.'); +} else { + echo('All tests completed without errors.'); +}