
/*
 * \brief  Child representation
 * \author Norman Feske
 * \date   2018-01-23
 */

/*
 * Copyright (C) 2018 Genode Labs GmbH
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU Affero General Public License version 3.
 */

/* local includes */
#include <child.h>

using namespace Depot_deploy;

static void forward_to_log(Genode::uint64_t const sec,
                           Genode::uint64_t const ms,
                           char      const *const base,
                           char      const *const end)
{
	log(sec, ".", ms < 10 ? "00" : ms < 100 ? "0" : "", ms, " ",
	    Cstring(base, end - base));
}


void Child::gen_start_node(Xml_generator          &xml,
                           Xml_node                common,
                           Depot_rom_server const &cached_depot_rom,
                           Depot_rom_server const &uncached_depot_rom)
{
	if (_state != UNFINISHED) {
		while (Timeout_event *event = _timeout_events.first()) {
			_timeout_events.remove(event);
			destroy(_alloc, event);
		}
		while (Log_event *event = _log_events.first()) {
			_log_events.remove(event);
			destroy(_alloc, event);
		}
		return;
	}

	if (_skip) {

		log("");
		log("--- Run \"", _name, "\" (max 1 sec) ---");
		log("");

		_state = State::SKIPPED;

		char name_padded[32];
		for (size_t i = 0; i < sizeof(name_padded) - 1; name_padded[i++] = ' ');
		name_padded[sizeof(name_padded) - 1] = '\0';
		memcpy(name_padded, _name.string(), min(_name.length() - 1, sizeof(name_padded) - 1));

		_conclusion = Conclusion {
			Cstring(name_padded), " skipped" };

		log(" ", _conclusion);
		_config_handler.submit();

		return;
	}

	if (!_configured() || _condition == UNSATISFIED)
		return;

	if (_defined_by_launcher() && !_launcher_xml.constructed())
		return;

	if (!_pkg_xml->xml().has_sub_node("runtime")) {
		warning("blueprint for '", _name, "' lacks runtime information");
		return;
	}

	Xml_node const runtime = _pkg_xml->xml().sub_node("runtime");

	xml.node("start", [&] () {

		xml.attribute("name", _name);

		unsigned long caps = _pkg_cap_quota;
		if (_defined_by_launcher())
			caps = _launcher_xml->xml().attribute_value("caps", caps);
		caps = _start_xml->xml().attribute_value("caps", caps);

		xml.attribute("caps", caps);

		typedef String<64> Version;
		Version const version = _start_xml->xml().attribute_value("version", Version());
		if (version.valid())
			xml.attribute("version", version);

		xml.node("binary", [&] () { xml.attribute("name", _binary_name); });

		Number_of_bytes ram = _pkg_ram_quota;
		if (_defined_by_launcher())
			ram = _launcher_xml->xml().attribute_value("ram", ram);
		ram = _start_xml->xml().attribute_value("ram", ram);

		xml.node("resource", [&] () {
			xml.attribute("name", "RAM");
			xml.attribute("quantum", String<32>(ram));
		});

		/*
		 * Insert inline '<config>' node if provided by the start node,
		 * the launcher definition (if a launcher is user), or the
		 * blueprint. The former is preferred over the latter.
		 */
		if (_start_xml->xml().has_sub_node("config")) {
			_gen_copy_of_sub_node(xml, _start_xml->xml(), "config");
		} else {
			if (_defined_by_launcher() && _launcher_xml->xml().has_sub_node("config")) {
				_gen_copy_of_sub_node(xml, _launcher_xml->xml(), "config");
			} else {
				if (runtime.has_sub_node("config"))
					_gen_copy_of_sub_node(xml, runtime, "config");
			}
		}

		/*
		 * Declare services provided by the subsystem.
		 */
		if (runtime.has_sub_node("provides")) {
			xml.node("provides", [&] () {
				runtime.sub_node("provides").for_each_sub_node([&] (Xml_node service) {
					_gen_provides_sub_node(xml, service, "audio_in",    "Audio_in");
					_gen_provides_sub_node(xml, service, "audio_out",   "Audio_out");
					_gen_provides_sub_node(xml, service, "block",       "Block");
					_gen_provides_sub_node(xml, service, "file_system", "File_system");
					_gen_provides_sub_node(xml, service, "framebuffer", "Framebuffer");
					_gen_provides_sub_node(xml, service, "input",       "Input");
					_gen_provides_sub_node(xml, service, "log",         "LOG");
					_gen_provides_sub_node(xml, service, "nic",         "Nic");
					_gen_provides_sub_node(xml, service, "nitpicker",   "Nitpicker");
					_gen_provides_sub_node(xml, service, "report",      "Report");
					_gen_provides_sub_node(xml, service, "rom",         "ROM");
					_gen_provides_sub_node(xml, service, "terminal",    "Terminal");
					_gen_provides_sub_node(xml, service, "timer",       "Timer");
				});
			});
		}

		xml.node("route", [&] () {
			_gen_routes(xml, common, cached_depot_rom, uncached_depot_rom); });
	});
	if (_running) {
		return; }

	Genode::uint64_t max_timeout_sec = 0;
	try {
		Xml_node const events = _pkg_xml->xml().sub_node("runtime").sub_node("events");
		events.for_each_sub_node("timeout", [&] (Xml_node const &event) {
			try {
				Timeout_event &timeout = *new (_alloc) Timeout_event(_timer, *this, event);
				if (timeout.sec() > max_timeout_sec) {
					max_timeout_sec = timeout.sec();
				}
				_timeout_events.insert(&timeout);
			}
			catch (Timeout_event::Invalid) { warning("Invalid timeout event"); }
		});
		events.for_each_sub_node("log", [&] (Xml_node const &event) {
			_log_events.insert(new (_alloc) Log_event(event));
		});
	}
	catch (...) { }
	log("");
	log("--- Run \"", _name, "\" (max ", max_timeout_sec, " sec) ---");
	log("");
	_running = true;
	init_time_us = _timer.curr_time().trunc_to_plain_us().value;
}


