/*
 * Runs one test file from the ES5 test suite test-262
 * Usage: mujs <this-file> [-s ] [-f] [-l file1.js -l ...] suit-root test-file
 * -s: print test source on failure
 * -f: print full paths/stacktraces if possible
 * -l: load a js file after the harness and before the test (to override things)
 *
 * If there are errors, print them and exits with code 1, else exit code is 0.
 *
 * The test suite is at: https://github.com/tc39/test262.git
 * The ES5 suite is at branch "es5-tests"
 *
 * - The test suite throws on any error, possibly with info at ex.message .
 * - Some tests make irreversible changes to global attrubutes, therefore it's
 *   required to run each test file in a new mujs instance.
 */

(function(global) {
	"use strict";

	// clean the global environment
	var mujs = {};

	["gc", "load", "compile", "print", "write", "read", "readline", "quit", "scriptArgs"]
	.forEach(function(a) {
		mujs[a] = global[a];
		delete global[a];
	});

	// restore the original Error.toString behavior - it's being tested too
	Error.prototype.toString = function() {
		return this.name + ': ' + this.message;
	}

	function die_usage(str) {
		if (str)
			mujs.print(str);
		mujs.print("Usage: mujs <this-file> [-f] [-l file1.js -l ...] suit-root test-file");
		mujs.quit(1);
	}

	// our file loader
	function load(str, as_filename) {
		try {
			var runtime_err = false;
			var compiled = mujs.compile(str, as_filename);
			runtime_err = true;
			compiled();
			return false;
		} catch (e) {
			return {err: e, runtime: runtime_err};
		}
	}

	var args = mujs.scriptArgs;
	var full_mode = false;
	var print_src = false;
	var overrides = [];
	while ((""+args[0])[0] == "-") {
		switch (args[0]) {
			case "-f": full_mode = true;
				   break;
			case "-s": print_src = true;
				   break;
			case "-l": args.shift();
				   overrides.push(args[0]);
				   break;
			default: die_usage("Unknown option " + args[0]);
		}
		args.shift();
	}
	if (args.length != 2)
		die_usage("Exactly 2 paths are expected");
	var root_path = args[0];
	var test_path = args[1];

	// load suite utils
	["sta.js", "testBuiltInObject.js", "testIntl.js"]
	.forEach(function(u) {
		var path = root_path + "/test/harness/" + u;
		var as_file = full_mode ? path : "test/harness/" + u;
		var err = load(mujs.read(path), as_file);
		if (err) throw (err.err);
	});

	// load user overrides (e.g. reduced getPrecision), with a global mujs
	if (overrides.length) {
		global.mujs = mujs
		overrides.forEach(function(f) {
			var err = load(mujs.read(f), f);
			if (err) throw (err.err);
		});
		delete global.mujs;
	}

	// the actual test
	var source = mujs.read(test_path);
	var negative = !!source.match(/@negative/);
	if (negative)
		var neg_str = (source.match(/@negative (.*)/) || [])[1];
	var as_file = test_path;
	if (!full_mode) {
		as_file = test_path.replace(/\\/g, "/");
		var sub = as_file.indexOf("/suite/");
		if (sub >= 0)
			as_file = "test" + as_file.substring(sub);
	}

	var result = load(mujs.read(test_path), as_file);
	if (!!result == negative) {
		// The docs don't really help about matching str, but this covers all cases
		if (neg_str)
			var err_for_match =  /NotEarlyError/.test(neg_str) ? result.err.message : result.err.name;
		if (!negative || !neg_str || RegExp(neg_str).exec(err_for_match))
			mujs.quit(0);
	}

	// failed
	// FIXME: @description can span lines. E.g. test/suite/bestPractice/Sbp_A3_T2.js
	var desc = source.match(/@description (.*)/);
	var info = "[File]  " + as_file +
		   (desc ? "\n[Desc]  " + desc[1] : "") +
		   "\n";

	if (result) {
		var err = result.err;
		var msg = !neg_str ? err : "[Mismatch @negative " + neg_str + "]" + "\n        " + err;

		info += (result.runtime ? "[run]   " : "[load]  ") + msg;
		if (err && err.stack && (result.runtime || full_mode)) {
			if (full_mode) {
				info += err.stack;
			} else {
				// trim the internal loader from the trace
				var internal = err.stack.indexOf("\n" + load("mujs_blahblah()").err.stack.trim().split("\n")[1]);
				if (internal >= 0)
					info += err.stack.substring(0, internal);
				else
					info += err.stack;
			}
		}
	} else {
		info += "[run]   [Error expected but none thrown]";
	}

	if (print_src)
		info += "\n[Source]\n" + source;

	mujs.print(info);
	mujs.quit(1);

})(this)
