main.lua

Thu, 31 Dec 2015 18:18:37 +0000

author
Matthew Wild <mwild1@gmail.com>
date
Thu, 31 Dec 2015 18:18:37 +0000
changeset 65
629162fc0681
parent 63
0f19ffbe1519
child 67
a2a9dd606200
permissions
-rwxr-xr-x

main: Change JSON log format for clarity, error may be more than just a simple message now

#!/usr/bin/env luajit

local json = require "cjson";
local time = require "socket".gettime;

local result_log_filename = nil;
local server_log_reader = nil;
local property_rules = {};

local function apply_object_properties(class, name, object)
	for _, rule in ipairs(property_rules) do
		if (not(rule.class) or tostring(rule.class):lower() == tostring(class):lower())
		and (not(rule.name) or tostring(rule.name):lower() == tostring(name):lower()) then
			for prop_key, prop_value in pairs(rule.properties) do
				object[prop_key] = prop_value;
			end
		end
	end
end

function process_options()
	local function get_value()
		return (assert(table.remove(arg, 1), "unexpected end of command-line options"));
	end
	while arg[1] and arg[1]:sub(1,1) == "-" do
		local opt = arg[1];
		if opt == "--" then
			table.remove(arg, 1);
			break;
		end
		table.remove(arg, 1);
		if opt == "--port" or opt == "-p" then
			local port = assert(tonumber(get_value()), "port number must be a number");
			table.insert(property_rules, { class = "client", properties = { connect_port = port } });
		elseif opt == "--host" or opt == "-h" then
			local host = get_value();
			table.insert(property_rules, { class = "client", properties = { connect_host = host } });
		elseif opt == "--out" or opt == "-o" then
			result_log_filename = get_value();
		elseif opt == "--server-log" or opt == "-s" then
			local server_log = assert(io.open(get_value(), "r"));
			function server_log_reader()
				local new_lines = {};
				for line in server_log:lines() do
					table.insert(new_lines, line);
				end
				return new_lines;
			end
		else
			error("Unhandled command-line option: "..opt);
		end
	end
	assert(#arg > 0, "No test script provided");
	assert(#arg < 2, "Too many parameters");
end

function read_script()
	io.input(arg[1]);
	return io.read("*a");
	
end

function parse_script(data)
	local parser = require "scansion.parser";
	return assert(parser.parse(data));
end

function initialize_script(script)
	local c = 0;
	for name, object in pairs(script.objects) do
		local o = require("scansion.objects."..object.type);
		object.handler = o;
		object.script = script;
		apply_object_properties(object.type, object.name, object);
		o._validate(object);
		c = c + 1;
	end
	
	--print("Script defines "..c.." objects, and "..#script.actions.." actions");	
	return script;
end

function initialize_verse(errcb)
	local verse = require "verse";
	
	verse.set_log_handler(verse._default_log_handler, { "debug", "info", "warn", "error" });

	local function error_handler(err)
		verse.log("error", "Error: %s", err);
		verse.log("error", "Traceback: %s", debug.traceback());
		errcb(err);
	end
	verse.set_error_handler(error_handler);
	return verse;
end

function main(log_data)

	local ok, err = true;

	local script = initialize_script(parse_script(read_script()));
	
	if server_log_reader then
		log_data("server", { lines = server_log_reader() });
	end

	local verse = initialize_verse(function (_err) ok, err = false, _err end);
	local async = require "scansion.async";

	local runner = async.runner(function (d)
		for _, action in pairs(script.actions) do
			local object = script.objects[action.object_name];
			local handler = object.handler;
			assert(handler[action.action], "Objects of type '"..object.type.."' do not support action '"..action.action.."'");
			print("");
			log_data("action", {
				action = action.action;
				object = object.name;
				object_type = object.type;
				extra = action.extra;
				annotation = action.annotation;
			});
			if action.annotation then
				print(action.annotation);
			end
			if server_log_reader then
				log_data("server", { lines = server_log_reader() });
			end
			print(object.name, action.action.."...");
			local ok, err = pcall(handler[action.action], object, action.extra);
			if not ok then
				log_data("error", { message = err });
				error(err);
			end
		end
		verse.log("info", "Completed script!");
		verse.quit();
	end, {
		error = function (runner, _err)
			verse.log("error", "Runner caught error: %s", _err);
			ok, err = false, _err;
			verse.quit();
		end;
	});
	
	runner:run(true);
	verse.log("debug", "runner paused")
	
	verse.loop();
	return ok, err;
end

-- Process command-line options
process_options();

-- Dummy logging function, used if no log file set
local log_data = function () end;

local result_log;
if result_log_filename then
	result_log = assert(io.open(result_log_filename, "w+"));
	result_log:write("[\n");
	function log_data(type, data)
		local entry = { type = type, data = data, time = time() };
		result_log:write("  ", json.encode(entry), ",\n");
		result_log:flush();
	end
end

log_data("start");
local ok, result, err = pcall(main, log_data);

local exit_code = 0;
if not ok then
	print("TEST ERROR:", result);
	exit_code = 2;
	log_data("test-error", { error = result });
elseif not result then
	print("TEST FAILED", err);
	exit_code = 1;
	log_data("test-failed", { error = err });
else
	print("TEST PASSED");
	log_data("test-passed");
end

if result_log then
	result_log:write([[  {"type": "end", "time": ]]..time().."}\n]\n");
	result_log:close();
end

os.exit(exit_code);

mercurial