void Child::_gen_routes(Xml_generator          &xml,
                        Xml_node                common,
                        Depot_rom_server const &cached_depot_rom,
                        Depot_rom_server const &uncached_depot_rom) const
{
	if (_skip) {
		return; }

	if (!_pkg_xml.constructed())
		return;

	typedef String<160> Path;

	/*
	 * Add routes given in the start node.
	 */
	if (_start_xml->xml().has_sub_node("route")) {
		Xml_node const route = _start_xml->xml().sub_node("route");
		route.with_raw_content([&] (char const *start, size_t length) {
			xml.append(start, length); });
	}

	/*
	 * Add routes given in the launcher definition.
	 */
	if (_launcher_xml.constructed() && _launcher_xml->xml().has_sub_node("route")) {
		Xml_node const route = _launcher_xml->xml().sub_node("route");
		route.with_raw_content([&] (char const *start, size_t length) {
			xml.append(start, length); });
	}

	/**
	 * Return name of depot-ROM server used for obtaining the 'path'
	 *
	 * If the depot path refers to the depot-user "local", route the
	 * session request to the non-cached ROM service.
	 */
	auto rom_server = [&] (Path const &path) {

		return (String<7>(path) == "local/") ? uncached_depot_rom
		                                     : cached_depot_rom;
	};

	/*
	 * Redirect config ROM request to label as given in the 'config' attribute,
	 * if present. We need to search the blueprint's <rom> nodes for the
	 * matching ROM module to rewrite the label with the configuration's path
	 * within the depot.
	 */
	if (_config_name.valid()) {
		_pkg_xml->xml().for_each_sub_node("rom", [&] (Xml_node rom) {

			if (!rom.has_attribute("path"))
				return;

			if (rom.attribute_value("label", Name()) != _config_name)
				return;

			/* we found the <rom> node for the config ROM */
			xml.node("service", [&] () {
				xml.attribute("name",  "ROM");
				xml.attribute("label", "config");
				typedef String<160> Path;
				Path const path = rom.attribute_value("path", Path());

				if (cached_depot_rom.valid())
					xml.node("child", [&] () {
						xml.attribute("name", rom_server(path));
						xml.attribute("label", path); });
				else
					xml.node("parent", [&] () {
						xml.attribute("label", path); });
			});
		});
	}

	/*
	 * Add common routes as defined in our config.
	 */
	common.with_raw_content([&] (char const *start, size_t length) {
		xml.append(start, length); });

	/*
	 * Add ROM routing rule with the label rewritten to the path within the
	 * depot.
	 */
	_pkg_xml->xml().for_each_sub_node("rom", [&] (Xml_node rom) {

		if (!rom.has_attribute("path"))
			return;

		typedef Name Label;
		Path  const path  = rom.attribute_value("path",  Path());
		Label const label = rom.attribute_value("label", Label());

		xml.node("service", [&] () {
			xml.attribute("name", "ROM");
			xml.attribute("label_last", label);

			if (cached_depot_rom.valid()) {
				xml.node("child", [&] () {
					xml.attribute("name",  rom_server(path));
					xml.attribute("label", path);
				});
			} else {
				xml.node("parent", [&] () {
					xml.attribute("label", path); });
			}
		});
	});
}


