mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-20 22:23:16 +00:00
Automated test for init
This commit is contained in:
parent
84fddafda7
commit
641fb08b5f
129
repos/os/run/init.run
Normal file
129
repos/os/run/init.run
Normal file
@ -0,0 +1,129 @@
|
||||
#
|
||||
# Build
|
||||
#
|
||||
|
||||
set build_components { core init drivers/timer app/dummy test/init }
|
||||
|
||||
build $build_components
|
||||
|
||||
create_boot_directory
|
||||
|
||||
#
|
||||
# Generate config
|
||||
#
|
||||
|
||||
append config {
|
||||
<config>
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="RAM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="PD"/>
|
||||
<service name="LOG"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="IO_PORT"/>
|
||||
</parent-provides>
|
||||
|
||||
<default-route>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
|
||||
<start name="report_rom">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<provides> <service name="ROM"/> <service name="Report"/> </provides>
|
||||
<config verbose="no">
|
||||
<policy label="init -> init.config" report="test-init -> init.config"/>
|
||||
<policy label="test-init -> state" report="init -> state"/>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="test-init">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<provides> <service name="LOG"/> </provides>
|
||||
<config>
|
||||
|
||||
<!-- let init settle, processing the initially invalid config -->
|
||||
<sleep ms="250"/>
|
||||
|
||||
|
||||
<message string="test state reporting"/>
|
||||
|
||||
<init_config version="initial">
|
||||
<report init_ram="yes" ids="yes" child_ram="yes" requested="yes"/>
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="RAM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="PD"/>
|
||||
<service name="LOG"/>
|
||||
</parent-provides>
|
||||
<start name="application">
|
||||
<binary name="dummy"/>
|
||||
<resource name="RAM" quantum="1G"/>
|
||||
<config>
|
||||
<log string="started"/>
|
||||
</config>
|
||||
<route> <any-service> <parent/> </any-service> </route>
|
||||
</start>
|
||||
</init_config>
|
||||
<expect_log string="[init -> application] started"/>
|
||||
<sleep ms="200"/>
|
||||
<expect_init_state>
|
||||
<attribute name="version" value="initial"/>
|
||||
<node name="child">
|
||||
<attribute name="name" value="application"/>
|
||||
<attribute name="binary" value="dummy"/>
|
||||
<node name="requested">
|
||||
<node name="session">
|
||||
<attribute name="service" value="PD"/>
|
||||
<attribute name="label" value="application"/>
|
||||
<attribute name="state" value="CAP_HANDED_OUT"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</expect_init_state>
|
||||
|
||||
</config>
|
||||
<route>
|
||||
<service name="Report"> <child name="report_rom"/> </service>
|
||||
<service name="ROM" label="state"> <child name="report_rom"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="init">
|
||||
<binary name="init"/>
|
||||
<resource name="RAM" quantum="16M"/>
|
||||
<configfile name="init.config"/>
|
||||
<route>
|
||||
<service name="ROM" label="init.config"> <child name="report_rom"/> </service>
|
||||
<service name="Report"> <child name="report_rom"/> </service>
|
||||
<service name="LOG"> <child name="test-init"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
</config>}
|
||||
|
||||
install_config $config
|
||||
|
||||
#
|
||||
# Boot modules
|
||||
#
|
||||
|
||||
set boot_modules { core ld.lib.so init timer report_rom test-init dummy }
|
||||
|
||||
build_boot_image $boot_modules
|
||||
|
||||
append qemu_args " -nographic "
|
||||
|
||||
run_genode_until {.*child "test-init" exited with exit value 0.*} 60
|
||||
|
98
repos/os/src/app/dummy/main.cc
Normal file
98
repos/os/src/app/dummy/main.cc
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* \brief Dummy component used for automated component-composition tests
|
||||
* \author Norman Feske
|
||||
* \date 2017-02-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/heap.h>
|
||||
#include <base/registry.h>
|
||||
#include <base/component.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <log_session/connection.h>
|
||||
|
||||
namespace Dummy {
|
||||
|
||||
struct Log_connections;
|
||||
struct Main;
|
||||
using namespace Genode;
|
||||
}
|
||||
|
||||
|
||||
struct Dummy::Log_connections
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
Heap _heap { _env.ram(), _env.rm() };
|
||||
|
||||
typedef Registered<Log_connection> Connection;
|
||||
|
||||
Registry<Connection> _connections;
|
||||
|
||||
Log_connections(Env &env, Xml_node node) : _env(env)
|
||||
{
|
||||
unsigned const count = node.attribute_value("count", 0UL);
|
||||
|
||||
log("going to create ", count, " LOG connections");
|
||||
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
new (_heap) Connection(_connections, _env, Session_label { i });
|
||||
|
||||
log("created all LOG connections");
|
||||
}
|
||||
|
||||
~Log_connections()
|
||||
{
|
||||
_connections.for_each([&] (Connection &c) { destroy(_heap, &c); });
|
||||
|
||||
log("destroyed all LOG connections");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Dummy::Main
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
Constructible<Timer::Connection> _timer;
|
||||
|
||||
Attached_rom_dataspace _config { _env, "config" };
|
||||
|
||||
Constructible<Log_connections> _log_connections;
|
||||
|
||||
Main(Env &env) : _env(env)
|
||||
{
|
||||
_config.xml().for_each_sub_node([&] (Xml_node node) {
|
||||
|
||||
if (node.type() == "create_log_connections")
|
||||
_log_connections.construct(_env, node);
|
||||
|
||||
if (node.type() == "destroy_log_connections")
|
||||
_log_connections.destruct();
|
||||
|
||||
if (node.type() == "sleep") {
|
||||
|
||||
if (!_timer.constructed())
|
||||
_timer.construct(_env);
|
||||
|
||||
_timer->msleep(node.attribute_value("ms", 100UL));
|
||||
}
|
||||
|
||||
if (node.type() == "log")
|
||||
log(node.attribute_value("string", String<50>()));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env) { static Dummy::Main main(env); }
|
3
repos/os/src/app/dummy/target.mk
Normal file
3
repos/os/src/app/dummy/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = dummy
|
||||
SRC_CC = main.cc
|
||||
LIBS += base
|
329
repos/os/src/test/init/main.cc
Normal file
329
repos/os/src/test/init/main.cc
Normal file
@ -0,0 +1,329 @@
|
||||
/*
|
||||
* \brief Test for the init component
|
||||
* \author Norman Feske
|
||||
* \date 2017-02-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#include <base/heap.h>
|
||||
#include <base/component.h>
|
||||
#include <base/session_label.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <log_session/log_session.h>
|
||||
#include <root/component.h>
|
||||
#include <os/reporter.h>
|
||||
#include <base/sleep.h>
|
||||
|
||||
namespace Test {
|
||||
|
||||
struct Log_message_handler;
|
||||
class Log_session_component;
|
||||
class Log_root;
|
||||
struct Main;
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
static bool xml_attribute_matches(Xml_node, Xml_node);
|
||||
static bool xml_matches(Xml_node, Xml_node);
|
||||
}
|
||||
|
||||
|
||||
/***************
|
||||
** Utilities **
|
||||
***************/
|
||||
|
||||
static inline bool Test::xml_attribute_matches(Xml_node condition, Xml_node node)
|
||||
{
|
||||
typedef String<32> Name;
|
||||
typedef String<64> Value;
|
||||
|
||||
Name const name = condition.attribute_value("name", Name());
|
||||
Value const value = condition.attribute_value("value", Value());
|
||||
|
||||
return node.attribute_value(name.string(), Value()) == value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if 'node' has expected content
|
||||
*
|
||||
* \expected description of the XML content expected in 'node'
|
||||
*/
|
||||
static inline bool Test::xml_matches(Xml_node expected, Xml_node node)
|
||||
{
|
||||
bool matches = true;
|
||||
expected.for_each_sub_node([&] (Xml_node condition) {
|
||||
|
||||
if (condition.type() == "attribute")
|
||||
matches = matches && xml_attribute_matches(condition, node);
|
||||
|
||||
if (condition.type() == "node") {
|
||||
|
||||
typedef String<32> Name;
|
||||
Name const name = condition.attribute_value("name", Name());
|
||||
|
||||
bool at_least_one_sub_node_matches = false;
|
||||
node.for_each_sub_node(name.string(), [&] (Xml_node sub_node) {
|
||||
if (xml_matches(condition, sub_node))
|
||||
at_least_one_sub_node_matches = true; });
|
||||
|
||||
matches = matches && at_least_one_sub_node_matches;
|
||||
}
|
||||
});
|
||||
return matches;
|
||||
}
|
||||
|
||||
|
||||
struct Test::Log_message_handler
|
||||
{
|
||||
typedef String<Log_session::MAX_STRING_LEN> Message;
|
||||
|
||||
enum Result { EXPECTED, UNEXPECTED, IGNORED };
|
||||
|
||||
virtual Result handle_log_message(Message const &message) = 0;
|
||||
};
|
||||
|
||||
|
||||
namespace Genode
|
||||
{
|
||||
static inline void print(Output &output, Test::Log_message_handler::Result result)
|
||||
{
|
||||
using Genode::print;
|
||||
switch (result) {
|
||||
case Test::Log_message_handler::EXPECTED: print(output, "expected"); break;
|
||||
case Test::Log_message_handler::UNEXPECTED: print(output, "expected"); break;
|
||||
case Test::Log_message_handler::IGNORED: print(output, "ignored"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Test::Log_session_component : public Rpc_object<Log_session>
|
||||
{
|
||||
private:
|
||||
|
||||
Session_label const _label;
|
||||
|
||||
Log_message_handler &_handler;
|
||||
|
||||
public:
|
||||
|
||||
Log_session_component(Session_label const &label, Log_message_handler &handler)
|
||||
:
|
||||
_label(label), _handler(handler)
|
||||
{ }
|
||||
|
||||
size_t write(String const &string)
|
||||
{
|
||||
/* strip known line delimiter from incoming message */
|
||||
unsigned n = 0;
|
||||
Genode::String<16> const pattern("\033[0m\n");
|
||||
for (char const *s = string.string(); s[n] && pattern != s + n; n++);
|
||||
|
||||
Log_message_handler::Message const
|
||||
message("[", _label, "] ", Cstring(string.string(), n));
|
||||
|
||||
Log_message_handler::Result const result =
|
||||
_handler.handle_log_message(message);
|
||||
|
||||
log(message, " (", result, ")");
|
||||
|
||||
return strlen(string.string());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Test::Log_root : public Root_component<Log_session_component>
|
||||
{
|
||||
private:
|
||||
|
||||
Log_message_handler &_handler;
|
||||
|
||||
public:
|
||||
|
||||
Log_root(Entrypoint &ep, Allocator &md_alloc, Log_message_handler &handler)
|
||||
:
|
||||
Root_component(ep, md_alloc), _handler(handler)
|
||||
{ }
|
||||
|
||||
Log_session_component *_create_session(const char *args, Affinity const &)
|
||||
{
|
||||
Session_label const label = label_from_args(args);
|
||||
|
||||
return new (md_alloc()) Log_session_component(label, _handler);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Test::Main : Log_message_handler
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
Timer::Connection _timer { _env };
|
||||
|
||||
Reporter _init_config_reporter { _env, "config", "init.config" };
|
||||
|
||||
Attached_rom_dataspace _config { _env, "config" };
|
||||
|
||||
void _publish_report(Reporter &reporter, Xml_node node)
|
||||
{
|
||||
typedef String<64> Version;
|
||||
Version const version = node.attribute_value("version", Version());
|
||||
|
||||
Reporter::Xml_generator xml(reporter, [&] () {
|
||||
|
||||
if (version.valid())
|
||||
xml.attribute("version", version);
|
||||
|
||||
xml.append(node.content_base(), node.content_size());
|
||||
});
|
||||
}
|
||||
|
||||
Log_message_handler::Message _expected_log_message;
|
||||
|
||||
unsigned const _num_steps = _config.xml().num_sub_nodes();
|
||||
unsigned _curr_step = 0;
|
||||
|
||||
Xml_node _curr_step_xml() const { return _config.xml().sub_node(_curr_step); }
|
||||
|
||||
/*
|
||||
* Handling of state reports generated by init
|
||||
*/
|
||||
Attached_rom_dataspace _init_state { _env, "state" };
|
||||
|
||||
Signal_handler<Main> _init_state_handler {
|
||||
_env.ep(), *this, &Main::_handle_init_state };
|
||||
|
||||
void _handle_init_state()
|
||||
{
|
||||
_init_state.update();
|
||||
_execute_curr_step();
|
||||
}
|
||||
|
||||
void _advance_step()
|
||||
{
|
||||
_curr_step++;
|
||||
|
||||
/* exit when reaching the end of the sequence */
|
||||
if (_curr_step == _num_steps) {
|
||||
_env.parent().exit(0);
|
||||
sleep_forever();
|
||||
}
|
||||
};
|
||||
|
||||
void _execute_curr_step()
|
||||
{
|
||||
for (;;) {
|
||||
Xml_node const step = _curr_step_xml();
|
||||
|
||||
log("step ", _curr_step, " (", step.type(), ")");
|
||||
|
||||
if (step.type() == "expect_log")
|
||||
return;
|
||||
|
||||
if (step.type() == "expect_init_state") {
|
||||
if (xml_matches(step, _init_state.xml())) {
|
||||
_advance_step();
|
||||
continue;
|
||||
} else {
|
||||
warning("init state does not match: ", _init_state.xml());
|
||||
warning("expected condition: ", step);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (step.type() == "init_config") {
|
||||
_publish_report(_init_config_reporter, step);
|
||||
_advance_step();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (step.type() == "message") {
|
||||
typedef String<80> Message;
|
||||
Message const message = step.attribute_value("string", Message());
|
||||
log("\n--- ", message, " ---");
|
||||
_advance_step();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (step.type() == "nop") {
|
||||
_advance_step();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (step.type() == "sleep") {
|
||||
unsigned long const timeout_ms = step.attribute_value("ms", 250UL);
|
||||
_timer.trigger_once(timeout_ms*1000);
|
||||
return;
|
||||
}
|
||||
|
||||
error("unexpected step: ", step);
|
||||
throw Exception();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log_message_handler interface
|
||||
*/
|
||||
Result handle_log_message(Log_message_handler::Message const &message) override
|
||||
{
|
||||
typedef Log_message_handler::Message Message;
|
||||
|
||||
if (_curr_step_xml().type() != "expect_log")
|
||||
return IGNORED;
|
||||
|
||||
Message const expected = _curr_step_xml().attribute_value("string", Message());
|
||||
|
||||
if (message != expected)
|
||||
return IGNORED;
|
||||
|
||||
_advance_step();
|
||||
_execute_curr_step();
|
||||
return EXPECTED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Timer handling
|
||||
*/
|
||||
Signal_handler<Main> _timer_handler { _env.ep(), *this, &Main::_handle_timer };
|
||||
|
||||
void _handle_timer()
|
||||
{
|
||||
if (_curr_step_xml().type() != "sleep") {
|
||||
error("got spurious timeout signal");
|
||||
throw Exception();
|
||||
}
|
||||
|
||||
_advance_step();
|
||||
_execute_curr_step();
|
||||
}
|
||||
|
||||
/*
|
||||
* LOG service provided to init
|
||||
*/
|
||||
Sliced_heap _sliced_heap { _env.ram(), _env.rm() };
|
||||
|
||||
Log_root _log_root { _env.ep(), _sliced_heap, *this };
|
||||
|
||||
|
||||
Main(Env &env) : _env(env)
|
||||
{
|
||||
_timer.sigh(_timer_handler);
|
||||
_init_config_reporter.enabled(true);
|
||||
_init_state.sigh(_init_state_handler);
|
||||
_execute_curr_step();
|
||||
|
||||
_env.parent().announce(_env.ep().manage(_log_root));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env) { static Test::Main main(env); }
|
||||
|
3
repos/os/src/test/init/target.mk
Normal file
3
repos/os/src/test/init/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = test-init
|
||||
SRC_CC = main.cc
|
||||
LIBS += base
|
Loading…
Reference in New Issue
Block a user