summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/TestClient.hpp54
-rw-r--r--tests/connect_disconnect_node_node.ttl36
-rw-r--r--tests/connect_disconnect_node_patch.ttl105
-rw-r--r--tests/connect_disconnect_patch_patch.ttl36
-rw-r--r--tests/copy_node.ttl16
-rw-r--r--tests/create_delete_node.ttl27
-rw-r--r--tests/create_delete_patch.ttl14
-rw-r--r--tests/create_delete_poly_patch.ttl15
-rw-r--r--tests/create_delete_port.ttl53
-rw-r--r--tests/disconnect_all_node.ttl45
-rw-r--r--tests/disconnect_all_port.ttl32
-rw-r--r--tests/duplicate_node.ttl19
-rw-r--r--tests/empty.ingen/main.ttl49
-rw-r--r--tests/empty.ingen/manifest.ttl17
-rw-r--r--tests/enable_graph.ttl15
-rw-r--r--tests/get_engine.ttl7
-rw-r--r--tests/get_node.ttl15
-rw-r--r--tests/get_patch.ttl39
-rw-r--r--tests/get_plugin.ttl7
-rw-r--r--tests/get_plugins.ttl7
-rw-r--r--tests/get_port.ttl15
-rw-r--r--tests/ingen_bench.cpp140
-rw-r--r--tests/ingen_test.cpp223
-rw-r--r--tests/load_graph.ttl8
-rw-r--r--tests/move_node.ttl16
-rw-r--r--tests/move_port.ttl16
-rw-r--r--tests/move_root_port.ttl20
-rw-r--r--tests/poly.ttl25
-rw-r--r--tests/put_audio_in.ttl10
-rw-r--r--tests/save_graph.ttl8
-rw-r--r--tests/set_graph_poly.ttl17
-rw-r--r--tests/set_patch_port_value.ttl17
-rw-r--r--tests/test_utils.hpp40
-rw-r--r--tests/tst_FilePath.cpp103
34 files changed, 1266 insertions, 0 deletions
diff --git a/tests/TestClient.hpp b/tests/TestClient.hpp
new file mode 100644
index 00000000..e80557f1
--- /dev/null
+++ b/tests/TestClient.hpp
@@ -0,0 +1,54 @@
+/*
+ This file is part of Ingen.
+ Copyright 2007-2017 David Robillard <http://drobilla.net/>
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef INGEN_TESTCLIENT_HPP
+#define INGEN_TESTCLIENT_HPP
+
+#include <boost/variant/get.hpp>
+
+#include "ingen/Interface.hpp"
+#include "ingen/Log.hpp"
+
+using namespace ingen;
+
+class TestClient : public ingen::Interface
+{
+public:
+ explicit TestClient(Log& log) : _log(log) {}
+ ~TestClient() {}
+
+ URI uri() const override { return URI("ingen:testClient"); }
+
+ void message(const Message& msg) override {
+ if (const Response* const response = boost::get<Response>(&msg)) {
+ if (response->status != Status::SUCCESS) {
+ _log.error(fmt("error on message %1%: %2% (%3%)\n")
+ % response->id
+ % ingen_status_string(response->status)
+ % response->subject);
+ exit(EXIT_FAILURE);
+ }
+ } else if (const Error* const error = boost::get<Error>(&msg)) {
+ _log.error(fmt("error: %1%\n") % error->message);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+private:
+ Log& _log;
+};
+
+#endif // INGEN_TESTCLIENT_HPP
diff --git a/tests/connect_disconnect_node_node.ttl b/tests/connect_disconnect_node_node.ttl
new file mode 100644
index 00000000..935de482
--- /dev/null
+++ b/tests/connect_disconnect_node_node.ttl
@@ -0,0 +1,36 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/node1> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <http://drobilla.net/plugins/mda/Shepard>
+ ] .
+
+<msg1>
+ a patch:Put ;
+ patch:subject <ingen:/main/node2> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <http://drobilla.net/plugins/mda/Shepard>
+ ] .
+
+<msg2>
+ a patch:Put ;
+ patch:subject <ingen:/main/> ;
+ patch:body [
+ a ingen:Arc ;
+ ingen:tail <ingen:/main/node1/left_out> ;
+ ingen:head <ingen:/main/node2/left_in>
+ ] .
+
+<msg3>
+ a patch:Delete ;
+ patch:body [
+ a ingen:Arc ;
+ ingen:tail <ingen:/main/node1/left_out> ;
+ ingen:head <ingen:/main/node2/left_in>
+ ] .
diff --git a/tests/connect_disconnect_node_patch.ttl b/tests/connect_disconnect_node_patch.ttl
new file mode 100644
index 00000000..77ada2ad
--- /dev/null
+++ b/tests/connect_disconnect_node_patch.ttl
@@ -0,0 +1,105 @@
+@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/amp> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <http://lv2plug.in/plugins/eg-amp>
+ ] .
+
+<msg1>
+ a patch:Put ;
+ patch:subject <ingen:/main/sampler> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <http://lv2plug.in/plugins/eg-sampler>
+ ] .
+
+<msg2>
+ a patch:Put ;
+ patch:subject <ingen:/main/out> ;
+ patch:body [
+ a lv2:OutputPort ,
+ lv2:AudioPort
+ ] .
+
+<msg3>
+ a patch:Put ;
+ patch:subject <ingen:/main/in> ;
+ patch:body [
+ a lv2:InputPort ,
+ lv2:AudioPort
+ ] .
+
+<msg4>
+ a patch:Put ;
+ patch:subject <ingen:/main/control> ;
+ patch:body [
+ a lv2:InputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence
+ ] .
+
+<msg5>
+ a patch:Put ;
+ patch:subject <ingen:/main/gain> ;
+ patch:body [
+ a lv2:InputPort ,
+ lv2:ControlPort
+ ] .
+
+<msg6>
+ a patch:Put ;
+ patch:subject <ingen:/main/> ;
+ patch:body [
+ a ingen:Arc ;
+ ingen:tail <ingen:/main/amp/out> ;
+ ingen:head <ingen:/main/out>
+ ] .
+
+<msg7>
+ a patch:Put ;
+ patch:subject <ingen:/main/> ;
+ patch:body [
+ a ingen:Arc ;
+ ingen:tail <ingen:/main/in> ;
+ ingen:head <ingen:/main/amp/in>
+ ] .
+
+<msg8>
+ a patch:Put ;
+ patch:subject <ingen:/main/> ;
+ patch:body [
+ a ingen:Arc ;
+ ingen:tail <ingen:/main/gain> ;
+ ingen:head <ingen:/main/amp/gain>
+ ] .
+
+<msg9>
+ a patch:Put ;
+ patch:subject <ingen:/main/> ;
+ patch:body [
+ a ingen:Arc ;
+ ingen:tail <ingen:/main/control> ;
+ ingen:head <ingen:/main/sampler/control>
+ ] .
+
+<msg10>
+ a patch:Delete ;
+ patch:body [
+ a ingen:Arc ;
+ ingen:tail <ingen:/main/amp/out> ;
+ ingen:head <ingen:/main/out>
+ ] .
+
+<msg11>
+ a patch:Delete ;
+ patch:body [
+ a ingen:Arc ;
+ ingen:tail <ingen:/main/in> ;
+ ingen:head <ingen:/main/amp/in>
+ ] .
diff --git a/tests/connect_disconnect_patch_patch.ttl b/tests/connect_disconnect_patch_patch.ttl
new file mode 100644
index 00000000..b35a4b55
--- /dev/null
+++ b/tests/connect_disconnect_patch_patch.ttl
@@ -0,0 +1,36 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/in> ;
+ patch:body [
+ a lv2:InputPort ,
+ lv2:AudioPort
+ ] .
+
+<msg1>
+ a patch:Put ;
+ patch:subject <ingen:/main/out> ;
+ patch:body [
+ a lv2:InputPort ,
+ lv2:AudioPort
+ ] .
+
+<msg2>
+ a patch:Put ;
+ patch:subject <ingen:/main/> ;
+ patch:body [
+ a ingen:Arc ;
+ ingen:tail <ingen:/main/in> ;
+ ingen:head <ingen:/main/out>
+ ] .
+
+<msg3>
+ a patch:Delete ;
+ patch:body [
+ a ingen:Arc ;
+ ingen:tail <ingen:/main/in> ;
+ ingen:head <ingen:/main/out>
+ ] .
diff --git a/tests/copy_node.ttl b/tests/copy_node.ttl
new file mode 100644
index 00000000..129ba758
--- /dev/null
+++ b/tests/copy_node.ttl
@@ -0,0 +1,16 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/node> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <http://drobilla.net/plugins/mda/Shepard>
+ ] .
+
+<msg1>
+ a patch:Copy ;
+ patch:subject <ingen:/main/node> ;
+ patch:destination <ingen:/main/copy> .
diff --git a/tests/create_delete_node.ttl b/tests/create_delete_node.ttl
new file mode 100644
index 00000000..81086e69
--- /dev/null
+++ b/tests/create_delete_node.ttl
@@ -0,0 +1,27 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/node> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <http://drobilla.net/plugins/mda/Shepard>
+ ] .
+
+<msg1>
+ a patch:Delete ;
+ patch:subject <ingen:/main/node> .
+
+<msg2>
+ a patch:Put ;
+ patch:subject <ingen:/main/deprecatednode> ;
+ patch:body [
+ a ingen:Block ;
+ ingen:prototype <http://drobilla.net/plugins/mda/Shepard>
+ ] .
+
+<msg3>
+ a patch:Delete ;
+ patch:subject <ingen:/main/deprecatednode> .
diff --git a/tests/create_delete_patch.ttl b/tests/create_delete_patch.ttl
new file mode 100644
index 00000000..2fa72c42
--- /dev/null
+++ b/tests/create_delete_patch.ttl
@@ -0,0 +1,14 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/sub> ;
+ patch:body [
+ a ingen:Graph ;
+ ] .
+
+<msg1>
+ a patch:Delete ;
+ patch:subject <ingen:/main/sub> .
diff --git a/tests/create_delete_poly_patch.ttl b/tests/create_delete_poly_patch.ttl
new file mode 100644
index 00000000..ea0228d4
--- /dev/null
+++ b/tests/create_delete_poly_patch.ttl
@@ -0,0 +1,15 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/sub> ;
+ patch:body [
+ a ingen:Graph ;
+ ingen:polyphony 8 ;
+ ] .
+
+<msg1>
+ a patch:Delete ;
+ patch:subject <ingen:/main/sub> .
diff --git a/tests/create_delete_port.ttl b/tests/create_delete_port.ttl
new file mode 100644
index 00000000..ba26560d
--- /dev/null
+++ b/tests/create_delete_port.ttl
@@ -0,0 +1,53 @@
+@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/audio_in> ;
+ patch:body [
+ a lv2:InputPort ,
+ lv2:AudioPort
+ ] .
+
+<msg1>
+ a patch:Put ;
+ patch:subject <ingen:/main/float_in> ;
+ patch:body [
+ a lv2:InputPort ,
+ lv2:ControlPort
+ ] .
+
+<msg2>
+ a patch:Put ;
+ patch:subject <ingen:/main/cv_in> ;
+ patch:body [
+ a lv2:InputPort ,
+ lv2:CVPort
+ ] .
+
+<msg3>
+ a patch:Put ;
+ patch:subject <ingen:/main/atom_in> ;
+ patch:body [
+ a lv2:InputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence
+ ] .
+
+<msg4>
+ a patch:Delete ;
+ patch:subject <ingen:/main/audio_in> .
+
+<msg5>
+ a patch:Delete ;
+ patch:subject <ingen:/main/float_in> .
+
+<msg6>
+ a patch:Delete ;
+ patch:subject <ingen:/main/cv_in> .
+
+<msg7>
+ a patch:Delete ;
+ patch:subject <ingen:/main/atom_in> .
diff --git a/tests/disconnect_all_node.ttl b/tests/disconnect_all_node.ttl
new file mode 100644
index 00000000..2b65f758
--- /dev/null
+++ b/tests/disconnect_all_node.ttl
@@ -0,0 +1,45 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/node1> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <http://drobilla.net/plugins/mda/Shepard>
+ ] .
+
+<msg1>
+ a patch:Put ;
+ patch:subject <ingen:/main/node2> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <http://drobilla.net/plugins/mda/Shepard>
+ ] .
+
+<msg2>
+ a patch:Put ;
+ patch:subject <ingen:/main/> ;
+ patch:body [
+ a ingen:Arc ;
+ ingen:tail <ingen:/main/node1/left_out> ;
+ ingen:head <ingen:/main/node2/left_in>
+ ] .
+
+<msg3>
+ a patch:Put ;
+ patch:subject <ingen:/main/> ;
+ patch:body [
+ a ingen:Arc ;
+ ingen:tail <ingen:/main/node1/right_out> ;
+ ingen:head <ingen:/main/node2/right_in>
+ ] .
+
+<msg4>
+ a patch:Delete ;
+ patch:subject <ingen:/main/> ;
+ patch:body [
+ a ingen:Arc ;
+ ingen:incidentTo <ingen:/main/node1>
+ ] .
diff --git a/tests/disconnect_all_port.ttl b/tests/disconnect_all_port.ttl
new file mode 100644
index 00000000..5c2d92f2
--- /dev/null
+++ b/tests/disconnect_all_port.ttl
@@ -0,0 +1,32 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/audio_in> ;
+ patch:body [
+ a lv2:InputPort ,
+ lv2:AudioPort
+ ] .
+
+<msg1>
+ a patch:Put ;
+ patch:subject <ingen:/main/node> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <http://drobilla.net/plugins/mda/Shepard>
+ ] .
+
+<msg2>
+ a patch:Put ;
+ patch:subject <ingen:/main/> ;
+ patch:body [
+ a ingen:Arc ;
+ ingen:tail <ingen:/main/audio_in> ;
+ ingen:head <ingen:/main/node/left_in>
+ ] .
+
+<msg3>
+ a patch:Delete ;
+ patch:subject <ingen:/main/audio_in> .
diff --git a/tests/duplicate_node.ttl b/tests/duplicate_node.ttl
new file mode 100644
index 00000000..47d71dfc
--- /dev/null
+++ b/tests/duplicate_node.ttl
@@ -0,0 +1,19 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/node> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <http://drobilla.net/plugins/mda/Shepard>
+ ] .
+
+<msg1>
+ a patch:Put ;
+ patch:subject <ingen:/main/deprecatednode> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <ingen:/main/node>
+ ] .
diff --git a/tests/empty.ingen/main.ttl b/tests/empty.ingen/main.ttl
new file mode 100644
index 00000000..8b60b3aa
--- /dev/null
+++ b/tests/empty.ingen/main.ttl
@@ -0,0 +1,49 @@
+@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+<control>
+ ingen:canvasX 32.0 ;
+ ingen:canvasY 32.0 ;
+ atom:bufferType atom:Sequence ;
+ atom:supports patch:Message ;
+ <http://lv2plug.in/ns/ext/resize-port#minimumSize> 4096 ;
+ lv2:designation lv2:control ;
+ lv2:index 0 ;
+ lv2:name "Control" ;
+ lv2:portProperty lv2:connectionOptional ;
+ lv2:symbol "control" ;
+ a atom:AtomPort ,
+ lv2:InputPort .
+
+<>
+ ingen:polyphony 1 ;
+ <http://lv2plug.in/ns/extensions/ui#ui> ingen:GraphUIGtk2 ;
+ lv2:extensionData <http://lv2plug.in/ns/ext/state#interface> ;
+ lv2:port <control> ,
+ <notify> ;
+ doap:name "empty" ;
+ a ingen:Graph ,
+ lv2:Plugin .
+
+<notify>
+ ingen:canvasX 128.0 ;
+ ingen:canvasY 32.0 ;
+ atom:bufferType atom:Sequence ;
+ atom:supports patch:Message ;
+ <http://lv2plug.in/ns/ext/resize-port#minimumSize> 4096 ;
+ lv2:designation lv2:control ;
+ lv2:index 1 ;
+ lv2:name "Notify" ;
+ lv2:portProperty lv2:connectionOptional ;
+ lv2:symbol "notify" ;
+ a atom:AtomPort ,
+ lv2:OutputPort .
+
diff --git a/tests/empty.ingen/manifest.ttl b/tests/empty.ingen/manifest.ttl
new file mode 100644
index 00000000..fac93265
--- /dev/null
+++ b/tests/empty.ingen/manifest.ttl
@@ -0,0 +1,17 @@
+@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+<main.ttl>
+ lv2:prototype ingen:GraphPrototype ;
+ a ingen:Graph ,
+ lv2:Plugin ;
+ rdfs:seeAlso <main.ttl> .
+
diff --git a/tests/enable_graph.ttl b/tests/enable_graph.ttl
new file mode 100644
index 00000000..f3fa786c
--- /dev/null
+++ b/tests/enable_graph.ttl
@@ -0,0 +1,15 @@
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+
+<msg0>
+ a patch:Set ;
+ patch:subject <ingen:/main> ;
+ patch:property ingen:enabled ;
+ patch:value true .
+
+<msg1>
+ a patch:Set ;
+ patch:subject <ingen:/main> ;
+ patch:property ingen:enabled ;
+ patch:value false .
diff --git a/tests/get_engine.ttl b/tests/get_engine.ttl
new file mode 100644
index 00000000..adfa6a01
--- /dev/null
+++ b/tests/get_engine.ttl
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Get ;
+ patch:subject <ingen:/engine> .
diff --git a/tests/get_node.ttl b/tests/get_node.ttl
new file mode 100644
index 00000000..12977092
--- /dev/null
+++ b/tests/get_node.ttl
@@ -0,0 +1,15 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/node> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <http://drobilla.net/plugins/mda/Shepard>
+ ] .
+
+<msg1>
+ a patch:Get ;
+ patch:subject <ingen:/main/node> .
diff --git a/tests/get_patch.ttl b/tests/get_patch.ttl
new file mode 100644
index 00000000..9ea9e036
--- /dev/null
+++ b/tests/get_patch.ttl
@@ -0,0 +1,39 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/sub> ;
+ patch:body [
+ a ingen:Graph
+ ] .
+
+<msg1>
+ a patch:Put ;
+ patch:subject <ingen:/main/sub/node1> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <http://drobilla.net/plugins/mda/Shepard>
+ ] .
+
+<msg2>
+ a patch:Put ;
+ patch:subject <ingen:/main/sub/node2> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <http://drobilla.net/plugins/mda/Combo>
+ ] .
+
+<msg3>
+ a patch:Put ;
+ patch:subject <ingen:/main/> ;
+ patch:body [
+ a ingen:Arc ;
+ ingen:tail <ingen:/main/sub/node1/left_out> ;
+ ingen:head <ingen:/main/sub/node2/left_in>
+ ] .
+
+<msg4>
+ a patch:Get ;
+ patch:subject <ingen:/main/> .
diff --git a/tests/get_plugin.ttl b/tests/get_plugin.ttl
new file mode 100644
index 00000000..19a9c93c
--- /dev/null
+++ b/tests/get_plugin.ttl
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Get ;
+ patch:subject <http://lv2plug.in/plugins/eg-amp> .
diff --git a/tests/get_plugins.ttl b/tests/get_plugins.ttl
new file mode 100644
index 00000000..8dc91cdf
--- /dev/null
+++ b/tests/get_plugins.ttl
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Get ;
+ patch:subject <ingen:/plugins> .
diff --git a/tests/get_port.ttl b/tests/get_port.ttl
new file mode 100644
index 00000000..4f26e499
--- /dev/null
+++ b/tests/get_port.ttl
@@ -0,0 +1,15 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/in> ;
+ patch:body [
+ a lv2:InputPort ,
+ lv2:AudioPort
+ ] .
+
+<msg1>
+ a patch:Get ;
+ patch:subject <ingen:/main/in> .
diff --git a/tests/ingen_bench.cpp b/tests/ingen_bench.cpp
new file mode 100644
index 00000000..2ac1f5f1
--- /dev/null
+++ b/tests/ingen_bench.cpp
@@ -0,0 +1,140 @@
+/*
+ This file is part of Ingen.
+ Copyright 2007-2017 David Robillard <http://drobilla.net/>
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <cstdlib>
+#include <iostream>
+#include <string>
+#include <thread>
+
+#include "ingen/Clock.hpp"
+#include "ingen/Configuration.hpp"
+#include "ingen/EngineBase.hpp"
+#include "ingen/Forge.hpp"
+#include "ingen/Interface.hpp"
+#include "ingen/Parser.hpp"
+#include "ingen/World.hpp"
+#include "ingen/runtime_paths.hpp"
+#include "ingen/types.hpp"
+
+#include "TestClient.hpp"
+#include "ingen_config.h"
+
+using namespace std;
+using namespace ingen;
+
+World* world = nullptr;
+
+static void
+ingen_try(bool cond, const char* msg)
+{
+ if (!cond) {
+ cerr << "ingen: Error: " << msg << endl;
+ delete world;
+ exit(EXIT_FAILURE);
+ }
+}
+
+static std::string
+real_path(const char* path)
+{
+ char* const c_real_path = realpath(path, nullptr);
+ const std::string result(c_real_path ? c_real_path : "");
+ free(c_real_path);
+ return result;
+}
+
+int
+main(int argc, char** argv)
+{
+ set_bundle_path_from_code((void*)&ingen_try);
+
+ // Create world
+ try {
+ world = new World(nullptr, nullptr, nullptr);
+ world->conf().add(
+ "output", "output", 'O', "File to write benchmark output",
+ ingen::Configuration::SESSION, world->forge().String, Atom());
+ world->load_configuration(argc, argv);
+ } catch (std::exception& e) {
+ cout << "ingen: " << e.what() << endl;
+ return EXIT_FAILURE;
+ }
+
+ // Get mandatory command line arguments
+ const Atom& load = world->conf().option("load");
+ const Atom& out = world->conf().option("output");
+ if (!load.is_valid() || !out.is_valid()) {
+ cerr << "Usage: ingen_bench --load START_GRAPH --output OUT_FILE" << endl;
+ return EXIT_FAILURE;
+ }
+
+ // Get start graph and output file options
+ const std::string start_graph = real_path((const char*)load.get_body());
+ const std::string out_file = (const char*)out.get_body();
+ if (start_graph.empty()) {
+ cerr << "error: initial graph '"
+ << ((const char*)load.get_body())
+ << "' does not exist" << endl;
+ return EXIT_FAILURE;
+ }
+
+ // Load modules
+ ingen_try(world->load_module("server"),
+ "Unable to load server module");
+
+ // Initialise engine
+ ingen_try(bool(world->engine()),
+ "Unable to create engine");
+ world->engine()->init(48000.0, 4096, 4096);
+ world->engine()->activate();
+
+ // Load graph
+ if (!world->parser()->parse_file(world, world->interface().get(), start_graph)) {
+ cerr << "error: failed to load initial graph " << start_graph << endl;
+ return EXIT_FAILURE;
+ }
+ world->engine()->flush_events(std::chrono::milliseconds(20));
+
+ // Run benchmark
+ // TODO: Set up real-time scheduling for this and worker threads
+ ingen::Clock clock;
+ const uint32_t n_test_frames = 1 << 20;
+ const uint32_t block_length = 4096;
+ const uint64_t t_start = clock.now_microseconds();
+ for (uint32_t i = 0; i < n_test_frames; i += block_length) {
+ world->engine()->advance(block_length);
+ world->engine()->run(block_length);
+ //world->engine()->main_iteration();
+ }
+ const uint64_t t_end = clock.now_microseconds();
+
+ // Write log output
+ FILE* log = fopen(out_file.c_str(), "a");
+ if (ftell(log) == 0) {
+ fprintf(log, "# n_threads\trun_time\treal_time\n");
+ }
+ fprintf(log, "%u\t%f\t%f\n",
+ world->conf().option("threads").get<int32_t>(),
+ (t_end - t_start) / 1000000.0,
+ (n_test_frames / 48000.0));
+ fclose(log);
+
+ // Shut down
+ world->engine()->deactivate();
+
+ delete world;
+ return EXIT_SUCCESS;
+}
diff --git a/tests/ingen_test.cpp b/tests/ingen_test.cpp
new file mode 100644
index 00000000..f0e92d30
--- /dev/null
+++ b/tests/ingen_test.cpp
@@ -0,0 +1,223 @@
+/*
+ This file is part of Ingen.
+ Copyright 2007-2017 David Robillard <http://drobilla.net/>
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <signal.h>
+
+#include <cstdlib>
+#include <iostream>
+#include <string>
+
+#include <boost/optional.hpp>
+
+#include "raul/Path.hpp"
+
+#include "serd/serd.h"
+#include "sord/sordmm.hpp"
+#include "sratom/sratom.h"
+
+#include "ingen_config.h"
+
+#include "ingen/AtomReader.hpp"
+#include "ingen/AtomWriter.hpp"
+#include "ingen/Configuration.hpp"
+#include "ingen/Configuration.hpp"
+#include "ingen/EngineBase.hpp"
+#include "ingen/Interface.hpp"
+#include "ingen/Parser.hpp"
+#include "ingen/Properties.hpp"
+#include "ingen/Serialiser.hpp"
+#include "ingen/Store.hpp"
+#include "ingen/URIMap.hpp"
+#include "ingen/World.hpp"
+#include "ingen/filesystem.hpp"
+#include "ingen/runtime_paths.hpp"
+#include "ingen/types.hpp"
+
+#include "TestClient.hpp"
+
+using namespace std;
+using namespace ingen;
+
+World* world = nullptr;
+
+static 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)
+{
+ set_bundle_path_from_code((void*)&ingen_try);
+
+ // Create world
+ try {
+ world = new World(nullptr, nullptr, nullptr);
+ world->load_configuration(argc, argv);
+ } catch (std::exception& e) {
+ cout << "ingen: " << e.what() << endl;
+ return EXIT_FAILURE;
+ }
+
+ // Get mandatory command line arguments
+ const Atom& load = world->conf().option("load");
+ const Atom& execute = world->conf().option("execute");
+ if (!load.is_valid() || !execute.is_valid()) {
+ cerr << "Usage: ingen_test --load START_GRAPH --execute COMMANDS_FILE" << endl;
+ return EXIT_FAILURE;
+ }
+
+ // Get start graph and commands file options
+ const char* load_path = (const char*)load.get_body();
+ char* real_start_graph = realpath(load_path, nullptr);
+ if (!real_start_graph) {
+ cerr << "error: initial graph '" << load_path << "' does not exist" << endl;
+ return EXIT_FAILURE;
+ }
+
+ const std::string start_graph = real_start_graph;
+ const FilePath cmds_file_path = (const char*)execute.get_body();
+ free(real_start_graph);
+
+ // Load modules
+ ingen_try(world->load_module("server"),
+ "Unable to load server module");
+
+ // Initialise engine
+ ingen_try(bool(world->engine()),
+ "Unable to create engine");
+ world->engine()->init(48000.0, 4096, 4096);
+ world->engine()->activate();
+
+ // Load graph
+ if (!world->parser()->parse_file(world, world->interface().get(), start_graph)) {
+ cerr << "error: failed to load initial graph " << start_graph << endl;
+ return EXIT_FAILURE;
+ }
+ world->engine()->flush_events(std::chrono::milliseconds(20));
+
+ // Read commands
+
+ LV2_URID_Map* map = &world->uri_map().urid_map_feature()->urid_map;
+ Sratom* sratom = sratom_new(map);
+
+ sratom_set_object_mode(sratom, SRATOM_OBJECT_MODE_BLANK_SUBJECT);
+
+ LV2_Atom_Forge forge;
+ lv2_atom_forge_init(&forge, map);
+
+ AtomForgeSink out(&forge);
+
+ // AtomReader to read commands from a file and send them to engine
+ AtomReader atom_reader(world->uri_map(),
+ world->uris(),
+ world->log(),
+ *world->interface().get());
+
+ // AtomWriter to serialise responses from the engine
+ SPtr<Interface> client(new TestClient(world->log()));
+
+ world->interface()->set_respondee(client);
+ world->engine()->register_client(client);
+
+ SerdURI cmds_base;
+ SerdNode cmds_file_uri = serd_node_new_file_uri(
+ (const uint8_t*)cmds_file_path.c_str(),
+ nullptr, &cmds_base, true);
+ Sord::Model* cmds = new Sord::Model(*world->rdf_world(),
+ (const char*)cmds_file_uri.buf);
+ SerdEnv* env = serd_env_new(&cmds_file_uri);
+ cmds->load_file(env, SERD_TURTLE, cmds_file_path);
+ Sord::Node nil;
+ int n_events = 0;
+ for (;; ++n_events) {
+ std::string subject_str = (fmt("msg%1%") % n_events).str();
+ Sord::URI subject(*world->rdf_world(), subject_str,
+ (const char*)cmds_file_uri.buf);
+ Sord::Iter iter = cmds->find(subject, nil, nil);
+ if (iter.end()) {
+ break;
+ }
+
+ out.clear();
+ sratom_read(sratom, &forge, world->rdf_world()->c_obj(),
+ cmds->c_obj(), subject.c_obj());
+
+#if 0
+ const LV2_Atom* atom = out.atom();
+ cerr << "READ " << atom->size << " BYTES" << endl;
+ cerr << sratom_to_turtle(
+ sratom,
+ &world->uri_map().urid_unmap_feature()->urid_unmap,
+ (const char*)cmds_file_uri.buf,
+ nullptr, nullptr, atom->type, atom->size, LV2_ATOM_BODY(atom)) << endl;
+#endif
+
+ if (!atom_reader.write(out.atom(), n_events + 1)) {
+ return EXIT_FAILURE;
+ }
+
+ world->engine()->flush_events(std::chrono::milliseconds(20));
+ }
+
+ delete cmds;
+
+ // Save resulting graph
+ auto r = world->store()->find(Raul::Path("/"));
+ const std::string base = cmds_file_path.stem();
+ const std::string out_name = base.substr(0, base.find('.')) + ".out.ingen";
+ const FilePath out_path = filesystem::current_path() / out_name;
+ world->serialiser()->write_bundle(r->second, URI(out_path));
+
+ // Undo every event (should result in a graph identical to the original)
+ for (int i = 0; i < n_events; ++i) {
+ world->interface()->undo();
+ world->engine()->flush_events(std::chrono::milliseconds(20));
+ }
+
+ // Save completely undone graph
+ r = world->store()->find(Raul::Path("/"));
+ const std::string undo_name = base.substr(0, base.find('.')) + ".undo.ingen";
+ const FilePath undo_path = filesystem::current_path() / undo_name;
+ world->serialiser()->write_bundle(r->second, URI(undo_path));
+
+ // Redo every event (should result in a graph identical to the pre-undo output)
+ for (int i = 0; i < n_events; ++i) {
+ world->interface()->redo();
+ world->engine()->flush_events(std::chrono::milliseconds(20));
+ }
+
+ // Save completely redone graph
+ r = world->store()->find(Raul::Path("/"));
+ const std::string redo_name = base.substr(0, base.find('.')) + ".redo.ingen";
+ const FilePath redo_path = filesystem::current_path() / redo_name;
+ world->serialiser()->write_bundle(r->second, URI(redo_path));
+
+ serd_env_free(env);
+ sratom_free(sratom);
+ serd_node_free(&cmds_file_uri);
+
+ // Shut down
+ world->engine()->deactivate();
+
+ delete world;
+ return EXIT_SUCCESS;
+}
diff --git a/tests/load_graph.ttl b/tests/load_graph.ttl
new file mode 100644
index 00000000..f1cd9dda
--- /dev/null
+++ b/tests/load_graph.ttl
@@ -0,0 +1,8 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Copy ;
+ patch:subject <empty.ingen/> ;
+ patch:destination <ingen:/main/> .
diff --git a/tests/move_node.ttl b/tests/move_node.ttl
new file mode 100644
index 00000000..22f6cfb8
--- /dev/null
+++ b/tests/move_node.ttl
@@ -0,0 +1,16 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/node> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <http://drobilla.net/plugins/mda/Shepard>
+ ] .
+
+<msg1>
+ a patch:Move ;
+ patch:subject <ingen:/main/node> ;
+ patch:destination <ingen:/main/tone> .
diff --git a/tests/move_port.ttl b/tests/move_port.ttl
new file mode 100644
index 00000000..4b035037
--- /dev/null
+++ b/tests/move_port.ttl
@@ -0,0 +1,16 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/in> ;
+ patch:body [
+ a lv2:InputPort ,
+ lv2:AudioPort
+ ] .
+
+<msg1>
+ a patch:Move ;
+ patch:subject <ingen:/main/in> ;
+ patch:destination <ingen:/main/input> .
diff --git a/tests/move_root_port.ttl b/tests/move_root_port.ttl
new file mode 100644
index 00000000..2c925767
--- /dev/null
+++ b/tests/move_root_port.ttl
@@ -0,0 +1,20 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/in> ;
+ patch:body [
+ a lv2:InputPort ,
+ lv2:AudioPort
+ ] .
+
+<msg1>
+ a patch:Move ;
+ patch:subject <ingen:/main/in> ;
+ patch:destination <ingen:/main/input> .
+
+<msg2>
+ a patch:Delete ;
+ patch:subject <ingen:/main/input> .
diff --git a/tests/poly.ttl b/tests/poly.ttl
new file mode 100644
index 00000000..a02ba96e
--- /dev/null
+++ b/tests/poly.ttl
@@ -0,0 +1,25 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/node> ;
+ patch:body [
+ a ingen:Block ;
+ lv2:prototype <http://drobilla.net/plugins/mda/Shepard>
+ ] .
+
+<msg1>
+ a patch:Set ;
+ patch:context ingen:internalContext ;
+ patch:subject <ingen:/main/> ;
+ patch:property ingen:polyphony ;
+ patch:value 4 .
+
+<msg2>
+ a patch:Set ;
+ patch:context ingen:externalContext ;
+ patch:subject <ingen:/main/node> ;
+ patch:property ingen:polyphonic ;
+ patch:value true .
diff --git a/tests/put_audio_in.ttl b/tests/put_audio_in.ttl
new file mode 100644
index 00000000..97468cff
--- /dev/null
+++ b/tests/put_audio_in.ttl
@@ -0,0 +1,10 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/in> ;
+ patch:body [
+ a lv2:InputPort ,
+ lv2:AudioPort
+ ] .
diff --git a/tests/save_graph.ttl b/tests/save_graph.ttl
new file mode 100644
index 00000000..5f472d80
--- /dev/null
+++ b/tests/save_graph.ttl
@@ -0,0 +1,8 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+
+<msg0>
+ a patch:Copy ;
+ patch:subject <ingen:/main/> ;
+ patch:destination <file:///tmp/savetest.ingen/> .
diff --git a/tests/set_graph_poly.ttl b/tests/set_graph_poly.ttl
new file mode 100644
index 00000000..0933c3a4
--- /dev/null
+++ b/tests/set_graph_poly.ttl
@@ -0,0 +1,17 @@
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+
+<msg0>
+ a patch:Set ;
+ patch:context ingen:internalContext ;
+ patch:subject <ingen:/main/> ;
+ patch:property ingen:polyphony ;
+ patch:value 4 .
+
+<msg1>
+ a patch:Set ;
+ patch:context ingen:internalContext ;
+ patch:subject <ingen:/main/> ;
+ patch:property ingen:polyphony ;
+ patch:value 1 .
diff --git a/tests/set_patch_port_value.ttl b/tests/set_patch_port_value.ttl
new file mode 100644
index 00000000..07b467b4
--- /dev/null
+++ b/tests/set_patch_port_value.ttl
@@ -0,0 +1,17 @@
+@prefix ingen: <http://drobilla.net/ns/ingen#> .
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+
+<msg0>
+ a patch:Put ;
+ patch:subject <ingen:/main/in> ;
+ patch:body [
+ a lv2:InputPort ,
+ lv2:ControlPort
+ ] .
+
+<msg1>
+ a patch:Set ;
+ patch:subject <ingen:/main/in> ;
+ patch:property ingen:value ;
+ patch:value 0.5 . \ No newline at end of file
diff --git a/tests/test_utils.hpp b/tests/test_utils.hpp
new file mode 100644
index 00000000..a0cc53ac
--- /dev/null
+++ b/tests/test_utils.hpp
@@ -0,0 +1,40 @@
+/*
+ This file is part of Ingen.
+ Copyright 2018 David Robillard <http://drobilla.net/>
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+
+#include <boost/format.hpp>
+
+typedef boost::basic_format<char> fmt;
+
+#define EXPECT_TRUE(value) \
+ if (!(value)) { \
+ std::cerr << (fmt("error: %1%:%2%: !%3%\n") % __FILE__ % \
+ __LINE__ % (#value)); \
+ }
+
+#define EXPECT_FALSE(value) \
+ if ((value)) { \
+ std::cerr << (fmt("error: %1%:%2%: !%3%\n") % __FILE__ % \
+ __LINE__ % (#value)); \
+ }
+
+#define EXPECT_EQ(value, expected) \
+ if (!((value) == (expected))) { \
+ std::cerr << (fmt("error: %1%:%2%: %3% != %4%\n") % __FILE__ % \
+ __LINE__ % (#value) % (#expected)); \
+ std::cerr << "note: actual value: " << value << std::endl; \
+ }
diff --git a/tests/tst_FilePath.cpp b/tests/tst_FilePath.cpp
new file mode 100644
index 00000000..ec0fe593
--- /dev/null
+++ b/tests/tst_FilePath.cpp
@@ -0,0 +1,103 @@
+/*
+ This file is part of Ingen.
+ Copyright 2018 David Robillard <http://drobilla.net/>
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <boost/utility/string_view.hpp>
+
+#include "ingen/FilePath.hpp"
+#include "test_utils.hpp"
+
+using ingen::FilePath;
+
+int
+main(int, char**)
+{
+ EXPECT_EQ(FilePath("/").parent_path(), FilePath("/"));
+
+ EXPECT_TRUE(FilePath("/abs").is_absolute())
+ EXPECT_FALSE(FilePath("/abs").is_relative())
+ EXPECT_EQ(FilePath("/abs").root_name(), FilePath());
+ EXPECT_EQ(FilePath("/abs").root_directory(), FilePath("/"));
+ EXPECT_EQ(FilePath("/abs").root_path(), FilePath("/"));
+ EXPECT_EQ(FilePath("/abs").relative_path(), FilePath("abs"));
+ EXPECT_EQ(FilePath("/abs").parent_path(), FilePath("/"));
+ EXPECT_EQ(FilePath("/abs").filename(), FilePath("abs"));
+ EXPECT_EQ(FilePath("/abs").stem(), FilePath("abs"));
+ EXPECT_EQ(FilePath("/abs").extension(), FilePath());
+
+ EXPECT_FALSE(FilePath("rel").is_absolute())
+ EXPECT_TRUE(FilePath("rel").is_relative())
+ EXPECT_EQ(FilePath("rel").root_name(), FilePath());
+ EXPECT_EQ(FilePath("rel").root_directory(), FilePath());
+ EXPECT_EQ(FilePath("rel").root_path(), FilePath());
+ EXPECT_EQ(FilePath("rel").relative_path(), FilePath());
+ EXPECT_EQ(FilePath("rel").parent_path(), FilePath());
+ EXPECT_EQ(FilePath("rel").filename(), "rel");
+ EXPECT_EQ(FilePath("rel").stem(), "rel");
+ EXPECT_EQ(FilePath("rel").extension(), FilePath());
+
+ EXPECT_FALSE(FilePath("file.txt").is_absolute())
+ EXPECT_TRUE(FilePath("file.txt").is_relative())
+ EXPECT_EQ(FilePath("file.txt").filename(), "file.txt");
+ EXPECT_EQ(FilePath("file.txt").stem(), "file");
+ EXPECT_EQ(FilePath("file.txt").extension(), ".txt");
+
+ EXPECT_TRUE(FilePath("/abs/file.txt").is_absolute())
+ EXPECT_FALSE(FilePath("/abs/file.txt").is_relative())
+ EXPECT_EQ(FilePath("/abs/file.txt").filename(), "file.txt");
+ EXPECT_EQ(FilePath("/abs/file.txt").stem(), "file");
+ EXPECT_EQ(FilePath("/abs/file.txt").extension(), ".txt");
+
+ EXPECT_FALSE(FilePath("rel/file.txt").is_absolute())
+ EXPECT_TRUE(FilePath("rel/file.txt").is_relative())
+ EXPECT_EQ(FilePath("rel/file.txt").filename(), "file.txt");
+ EXPECT_EQ(FilePath("rel/file.txt").stem(), "file");
+ EXPECT_EQ(FilePath("rel/file.txt").extension(), ".txt");
+
+ FilePath path("/x");
+ EXPECT_EQ(path, "/x");
+ path = std::string("/a");
+ EXPECT_EQ(path, "/a");
+
+ path /= FilePath("b");
+ EXPECT_EQ(path, "/a/b");
+
+ path += FilePath("ar");
+ EXPECT_EQ(path, "/a/bar");
+
+ path += std::string("/c");
+ EXPECT_EQ(path, "/a/bar/c");
+
+ path += "a";
+ EXPECT_EQ(path, "/a/bar/ca");
+
+ path += 'r';
+ EXPECT_EQ(path, "/a/bar/car");
+
+ path += boost::string_view("/d");
+ EXPECT_EQ(path, "/a/bar/car/d");
+
+ const FilePath apple("apple");
+ const FilePath zebra("zebra");
+ EXPECT_TRUE(apple == apple);
+ EXPECT_TRUE(apple != zebra);
+ EXPECT_TRUE(apple < zebra);
+ EXPECT_TRUE(apple <= zebra);
+ EXPECT_TRUE(apple <= apple);
+ EXPECT_TRUE(zebra > apple);
+ EXPECT_TRUE(zebra >= apple);
+ EXPECT_TRUE(zebra >= zebra);
+ return 0;
+}