bool Child::_defined_by_launcher() const
{
	if (_skip) {
		return false; }

	/*
	 * If the <start> node lacks a 'pkg' attribute, we expect the
	 * policy to be defined by a launcher XML snippet.
	 */
	return _start_xml.constructed() && !_start_xml->xml().has_attribute("pkg");
}


Archive::Path Child::_config_pkg_path() const
{
	if (_skip) {
		return Archive::Path(); }

	if (_defined_by_launcher() && _launcher_xml.constructed())
		return _launcher_xml->xml().attribute_value("pkg", Archive::Path());

	return _start_xml->xml().attribute_value("pkg", Archive::Path());
}


Child::Launcher_name Child::_launcher_name() const
{
	if (_skip) {
		return Launcher_name(); }

	if (!_defined_by_launcher())
		return Launcher_name();

	if (_start_xml->xml().has_attribute("launcher"))
		return _start_xml->xml().attribute_value("launcher", Launcher_name());

	return _start_xml->xml().attribute_value("name", Launcher_name());
}


bool Child::_configured() const
{
	if (_skip) {
		return false; }

	return _pkg_xml.constructed()
	   && (_config_pkg_path() == _blueprint_pkg_path);
}


void Child::_gen_provides_sub_node(Xml_generator        &xml,
                                   Xml_node              service,
                                   Xml_node::Type const &node_type,
                                   Service::Name  const &service_name)
{
	if (service.type() == node_type)
		xml.node("service", [&] () {
			xml.attribute("name", service_name); });
}


void Child::_gen_copy_of_sub_node(Xml_generator        &xml,
                                  Xml_node              from_node,
                                  Xml_node::Type const &sub_node_type)
{
	if (!from_node.has_sub_node(sub_node_type.string()))
		return;

	Xml_node const sub_node = from_node.sub_node(sub_node_type.string());
	sub_node.with_raw_node([&] (char const *start, size_t length) {
		xml.append(start, length); });
}


Child::Child(Allocator                       &alloc,
             Xml_node                         start_node,
             Timer::Connection               &timer,
             Signal_context_capability const &config_handler)
:
	_skip           { start_node.attribute_value("skip", false) },
	_alloc          { alloc },
	_start_xml      { _alloc, start_node },
	_name           { _start_xml->xml().attribute_value("name", Name()) },
	_timer          { timer },
	_config_handler { config_handler }
{ }


Child::~Child()
{
	while (Timeout_event *event = _timeout_events.first()) {
		_timeout_events.remove(event);
		destroy(_alloc, event);
	}
	while (Log_event *event = _log_events.first()) {
		_log_events.remove(event);
		destroy(_alloc, event);
	}
}


