From ec4fd6dd3809a055b66c28f841df277e4cd9f62e Mon Sep 17 00:00:00 2001 From: David Robillard Date: Fri, 18 May 2012 18:03:24 +0000 Subject: Beginnings of a test framework. git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@4427 a436a847-0d15-0410-975c-d299462d15a1 --- ingen/EngineBase.hpp | 7 +++ ingen/shared/Module.hpp | 17 ++++-- src/server/DirectDriver.hpp | 78 ++++++++++++++++++++++++++ src/server/Engine.cpp | 25 +++++++-- src/server/Engine.hpp | 1 + src/server/events/CreatePort.cpp | 112 ++++++++++++++++++++----------------- src/server/wscript | 13 +++++ src/shared/Module.cpp | 33 ----------- src/shared/World.cpp | 65 ++++++++++++---------- src/shared/wscript | 45 +++++++++------ tests/empty.ingen/empty.ttl | 41 ++++++++++++++ tests/empty.ingen/manifest.ttl | 17 ++++++ tests/ingen_test.cpp | 117 +++++++++++++++++++++++++++++++++++++++ wscript | 41 +++++++++++++- 14 files changed, 475 insertions(+), 137 deletions(-) create mode 100644 src/server/DirectDriver.hpp delete mode 100644 src/shared/Module.cpp create mode 100644 tests/empty.ingen/empty.ttl create mode 100644 tests/empty.ingen/manifest.ttl create mode 100644 tests/ingen_test.cpp diff --git a/ingen/EngineBase.hpp b/ingen/EngineBase.hpp index c9acb512..5288eafa 100644 --- a/ingen/EngineBase.hpp +++ b/ingen/EngineBase.hpp @@ -36,6 +36,13 @@ class EngineBase public: virtual ~EngineBase() {} + /** + Initialise the engine for local use (e.g. without a Jack driver). + @param sample_rate Audio sampling rate in Hz. + @param block_length Audio block length (i.e. buffer size) in frames. + */ + virtual void init(double sample_rate, uint32_t block_length) = 0; + /** Activate the engine. */ diff --git a/ingen/shared/Module.hpp b/ingen/shared/Module.hpp index 1552a225..81179426 100644 --- a/ingen/shared/Module.hpp +++ b/ingen/shared/Module.hpp @@ -19,8 +19,6 @@ #include -#include "raul/SharedPtr.hpp" - namespace Ingen { namespace Shared { @@ -32,12 +30,23 @@ class World; * @ingroup IngenShared */ struct Module { - virtual ~Module(); + Module() : library(NULL) {} + virtual ~Module() {} virtual void load(Ingen::Shared::World* world) = 0; virtual void run(Ingen::Shared::World* world) {} - SharedPtr library; + /** Library implementing this module. + * + * This is managed by the World and not this class, since closing the library + * in this destructor could possibly reference code from the library + * afterwards and cause a segfault on exit. + */ + Glib::Module* library; + +private: + Module(const Module& noncopyable); + Module& operator=(const Module& noncopyable); }; } // namespace Shared diff --git a/src/server/DirectDriver.hpp b/src/server/DirectDriver.hpp new file mode 100644 index 00000000..b6d66df6 --- /dev/null +++ b/src/server/DirectDriver.hpp @@ -0,0 +1,78 @@ +/* + This file is part of Ingen. + Copyright 2007-2012 David Robillard + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. + + You should have received a copy of the GNU Affero General Public License + along with Ingen. If not, see . +*/ + +#ifndef INGEN_ENGINE_DIRECT_DRIVER_HPP +#define INGEN_ENGINE_DIRECT_DRIVER_HPP + +#include "Driver.hpp" + +namespace Ingen { +namespace Server { + +/** Driver for running Ingen directly as a library. + * \ingroup engine + */ +class DirectDriver : public Driver { +public: + DirectDriver(double sample_rate, SampleCount block_length) + : _sample_rate(sample_rate) + , _block_length(block_length) + {} + + virtual ~DirectDriver() {} + + virtual void activate() {} + + virtual void deactivate() {} + + virtual EnginePort* create_port(DuplexPort* patch_port) { + return NULL; + } + + virtual EnginePort* engine_port(ProcessContext& context, + const Raul::Path& path) { + return NULL; + } + + virtual void add_port(ProcessContext& context, EnginePort* port) {} + + virtual Raul::Deletable* remove_port(ProcessContext& context, + const Raul::Path& path, + EnginePort** port = NULL) { + return NULL; + } + + virtual SampleCount block_length() const { return _block_length; } + + virtual SampleCount sample_rate() const { return _sample_rate; } + + virtual SampleCount frame_time() const { + return 0; + } + + virtual bool is_realtime() const { + return false; + } + +private: + SampleCount _sample_rate; + SampleCount _block_length; +}; + +} // namespace Server +} // namespace Ingen + +#endif // INGEN_ENGINE_DIRECT_DRIVER_HPP diff --git a/src/server/Engine.cpp b/src/server/Engine.cpp index 69ad435b..9359bacf 100644 --- a/src/server/Engine.cpp +++ b/src/server/Engine.cpp @@ -14,7 +14,6 @@ along with Ingen. If not, see . */ -#include #include #include @@ -31,6 +30,7 @@ #include "Broadcaster.hpp" #include "BufferFactory.hpp" +#include "DirectDriver.hpp" #include "ControlBindings.hpp" #include "Driver.hpp" #include "Engine.hpp" @@ -142,14 +142,24 @@ execute_and_delete_event(ProcessContext& context, Event* ev) delete ev; } +void +Engine::init(double sample_rate, uint32_t block_length) +{ + set_driver(SharedPtr(new DirectDriver(sample_rate, block_length))); +} + bool Engine::activate() { - assert(_driver); + if (!_driver) { + return false; + } + ThreadManager::single_threaded = true; _buffer_factory->set_block_length(_driver->block_length()); + _pre_processor->start(); _message_context.Thread::start(); const Ingen::Shared::URIs& uris = world()->uris(); @@ -226,8 +236,15 @@ Engine::activate() void Engine::deactivate() { - _driver->deactivate(); - _root_patch->deactivate(); + _pre_processor->stop(); + + if (_driver) { + _driver->deactivate(); + } + + if (_root_patch) { + _root_patch->deactivate(); + } ThreadManager::single_threaded = true; } diff --git a/src/server/Engine.hpp b/src/server/Engine.hpp index 8fcb5ce6..edbe33ae 100644 --- a/src/server/Engine.hpp +++ b/src/server/Engine.hpp @@ -65,6 +65,7 @@ public: virtual ~Engine(); // EngineBase methods + virtual void init(double sample_rate, uint32_t block_length); virtual bool activate(); virtual void deactivate(); virtual unsigned run(uint32_t sample_count); diff --git a/src/server/events/CreatePort.cpp b/src/server/events/CreatePort.cpp index d8c183cd..a783512f 100644 --- a/src/server/events/CreatePort.cpp +++ b/src/server/events/CreatePort.cpp @@ -90,73 +90,85 @@ CreatePort::CreatePort(Engine& engine, void CreatePort::pre_process() { - if (_status == UNKNOWN_TYPE || _engine.engine_store()->find_object(_path)) { + if (_status) { Event::pre_process(); return; } - _patch = _engine.engine_store()->find_patch(_path.parent()); + if (_engine.engine_store()->find_object(_path)) { + _status = EXISTS; + Event::pre_process(); + return; + } + + if (!(_patch = _engine.engine_store()->find_patch(_path.parent()))) { + _status = PARENT_NOT_FOUND; + Event::pre_process(); + return; + } const Ingen::Shared::URIs& uris = _engine.world()->uris(); - if (_patch != NULL) { - assert(_patch->path() == _path.parent()); - - size_t buffer_size = _engine.buffer_factory()->default_buffer_size(_buffer_type); - - const uint32_t old_num_ports = (_patch->external_ports()) - ? _patch->external_ports()->size() - : 0; - - Resource::Properties::const_iterator index_i = _properties.find(uris.lv2_index); - if (index_i == _properties.end()) { - index_i = _properties.insert( - std::make_pair(uris.lv2_index, - _engine.world()->forge().make(int32_t(old_num_ports)))); - } else if (index_i->second.type() != uris.forge.Int - || index_i->second.get_int32() != static_cast(old_num_ports)) { - Event::pre_process(); - _status = BAD_INDEX; - return; - } + assert(_patch->path() == _path.parent()); - Resource::Properties::const_iterator poly_i = _properties.find(uris.ingen_polyphonic); - bool polyphonic = (poly_i != _properties.end() && - poly_i->second.type() == uris.forge.Bool && - poly_i->second.get_bool()); + size_t buffer_size = _engine.buffer_factory()->default_buffer_size(_buffer_type); - _patch_port = _patch->create_port(*_engine.buffer_factory(), _path.symbol(), - _port_type, _buffer_type, buffer_size, - _is_output, polyphonic); + const uint32_t old_num_ports = (_patch->external_ports()) + ? _patch->external_ports()->size() + : 0; - _patch_port->properties().insert(_properties.begin(), _properties.end()); + Resource::Properties::const_iterator index_i = _properties.find(uris.lv2_index); + if (index_i == _properties.end()) { + index_i = _properties.insert( + std::make_pair(uris.lv2_index, + _engine.world()->forge().make(int32_t(old_num_ports)))); + } else if (index_i->second.type() != uris.forge.Int + || index_i->second.get_int32() != static_cast(old_num_ports)) { + _status = BAD_INDEX; + Event::pre_process(); + return; + } - assert(index_i->second == _engine.world()->forge().make((int)_patch_port->index())); + Resource::Properties::const_iterator poly_i = _properties.find(uris.ingen_polyphonic); + bool polyphonic = (poly_i != _properties.end() && + poly_i->second.type() == uris.forge.Bool && + poly_i->second.get_bool()); - if (_patch_port) { - if (_is_output) - _patch->add_output(new Raul::List::Node(_patch_port)); - else - _patch->add_input(new Raul::List::Node(_patch_port)); + if (!(_patch_port = _patch->create_port( + *_engine.buffer_factory(), _path.symbol(), + _port_type, _buffer_type, buffer_size, _is_output, polyphonic))) { + _status = CREATION_FAILED; + Event::pre_process(); + return; + } - if (_patch->external_ports()) - _ports_array = new Raul::Array(old_num_ports + 1, *_patch->external_ports(), NULL); - else - _ports_array = new Raul::Array(old_num_ports + 1, NULL); + _patch_port->properties().insert(_properties.begin(), _properties.end()); - _ports_array->at(old_num_ports) = _patch_port; - _engine.engine_store()->add(_patch_port); + assert(index_i->second == _engine.world()->forge().make((int)_patch_port->index())); - if (!_patch->parent()) { - _engine_port = _engine.driver()->create_port( - dynamic_cast(_patch_port)); - } + if (_patch_port) { + if (_is_output) + _patch->add_output(new Raul::List::Node(_patch_port)); + else + _patch->add_input(new Raul::List::Node(_patch_port)); + + if (_patch->external_ports()) + _ports_array = new Raul::Array(old_num_ports + 1, *_patch->external_ports(), NULL); + else + _ports_array = new Raul::Array(old_num_ports + 1, NULL); + + _ports_array->at(old_num_ports) = _patch_port; + _engine.engine_store()->add(_patch_port); + + if (!_patch->parent()) { + _engine_port = _engine.driver()->create_port( + dynamic_cast(_patch_port)); + } - assert(_ports_array->size() == _patch->num_ports_non_rt()); + assert(_ports_array->size() == _patch->num_ports_non_rt()); - } else { - _status = CREATION_FAILED; - } + } else { + _status = CREATION_FAILED; } _update = _patch_port->properties(); diff --git a/src/server/wscript b/src/server/wscript index 4cb61be5..8f505983 100644 --- a/src/server/wscript +++ b/src/server/wscript @@ -60,6 +60,19 @@ def build(bld): core_libs = 'GLIBMM GTHREAD LV2 LILV RAUL SORD' autowaf.use_lib(bld, obj, core_libs) + if bld.env['BUILD_TESTS']: + obj = bld(features = 'cxx cxxshlib', + source = core_source, + export_includes = ['../..'], + includes = ['.', '../..'], + name = 'libingen_server_profiled', + target = 'ingen_server_profiled', + install_path = '${LIBDIR}', + use = 'libingen_shared_profiled', + lib = bld.env['INGEN_TEST_LIBS'], + cxxflags = bld.env['INGEN_TEST_CXXFLAGS']) + autowaf.use_lib(bld, obj, core_libs) + if bld.is_defined('HAVE_JACK'): obj = bld(features = 'cxx cxxshlib', source = 'JackDriver.cpp ingen_jack.cpp', diff --git a/src/shared/Module.cpp b/src/shared/Module.cpp deleted file mode 100644 index 9ef8f0d6..00000000 --- a/src/shared/Module.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2012 David Robillard - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see . -*/ - -#include - -#include "raul/log.hpp" -#include "raul/SharedPtr.hpp" - -#include "ingen/shared/Module.hpp" - -namespace Ingen { -namespace Shared { - -Module::~Module() -{ - Raul::info("[Module] ")(Raul::fmt("Unloading %1%\n") % library->get_name()); -} - -} // namespace Shared -} // namespace Ingen diff --git a/src/shared/World.cpp b/src/shared/World.cpp index b71ea8c8..608a8409 100644 --- a/src/shared/World.cpp +++ b/src/shared/World.cpp @@ -50,7 +50,7 @@ namespace Shared { * * \param name The base name of the module, e.g. "ingen_serialisation" */ -static SharedPtr +Glib::Module* ingen_load_module(const string& name) { Glib::Module* module = NULL; @@ -61,15 +61,14 @@ ingen_load_module(const string& name) if (module_path_found) { string dir; istringstream iss(module_path); - while (getline(iss, dir, ':')) { + while (getline(iss, dir, G_SEARCHPATH_SEPARATOR)) { string filename = Shared::module_path(name, dir); if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) { - module = new Glib::Module(filename, Glib::MODULE_BIND_LAZY); + module = new Glib::Module(filename); if (*module) { LOG(Raul::info)(Raul::fmt("Loading %1%\n") % filename); - return SharedPtr(module); + return module; } else { - delete module; Raul::error << Glib::Module::get_last_error() << endl; } } @@ -77,23 +76,20 @@ ingen_load_module(const string& name) } // Try default directory if not found - module = new Glib::Module(Shared::module_path(name), Glib::MODULE_BIND_LAZY); + module = new Glib::Module(Shared::module_path(name)); - // FIXME: SEGV on exit without this - module->make_resident(); - - if (*module) { + if (module) { LOG(Raul::info)(Raul::fmt("Loading %1%\n") % Shared::module_path(name)); - return SharedPtr(module); + return module; } else if (!module_path_found) { LOG(Raul::error)(Raul::fmt("Unable to find %1% (%2%)\n") % name % Glib::Module::get_last_error()); - return SharedPtr(); + return NULL; } else { LOG(Raul::error)(Raul::fmt("Unable to load %1% from %2% (%3%)\n") % name % module_path % Glib::Module::get_last_error()); LOG(Raul::error)("Is Ingen installed?\n"); - return SharedPtr(); + return NULL; } } @@ -131,28 +127,39 @@ public: rdf_world->add_prefix("xsd", "http://www.w3.org/2001/XMLSchema#"); } - virtual ~Impl() + ~Impl() { serialiser.reset(); parser.reset(); - + interface.reset(); engine.reset(); store.reset(); - modules.clear(); interface_factories.clear(); script_runners.clear(); - lilv_world_free(lilv_world); - delete rdf_world; delete lv2_features; delete uris; delete forge; delete uri_map; + + lilv_world_free(lilv_world); + + + for (Modules::iterator i = modules.begin(); i != modules.end(); ++i) { + // Keep a reference to the library + Glib::Module* lib = i->second->library; + + // Destroy the Ingen module + delete i->second; + + // Now all references to library code should be done, close it + delete lib; + } } - typedef std::map< const std::string, SharedPtr > Modules; + typedef std::map Modules; Modules modules; typedef std::map InterfaceFactories; @@ -189,7 +196,6 @@ World::World(int& argc, World::~World() { - unload_modules(); delete _impl; } @@ -225,18 +231,21 @@ World::load_module(const char* name) LOG(Raul::info)(Raul::fmt("Module `%1%' already loaded\n") % name); return true; } - SharedPtr lib = ingen_load_module(name); + Glib::Module* lib = ingen_load_module(name); Ingen::Shared::Module* (*module_load)() = NULL; if (lib && lib->get_symbol("ingen_module_load", (void*&)module_load)) { Module* module = module_load(); - module->library = lib; - module->load(this); - _impl->modules.insert(make_pair(string(name), module)); - return true; - } else { - LOG(Raul::error)(Raul::fmt("Failed to load module `%1%'\n") % name); - return false; + if (module) { + module->library = lib; + module->load(this); + _impl->modules.insert(make_pair(string(name), module)); + return true; + } } + + LOG(Raul::error)(Raul::fmt("Failed to load module `%1%'\n") % name); + delete lib; + return false; } bool diff --git a/src/shared/wscript b/src/shared/wscript index 15e6097e..751951e1 100644 --- a/src/shared/wscript +++ b/src/shared/wscript @@ -1,8 +1,25 @@ #!/usr/bin/env python from waflib.extras import autowaf as autowaf +sources = [ + 'AtomReader.cpp', + 'AtomWriter.cpp', + 'Builder.cpp', + 'ClashAvoider.cpp', + 'Configuration.cpp', + 'Forge.cpp', + 'LV2Features.cpp', + 'ResourceImpl.cpp', + 'Store.cpp', + 'URIMap.cpp', + 'URIs.cpp', + 'World.cpp', + 'runtime_paths.cpp', +] + def build(bld): obj = bld(features = 'cxx cxxshlib', + source = sources, export_includes = ['../..'], includes = ['../..'], name = 'libingen_shared', @@ -12,19 +29,15 @@ def build(bld): lib = ['dl']) autowaf.use_lib(bld, obj, 'GLIBMM LV2 LILV RAUL SORD LV2_MIDI') - obj.source = ''' - AtomReader.cpp - AtomWriter.cpp - Builder.cpp - ClashAvoider.cpp - Configuration.cpp - Forge.cpp - LV2Features.cpp - Module.cpp - ResourceImpl.cpp - Store.cpp - URIMap.cpp - URIs.cpp - World.cpp - runtime_paths.cpp - ''' + if bld.env['BUILD_TESTS']: + obj = bld(features = 'cxx cxxshlib', + source = sources, + export_includes = ['../..'], + includes = ['../..'], + name = 'libingen_shared_profiled', + target = 'ingen_shared_profiled', + install_path = '', + lib = ['dl'] + bld.env['INGEN_TEST_LIBS'], + cxxflags = bld.env['INGEN_TEST_CXXFLAGS']) + autowaf.use_lib(bld, obj, 'GLIBMM LV2 LILV RAUL SORD LV2_MIDI') + diff --git a/tests/empty.ingen/empty.ttl b/tests/empty.ingen/empty.ttl new file mode 100644 index 00000000..ac905a2e --- /dev/null +++ b/tests/empty.ingen/empty.ttl @@ -0,0 +1,41 @@ +@prefix rdf: . +@prefix atom: . +@prefix patch: . +@prefix doap: . +@prefix ingen: . +@prefix lv2: . +@prefix lv2ev: . +@prefix midi: . +@prefix owl: . +@prefix rdfs: . +@prefix xsd: . + + + ingen:polyphonic false ; + atom:bufferType atom:Sequence ; + lv2:index 0 ; + lv2:name "Control" ; + lv2:portProperty lv2:connectionOptional ; + lv2:symbol "control_in" ; + a atom:AtomPort , + lv2:InputPort . + + + ingen:polyphonic false ; + atom:bufferType atom:Sequence ; + lv2:index 1 ; + lv2:name "Control" ; + lv2:symbol "control_out" ; + a atom:AtomPort , + lv2:OutputPort . + +<> + ingen:polyphony 1 ; + ingen:PatchUIGtk2 ; + lv2:extensionData ; + lv2:port , + ; + lv2:symbol "empty" ; + doap:name "empty" ; + a ingen:Patch , + lv2:Plugin . diff --git a/tests/empty.ingen/manifest.ttl b/tests/empty.ingen/manifest.ttl new file mode 100644 index 00000000..5aec4db2 --- /dev/null +++ b/tests/empty.ingen/manifest.ttl @@ -0,0 +1,17 @@ +@prefix rdf: . +@prefix atom: . +@prefix patch: . +@prefix doap: . +@prefix ingen: . +@prefix lv2: . +@prefix lv2ev: . +@prefix midi: . +@prefix owl: . +@prefix rdfs: . +@prefix xsd: . + + + lv2:binary ; + a ingen:Patch , + lv2:Plugin ; + rdfs:seeAlso . diff --git a/tests/ingen_test.cpp b/tests/ingen_test.cpp new file mode 100644 index 00000000..0a882023 --- /dev/null +++ b/tests/ingen_test.cpp @@ -0,0 +1,117 @@ +/* + This file is part of Ingen. + Copyright 2007-2012 David Robillard + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. + + You should have received a copy of the GNU Affero General Public License + along with Ingen. If not, see . +*/ + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include "raul/Configuration.hpp" +#include "raul/Path.hpp" +#include "raul/SharedPtr.hpp" +#include "raul/Thread.hpp" +#include "raul/log.hpp" + +#include "serd/serd.h" +#include "sord/sordmm.hpp" + +#include "ingen_config.h" + +#include "ingen/EngineBase.hpp" +#include "ingen/Interface.hpp" +#include "ingen/serialisation/Parser.hpp" +#include "ingen/shared/Configuration.hpp" +#include "ingen/shared/World.hpp" +#include "ingen/shared/runtime_paths.hpp" +#include "ingen/client/ThreadedSigClientInterface.hpp" +#ifdef WITH_BINDINGS +#include "bindings/ingen_bindings.hpp" +#endif + +using namespace std; +using namespace Ingen; + +Ingen::Shared::World* world = NULL; + +void +ingen_try(bool cond, const char* msg) +{ + if (!cond) { + cerr << "ingen: Error: " << msg << endl; + delete world; + exit(EXIT_FAILURE); + } +} + +int +main(int argc, char** argv) +{ + Glib::thread_init(); + Shared::set_bundle_path_from_code((void*)&main); + + // Create world + try { + world = new Ingen::Shared::World(argc, argv, NULL, NULL); + if (argc <= 1) { + world->conf().print_usage("ingen", cout); + return EXIT_FAILURE; + } else if (world->conf().option("help").get_bool()) { + world->conf().print_usage("ingen", cout); + return EXIT_SUCCESS; + } + } catch (std::exception& e) { + cout << "ingen: " << e.what() << endl; + return EXIT_FAILURE; + } + + // Run engine + //SharedPtr engine_interface; + ingen_try(world->load_module("server_profiled"), + "Unable to load server module"); + + //ingen_try(world->load_module("client"), + // "Unable to load client module"); + + ingen_try(world->engine(), + "Unable to create engine"); + + ingen_try(world->load_module("serialisation_profiled"), + "Unable to load serialisation module"); + + world->engine()->init(48000.0, 4096); + world->engine()->activate(); + + world->parser()->parse_file(world, world->interface().get(), argv[1]); + while (world->engine()->run(4096)) { + world->engine()->main_iteration(); + } + + // Shut down + if (world->engine()) + world->engine()->deactivate(); + + delete world; + return 0; +} + diff --git a/wscript b/wscript index cefc8745..0229608c 100644 --- a/wscript +++ b/wscript @@ -31,6 +31,8 @@ def options(opt): help="Do not build JACK session support") opt.add_option('--no-socket', action='store_true', default=False, dest='no_socket', help="Do not build Socket interface") + opt.add_option('--test', action='store_true', default=False, dest='build_tests', + help="Build unit tests") opt.add_option('--log-debug', action='store_true', default=False, dest='log_debug', help="Print debugging output") @@ -78,6 +80,19 @@ def configure(conf): if conf.is_defined('HAVE_NEW_JACK'): autowaf.define(conf, 'INGEN_JACK_SESSION', 1) + conf.env['BUILD_TESTS'] = Options.options.build_tests + if conf.env['BUILD_TESTS']: + conf.check_cxx(lib='gcov', + define_name='HAVE_GCOV', + mandatory=False) + + if conf.is_defined('HAVE_GCOV'): + conf.env['INGEN_TEST_LIBS'] = ['gcov'] + conf.env['INGEN_TEST_CXXFLAGS'] = ['-fprofile-arcs', '-ftest-coverage'] + else: + conf.env['INGEN_TEST_LIBS'] = [] + conf.env['INGEN_TEST_CXXFLAGS'] = [] + # Check for posix_memalign (OSX, amazingly, doesn't have it) conf.check(function_name='posix_memalign', defines='_POSIX_SOURCE=1', @@ -142,12 +157,23 @@ def build(bld): obj = bld(features = 'c cxx cxxprogram', source = 'src/ingen/main.cpp', target = bld.path.get_bld().make_node('ingen'), - includes = ['.', '../..'], - defines = 'VERSION="' + bld.env['INGEN_VERSION'] + '"', + includes = ['.'], use = 'libingen_shared', install_path = '${BINDIR}') autowaf.use_lib(bld, obj, 'GTHREAD GLIBMM SORD RAUL LILV INGEN LV2') + # Test program + if bld.env['BUILD_TESTS']: + obj = bld(features = 'cxx cxxprogram', + source = 'tests/ingen_test.cpp', + target = 'tests/ingen_test', + includes = ['.'], + use = 'libingen_shared_profiled', + install_path = '', + lib = bld.env['INGEN_TEST_LIBS'], + cxxflags = bld.env['INGEN_TEST_CXXFLAGS']) + autowaf.use_lib(bld, obj, 'GTHREAD GLIBMM SORD RAUL LILV INGEN LV2') + bld.install_files('${DATADIR}/applications', 'src/ingen/ingen.desktop') bld.install_files('${BINDIR}', 'scripts/ingenish', chmod=Utils.O755) @@ -174,3 +200,14 @@ def build(bld): def lint(ctx): subprocess.call('cpplint.py --filter=-whitespace/comments,-whitespace/tab,-whitespace/braces,-whitespace/labels,-build/header_guard,-readability/casting,-readability/todo,-build/namespaces,-whitespace/line_length,-runtime/rtti,-runtime/references,-whitespace/blank_line,-runtime/sizeof,-readability/streams,-whitespace/operators,-whitespace/parens,-build/include `find -name *.cpp -or -name *.hpp`', shell=True) + +def test(ctx): + os.environ['PATH'] = 'tests' + os.pathsep + os.getenv('PATH') + os.environ['LD_LIBRARY_PATH'] = os.path.join('src', 'shared') + os.environ['INGEN_MODULE_PATH'] = os.pathsep.join([ + os.path.join('src', 'server') , + os.path.join('src', 'serialisation')]) + + autowaf.pre_test(ctx, APPNAME, dirs=['.', 'src', 'tests']) + autowaf.run_tests(ctx, APPNAME, ['ingen_test ../tests/empty.ingen'], dirs=['.', 'src', 'tests']) + autowaf.post_test(ctx, APPNAME, dirs=['.', 'src', 'tests']) -- cgit v1.2.1