void Child::log_session_write(Log_event::Line const &log_line)
{
	if (_skip) {
		return; }

	enum { ASCII_ESC = 27 };
	enum { ASCII_LF  = 10 };
	enum { ASCII_TAB = 9 };

	struct Break : Exception { };

	
	struct Skip_escape_sequence
	{
		char const * const base;
		size_t       const size;
	};
	struct Replace_ampersend_sequence
	{
		char const * const base;
		size_t       const size;
		char         const by;
	};
	static Skip_escape_sequence skip_esc_seq[5]
	{
		{ "[0m", 3 },
		{ "[31m", 4 },
		{ "[32m", 4 },
		{ "[33m", 4 },
		{ "[34m", 4 },
	};
	static Replace_ampersend_sequence replace_amp_seq[3]
	{
		{ "lt;", 3, '<' },
		{ "amp;", 4, '&' },
		{ "#42;", 4, '*' }
	};

	/* calculate timestamp that prefixes*/
	Genode::uint64_t const time_us  { _timer.curr_time().trunc_to_plain_us().value - init_time_us };
	Genode::uint64_t       time_ms  { time_us / 1000UL };
	Genode::uint64_t const time_sec { time_ms / 1000UL };
	time_ms = time_ms - time_sec * 1000UL;

	char const *const log_base { log_line.string() };
	char const *const log_end  { log_base + strlen(log_base) };
	try {
		char const *log_print { log_base };
		_log_events.for_each([&] (Log_event &log_event) {

			bool        match        { false };
			char const *pattern_end  { log_event.remaining_end() };
			char const *pattern_curr { log_event.remaining_base() };
			char const *log_curr     { log_base };

			for (;;) {

				/* handle end of pattern */
				if (pattern_curr == pattern_end) {
					match = true;
					log_event.remaining_base() = log_event.base();
					log_event.reset_to()       = log_event.base();
					log_event.reset_retry()    = false;
					break;
				}
				/* skip irrelevant characters in the pattern */
				if (*pattern_curr == ASCII_LF || *pattern_curr == ASCII_TAB) {
					pattern_curr++;
					continue;
				}
				if (*pattern_curr == '*') {
					pattern_curr++;
					log_event.reset_to()    = pattern_curr;
					log_event.reset_retry() = false;
					continue;
				}
				/* handle end of log line */
				if (log_curr == log_end) {
					log_event.remaining_base() = pattern_curr;
					break;
				}
				/* skip irrelevant characters in the log line */
				if (*log_curr == ASCII_LF) {

					/* forward to our log session a complete line */
					if (log_print < log_curr) {
						forward_to_log(time_sec, time_ms, log_print, log_curr);
						log_print = log_curr + 1;
					}
					log_curr++;
					continue;
				}
				if (*log_curr == ASCII_TAB) {
					log_curr++;
					continue;
				}
				/* skip irrelevant escape sequences in the log line */
				if (*log_curr == ASCII_ESC) {

					bool seq_match { false };

					for (unsigned i = 0; i < sizeof(skip_esc_seq)/sizeof(skip_esc_seq[0]); i++) {

						char const *seq_curr { skip_esc_seq[i].base };
						char const *seq_end  { seq_curr + skip_esc_seq[i].size };

						for (char const *log_seq_curr { log_curr + 1 } ; ; log_seq_curr++, seq_curr++) {

							if (seq_curr == seq_end) {
								seq_match = true;
								log_curr  = log_seq_curr;
								break;
							}
							if (log_seq_curr == log_end) {
								break; }

							if (*log_seq_curr != *seq_curr) {
								break; }
						}
						if (seq_match) {
							break; }
					}
					if (seq_match) {
						continue; }
				}
				char   pattern_curr_san    = *pattern_curr;
				size_t pattern_curr_san_sz = 1;

				/* replace ampersend sequences in the pattern */
				if (*pattern_curr == '&') {

					bool seq_match { false };

					for (unsigned i = 0; i < sizeof(replace_amp_seq)/sizeof(replace_amp_seq[0]); i++) {

						char const *seq_curr { replace_amp_seq[i].base };
						char const *seq_end  { seq_curr + replace_amp_seq[i].size };

						for (char const *pattern_seq_curr { pattern_curr + 1 } ; ; pattern_seq_curr++, seq_curr++) {

							if (seq_curr == seq_end) {
								seq_match = true;
								pattern_curr_san    = replace_amp_seq[i].by;
								pattern_curr_san_sz = replace_amp_seq[i].size + 1;
								break;
							}
							if (pattern_seq_curr == pattern_end) {
								break; }

							if (*pattern_seq_curr != *seq_curr) {
								break; }
						}
						if (seq_match) {
							break;
						}
					}
				}
				/* check if log keeps matching pattern */
				if (*log_curr != pattern_curr_san) {
					pattern_curr = log_event.reset_to();
					if (!log_event.reset_retry()) {

						log_curr++; }
					else {
						log_event.reset_retry() = false; }
				} else {
					pattern_curr += pattern_curr_san_sz;
					log_curr++;
					log_event.reset_retry() = true;
				}
			}
			/* forward to our log session what is left */
			if (log_print < log_curr) {
				for (;; log_curr++) {

					if (log_curr == log_end) {

						forward_to_log(time_sec, time_ms, log_print, log_curr);
						log_print = log_curr;
						break;
					}
					if (*log_curr == ASCII_LF) {

						forward_to_log(time_sec, time_ms, log_print, log_curr);
						log_print = log_curr + 1;
						break;
					}
				}
			}
			/* check if log line finished a match with the pattern */
			if (!match) {
				return; }

			/* execute event handler and stop trying further events */
			event_occured(log_event, time_us);
			throw Break();
		});
	}
	catch (...) { }
}


void Child::apply_config(Xml_node start_node)
{
	if (_skip)
		return;

	if (!start_node.differs_from(_start_xml->xml()))
		return;

	Archive::Path const old_pkg_path = _config_pkg_path();

	/* import new start node */
	_start_xml.construct(_alloc, start_node);

	Archive::Path const new_pkg_path = _config_pkg_path();

	/* invalidate blueprint if 'pkg' path changed */
	if (old_pkg_path != new_pkg_path) {
		_blueprint_pkg_path = new_pkg_path;
		_pkg_xml.destruct();

		/* reset error state, attempt to obtain the blueprint again */
		_pkg_incomplete = false;
	}
}


void Child::apply_blueprint(Xml_node pkg)
{
	if (_skip) {
		return; }

	if (pkg.attribute_value("path", Archive::Path()) != _blueprint_pkg_path)
		return;

	try {
		Xml_node const runtime = pkg.sub_node("runtime");

		/* package was missing but is installed now */
		_pkg_incomplete = false;

		_pkg_ram_quota = runtime.attribute_value("ram", Number_of_bytes());
		_pkg_cap_quota = runtime.attribute_value("caps", 0UL);

		_binary_name = runtime.attribute_value("binary", Binary_name());
		_config_name = runtime.attribute_value("config", Config_name());

		/* keep copy of the blueprint info */
		_pkg_xml.construct(_alloc, pkg);
	}
	catch (Xml_node::Nonexistent_sub_node) {
		error("missing runtime subnode in packege blueprint");
	}
}


void Child::apply_launcher(Launcher_name const &name,
                           Xml_node             launcher)
{
	if (_skip)
		return;

	if (!_defined_by_launcher())
		return;

	if (_launcher_name() != name)
		return;

	if (_launcher_xml.constructed() && !launcher.differs_from(_launcher_xml->xml()))
		return;

	_launcher_xml.construct(_alloc, launcher);

	_blueprint_pkg_path = _config_pkg_path();
}


Child::State_name Child::_padded_state_name() const
{
	if (_skip) {
		return "?"; }

	switch (_state) {
	case SUCCEEDED:  return "ok     ";
	case FAILED:     return "failed ";
	case SKIPPED:;
	case UNFINISHED: ;
	}
	return "?";
}


void Child::print_conclusion()
{
	log(" ", _conclusion);
}

void Child::conclusion(Result &result)
{
	struct Bad_state : Exception { };
	switch (_state) {
	case SUCCEEDED: result.succeeded++; break;
	case FAILED:    result.failed++;    break;
	case SKIPPED:   result.skipped++;   break;
	default:        throw Bad_state();
	}
}


void Child::mark_as_incomplete(Xml_node missing)
{
	if (_skip) {
		return; }

	/* print error message only once */
	if(_pkg_incomplete)
		return;

	Archive::Path const path = missing.attribute_value("path", Archive::Path());
	if (path != _blueprint_pkg_path)
		return;

	log(path, " incomplete or missing");

	_pkg_incomplete = true;
}


void Child::reset_incomplete()
{
	if (_skip) {
		return; }

	if (_pkg_incomplete) {
		_pkg_incomplete = false;
		_pkg_xml.destruct();
	}
}


bool Child::gen_query(Xml_generator &xml) const
{
	if (_skip) {
		return false; }

	if (_configured() || _pkg_incomplete)
		return false;

	if (_defined_by_launcher() && !_launcher_xml.constructed())
		return false;

	xml.node("blueprint", [&] () {
		xml.attribute("pkg", _blueprint_pkg_path); });

	return true;
}


void Child::gen_installation_entry(Xml_generator &xml) const
{
	if (_skip) {
		return; }

	if (!_pkg_incomplete) return;

	xml.node("archive", [&] () {
		xml.attribute("path", _config_pkg_path());
		xml.attribute("source", "no");
	});
}


void Child::event_occured(Event            const &event,
                          Genode::uint64_t const  time_us)
{
	if (_skip) {
		return; }

	if (_state != UNFINISHED) {
		return; }

	if      (event.meaning() == "succeeded") { _finished(SUCCEEDED, event, time_us); }
	else if (event.meaning() == "failed"   ) { _finished(FAILED,    event, time_us); }
}


void Child::_finished(State                   state,
                      Event            const &event,
                      Genode::uint64_t const  time_us)
{
	if (_skip) {
		return; }

	_running = false;
	_state = state;

	Genode::uint64_t       time_ms  { time_us / 1000UL };
	Genode::uint64_t const time_sec { time_ms / 1000UL };
	time_ms = time_ms - time_sec * 1000UL;

	char name_padded[32];
	for (size_t i = 0; i < sizeof(name_padded) - 1; name_padded[i++] = ' ');
	name_padded[sizeof(name_padded) - 1] = '\0';
	memcpy(name_padded, _name.string(), min(_name.length() - 1, sizeof(name_padded) - 1));

	if (event.has_type(Event::Type::LOG)) {

		enum { MAX_EXPL_SZ = 32 };

		Log_event const &log_event = *static_cast<Log_event const *>(&event);

		char const *const pattern_base = log_event.base();
		size_t      const pattern_sz   = log_event.size();
		char const *const pattern_end  = pattern_base + pattern_sz;

		char const *expl_base = pattern_base;
		for (; expl_base < pattern_end && *expl_base < 33; expl_base++) ;

		char const *expl_end = expl_base;
		for (; expl_end < pattern_end && *expl_end > 31; expl_end++) ;

		size_t const expl_sz = min((size_t)(expl_end - expl_base), (size_t)MAX_EXPL_SZ);

		_conclusion = Conclusion {
			Cstring(name_padded), " ", _padded_state_name(), "  ",
			time_sec < 10 ? "  " : time_sec < 100 ? " " : "", time_sec, ".",
			time_ms  < 10 ? "00" : time_ms  < 100 ? "0" : "", time_ms,
			"  log \"", Cstring(expl_base, expl_sz),
			expl_sz < MAX_EXPL_SZ ? "" : " ...", "\"" };

	} else if (event.has_type(Event::Type::TIMEOUT)) {

		Timeout_event const &timeout_event = *static_cast<Timeout_event const *>(&event);

		_conclusion = Conclusion {
			Cstring(name_padded), " ", _padded_state_name(), "  ",
			time_sec < 10 ? "  " : time_sec < 100 ? " " : "", time_sec, ".",
			time_ms  < 10 ? "00" : time_ms  < 100 ? "0" : "", time_ms,
			"  timeout ", timeout_event.sec(), " sec" };

	} else {

		_conclusion = Conclusion { _padded_state_name(), "  ", _name };

	}
	log("");
	log(" ", _conclusion);
	_config_handler.submit();
}


/*******************
 ** Timeout_event **
 *******************/

Timeout_event::Timeout_event(Timer::Connection &timer,
                             Child             &child,
                             Xml_node    const &event)
:
	Event    { event, Type::TIMEOUT },
	_child   { child },
	_timer   { timer },
	_sec     { event.attribute_value("sec", (Genode::uint64_t)0) },
	_timeout { timer, *this, &Timeout_event::_handle_timeout }
{
	if (!_sec) {
		throw Invalid(); }

	_timeout.schedule(Microseconds(_sec * 1000 * 1000));
}


void Timeout_event::_handle_timeout(Duration)
{
	_child.event_occured(*this, _timer.curr_time().trunc_to_plain_us().value - _child.init_time_us);
}


/***************
 ** Log_event **
 ***************/

static char const *xml_content_base(Xml_node node)
{
	char const *result = nullptr;
	node.with_raw_content([&] (char const *start, size_t) { result = start; });
	return result;
}


static size_t xml_content_size(Xml_node node)
{
	size_t result = 0;
	node.with_raw_content([&] (char const *, size_t length) { result = length; });
	return result;
}


Log_event::Log_event(Xml_node const &xml)
:
	Event           { xml, Type::LOG },
	_base           { xml_content_base(xml) },
	_size           { xml_content_size(xml) },
	_remaining_base { xml_content_base(xml) },
	_remaining_end  { _remaining_base + xml_content_size(xml) },
	_reset_to       { xml_content_base(xml) }
{ }


/***********
 ** Event **
 ***********/

Event::Event(Xml_node const &node,
             Type            type)
:
	_meaning { node.attribute_value("meaning", Meaning_string()) },
	_type    { type }
{
	if (_meaning != Meaning_string("failed") &&
	    _meaning != Meaning_string("succeeded"))
	{
		throw Invalid();
	}
}


/************
 ** Result **
 ************/

void Result::print(Output &output) const
{
	Genode::print(output, "succeeded: ", succeeded, " failed: ", failed,
	              " skipped: ", skipped);
}
