summaryrefslogtreecommitdiffstats
path: root/test/lilv_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/lilv_test.c')
-rw-r--r--test/lilv_test.c2299
1 files changed, 2299 insertions, 0 deletions
diff --git a/test/lilv_test.c b/test/lilv_test.c
new file mode 100644
index 0000000..b55f3e9
--- /dev/null
+++ b/test/lilv_test.c
@@ -0,0 +1,2299 @@
+/*
+ Copyright 2007-2016 David Robillard <http://drobilla.net>
+ Copyright 2008 Krzysztof Foltman
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#define _POSIX_C_SOURCE 200809L /* for setenv */
+#define _XOPEN_SOURCE 600 /* for mkstemp */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+# include <direct.h>
+# include <io.h>
+# define mkdir(path, flags) _mkdir(path)
+# define setenv(n, v, r) SetEnvironmentVariable((n), (v))
+# define unsetenv(n) SetEnvironmentVariable((n), NULL)
+# define mkstemp(pat) _mktemp(pat)
+#else
+# include <dirent.h>
+# include <unistd.h>
+#endif
+
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#include "lv2/lv2plug.in/ns/ext/presets/presets.h"
+#include "lv2/lv2plug.in/ns/ext/state/state.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+
+#define TEST_PATH_MAX 1024
+
+#if defined(__APPLE__)
+# define SHLIB_EXT ".dylib"
+#elif defined(_WIN32)
+# define SHLIB_EXT ".dll"
+#else
+# define SHLIB_EXT ".so"
+#endif
+
+static char bundle_dir_name[TEST_PATH_MAX + sizeof("/.lv2/lilv-test.lv2")];
+static char bundle_dir_uri[sizeof(bundle_dir_name) + sizeof("file:///")];
+static char manifest_name[sizeof(bundle_dir_name) + sizeof("/manifest.ttl")];
+static char content_name[sizeof(bundle_dir_name) + sizeof("plugin.ttl")];
+
+static LilvWorld* world;
+
+int test_count = 0;
+int error_count = 0;
+
+static void
+delete_bundle(void)
+{
+ unlink(content_name);
+ unlink(manifest_name);
+ remove(bundle_dir_name);
+}
+
+static void
+init_tests(void)
+{
+ snprintf(bundle_dir_name, sizeof(bundle_dir_name), "%s/.lv2/lilv-test.lv2",
+ getenv("HOME"));
+ lilv_mkdir_p(bundle_dir_name);
+
+ snprintf(bundle_dir_uri, sizeof(bundle_dir_uri), "file://%s/",
+ bundle_dir_name);
+ snprintf(manifest_name, sizeof(manifest_name), "%s/manifest.ttl",
+ bundle_dir_name);
+ snprintf(content_name, sizeof(content_name), "%s/plugin.ttl",
+ bundle_dir_name);
+
+ delete_bundle();
+}
+
+static void
+fatal_error(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ fprintf(stderr, "error: ");
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ exit(1);
+}
+
+static void
+write_file(const char* name, const char* content)
+{
+ FILE* f = fopen(name, "w");
+ size_t len = strlen(content);
+ if (fwrite(content, 1, len, f) != len) {
+ fatal_error("Failed to write to file '%s' (%s)\n",
+ name, strerror(errno));
+ }
+ fclose(f);
+}
+
+static int
+init_world(void)
+{
+ world = lilv_world_new();
+ return world != NULL;
+}
+
+static int
+load_all_bundles(void)
+{
+ if (!init_world()) {
+ return 0;
+ }
+ lilv_world_load_all(world);
+ return 1;
+}
+
+static void
+create_bundle(const char* manifest, const char* content)
+{
+ if (mkdir(bundle_dir_name, 0700) && errno != EEXIST) {
+ fatal_error("Failed to create directory '%s' (%s)\n",
+ bundle_dir_name, strerror(errno));
+ }
+ write_file(manifest_name, manifest);
+ write_file(content_name, content);
+}
+
+static int
+start_bundle(const char* manifest, const char* content)
+{
+ create_bundle(manifest, content);
+ return load_all_bundles();
+}
+
+static void
+unload_bundle(void)
+{
+ if (world) {
+ lilv_world_free(world);
+ }
+ world = NULL;
+}
+
+static void
+cleanup(void)
+{
+ delete_bundle();
+}
+
+/*****************************************************************************/
+
+#define TEST_CASE(name) { #name, test_##name }
+#define TEST_ASSERT(check) do {\
+ test_count++;\
+ if (!(check)) {\
+ error_count++;\
+ fprintf(stderr, "lilv_test.c:%d: error: test `%s' failed\n", __LINE__, #check);\
+ abort();\
+ }\
+} while (0)
+
+typedef int (*TestFunc)(void);
+
+struct TestCase {
+ const char* title;
+ TestFunc func;
+};
+
+#define PREFIX_ATOM "@prefix atom: <http://lv2plug.in/ns/ext/atom#> . \n"
+#define PREFIX_LINE "@prefix : <http://example.org/> .\n"
+#define PREFIX_LV2 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
+#define PREFIX_LV2EV "@prefix lv2ev: <http://lv2plug.in/ns/ext/event#> . \n"
+#define PREFIX_LV2UI "@prefix lv2ui: <http://lv2plug.in/ns/extensions/ui#> .\n"
+#define PREFIX_RDF "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
+#define PREFIX_RDFS "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
+#define PREFIX_FOAF "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"
+#define PREFIX_DOAP "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"
+#define PREFIX_PSET "@prefix pset: <http://lv2plug.in/ns/ext/presets#> .\n"
+
+#define MANIFEST_PREFIXES PREFIX_LINE PREFIX_LV2 PREFIX_RDFS
+#define BUNDLE_PREFIXES PREFIX_ATOM PREFIX_LINE PREFIX_LV2 PREFIX_RDF PREFIX_RDFS PREFIX_FOAF PREFIX_DOAP PREFIX_PSET
+#define PLUGIN_NAME(name) "doap:name \"" name "\""
+#define LICENSE_GPL "doap:license <http://usefulinc.com/doap/licenses/gpl>"
+
+static const char* uris_plugin = "http://example.org/plug";
+static LilvNode* plugin_uri_value;
+static LilvNode* plugin2_uri_value;
+
+/*****************************************************************************/
+
+static void
+init_uris(void)
+{
+ plugin_uri_value = lilv_new_uri(world, uris_plugin);
+ plugin2_uri_value = lilv_new_uri(world, "http://example.org/foobar");
+ TEST_ASSERT(plugin_uri_value);
+ TEST_ASSERT(plugin2_uri_value);
+}
+
+static void
+cleanup_uris(void)
+{
+ lilv_node_free(plugin2_uri_value);
+ lilv_node_free(plugin_uri_value);
+ plugin2_uri_value = NULL;
+ plugin_uri_value = NULL;
+}
+
+/*****************************************************************************/
+
+static int
+test_value(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"Foo\" ; "
+ "] .")) {
+ return 0;
+ }
+
+ init_uris();
+
+ LilvNode* uval = lilv_new_uri(world, "http://example.org");
+ LilvNode* sval = lilv_new_string(world, "Foo");
+ LilvNode* ival = lilv_new_int(world, 42);
+ LilvNode* fval = lilv_new_float(world, 1.6180);
+
+ TEST_ASSERT(lilv_node_is_uri(uval));
+ TEST_ASSERT(lilv_node_is_string(sval));
+ TEST_ASSERT(lilv_node_is_int(ival));
+ TEST_ASSERT(lilv_node_is_float(fval));
+
+ TEST_ASSERT(!lilv_node_is_literal(NULL));
+ TEST_ASSERT(!lilv_node_is_literal(uval));
+ TEST_ASSERT(lilv_node_is_literal(sval));
+ TEST_ASSERT(lilv_node_is_literal(ival));
+ TEST_ASSERT(lilv_node_is_literal(fval));
+ TEST_ASSERT(!lilv_node_get_path(fval, NULL));
+
+ TEST_ASSERT(!strcmp(lilv_node_as_uri(uval), "http://example.org"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(sval), "Foo"));
+ TEST_ASSERT(lilv_node_as_int(ival) == 42);
+ TEST_ASSERT(fabs(lilv_node_as_float(fval) - 1.6180) < FLT_EPSILON);
+ TEST_ASSERT(isnan(lilv_node_as_float(sval)));
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+ TEST_ASSERT(!strcmp(lilv_uri_to_path("file:///foo"), "/foo"));
+
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+# pragma GCC diagnostic pop
+#endif
+
+ LilvNode* loc_abs = lilv_new_file_uri(world, NULL, "/foo/bar");
+ LilvNode* loc_rel = lilv_new_file_uri(world, NULL, "foo");
+ LilvNode* host_abs = lilv_new_file_uri(world, "host", "/foo/bar");
+ LilvNode* host_rel = lilv_new_file_uri(world, "host", "foo");
+
+ TEST_ASSERT(!strcmp(lilv_node_as_uri(loc_abs), "file:///foo/bar"));
+ TEST_ASSERT(!strncmp(lilv_node_as_uri(loc_rel), "file:///", 8));
+ TEST_ASSERT(!strcmp(lilv_node_as_uri(host_abs), "file://host/foo/bar"));
+ TEST_ASSERT(!strncmp(lilv_node_as_uri(host_rel), "file://host/", 12));
+
+ lilv_node_free(host_rel);
+ lilv_node_free(host_abs);
+ lilv_node_free(loc_rel);
+ lilv_node_free(loc_abs);
+
+ char* tok = lilv_node_get_turtle_token(uval);
+ TEST_ASSERT(!strcmp(tok, "<http://example.org>"));
+ lilv_free(tok);
+ tok = lilv_node_get_turtle_token(sval);
+ TEST_ASSERT(!strcmp(tok, "Foo"));
+ lilv_free(tok);
+ tok = lilv_node_get_turtle_token(ival);
+ TEST_ASSERT(!strcmp(tok, "42"));
+ lilv_free(tok);
+ tok = lilv_node_get_turtle_token(fval);
+ TEST_ASSERT(!strncmp(tok, "1.6180", 6));
+ lilv_free(tok);
+
+ LilvNode* uval_e = lilv_new_uri(world, "http://example.org");
+ LilvNode* sval_e = lilv_new_string(world, "Foo");
+ LilvNode* ival_e = lilv_new_int(world, 42);
+ LilvNode* fval_e = lilv_new_float(world, 1.6180);
+ LilvNode* uval_ne = lilv_new_uri(world, "http://no-example.org");
+ LilvNode* sval_ne = lilv_new_string(world, "Bar");
+ LilvNode* ival_ne = lilv_new_int(world, 24);
+ LilvNode* fval_ne = lilv_new_float(world, 3.14159);
+
+ TEST_ASSERT(lilv_node_equals(uval, uval_e));
+ TEST_ASSERT(lilv_node_equals(sval, sval_e));
+ TEST_ASSERT(lilv_node_equals(ival, ival_e));
+ TEST_ASSERT(lilv_node_equals(fval, fval_e));
+
+ TEST_ASSERT(!lilv_node_equals(uval, uval_ne));
+ TEST_ASSERT(!lilv_node_equals(sval, sval_ne));
+ TEST_ASSERT(!lilv_node_equals(ival, ival_ne));
+ TEST_ASSERT(!lilv_node_equals(fval, fval_ne));
+
+ TEST_ASSERT(!lilv_node_equals(uval, sval));
+ TEST_ASSERT(!lilv_node_equals(sval, ival));
+ TEST_ASSERT(!lilv_node_equals(ival, fval));
+
+ LilvNode* uval_dup = lilv_node_duplicate(uval);
+ TEST_ASSERT(lilv_node_equals(uval, uval_dup));
+
+ LilvNode* ifval = lilv_new_float(world, 42.0);
+ TEST_ASSERT(!lilv_node_equals(ival, ifval));
+ lilv_node_free(ifval);
+
+ LilvNode* nil = NULL;
+ TEST_ASSERT(!lilv_node_equals(uval, nil));
+ TEST_ASSERT(!lilv_node_equals(nil, uval));
+ TEST_ASSERT(lilv_node_equals(nil, nil));
+
+ LilvNode* nil2 = lilv_node_duplicate(nil);
+ TEST_ASSERT(lilv_node_equals(nil, nil2));
+
+ lilv_node_free(uval);
+ lilv_node_free(sval);
+ lilv_node_free(ival);
+ lilv_node_free(fval);
+ lilv_node_free(uval_e);
+ lilv_node_free(sval_e);
+ lilv_node_free(ival_e);
+ lilv_node_free(fval_e);
+ lilv_node_free(uval_ne);
+ lilv_node_free(sval_ne);
+ lilv_node_free(ival_ne);
+ lilv_node_free(fval_ne);
+ lilv_node_free(uval_dup);
+ lilv_node_free(nil2);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_util(void)
+{
+ TEST_ASSERT(!lilv_realpath(NULL));
+
+ char a_path[16];
+ char b_path[16];
+ strncpy(a_path, "copy_a_XXXXXX", sizeof(a_path));
+ strncpy(b_path, "copy_b_XXXXXX", sizeof(b_path));
+ mkstemp(a_path);
+ mkstemp(b_path);
+
+ FILE* fa = fopen(a_path, "w");
+ FILE* fb = fopen(b_path, "w");
+ fprintf(fa, "AA\n");
+ fprintf(fb, "AB\n");
+ fclose(fa);
+ fclose(fb);
+
+ TEST_ASSERT(lilv_copy_file("does/not/exist", "copy"));
+ TEST_ASSERT(lilv_copy_file(a_path, "not/a/dir/copy"));
+ TEST_ASSERT(!lilv_copy_file(a_path, "copy_c"));
+ TEST_ASSERT(!lilv_file_equals(a_path, b_path));
+ TEST_ASSERT(lilv_file_equals(a_path, a_path));
+ TEST_ASSERT(lilv_file_equals(a_path, "copy_c"));
+ TEST_ASSERT(!lilv_file_equals("does/not/exist", b_path));
+ TEST_ASSERT(!lilv_file_equals(a_path, "does/not/exist"));
+ TEST_ASSERT(!lilv_file_equals("does/not/exist", "/does/not/either"));
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int discovery_plugin_found = 0;
+
+static void
+discovery_verify_plugin(const LilvPlugin* plugin)
+{
+ const LilvNode* value = lilv_plugin_get_uri(plugin);
+ if (lilv_node_equals(value, plugin_uri_value)) {
+ const LilvNode* lib_uri = NULL;
+ TEST_ASSERT(!lilv_node_equals(value, plugin2_uri_value));
+ discovery_plugin_found = 1;
+ lib_uri = lilv_plugin_get_library_uri(plugin);
+ TEST_ASSERT(lib_uri);
+ TEST_ASSERT(lilv_node_is_uri(lib_uri));
+ TEST_ASSERT(lilv_node_as_uri(lib_uri));
+ TEST_ASSERT(strstr(lilv_node_as_uri(lib_uri), "foo" SHLIB_EXT));
+ TEST_ASSERT(lilv_plugin_verify(plugin));
+ }
+}
+
+static int
+test_discovery(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ;"
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:port [ a lv2:ControlPort ; a lv2:InputPort ;"
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; ] .")) {
+ return 0;
+ }
+
+ init_uris();
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ TEST_ASSERT(lilv_plugins_size(plugins) > 0);
+
+ const LilvPlugin* explug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(explug != NULL);
+ const LilvPlugin* explug2 = lilv_plugins_get_by_uri(plugins, plugin2_uri_value);
+ TEST_ASSERT(explug2 == NULL);
+
+ if (explug) {
+ LilvNode* name = lilv_plugin_get_name(explug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "Test plugin"));
+ lilv_node_free(name);
+ }
+
+ discovery_plugin_found = 0;
+ LILV_FOREACH(plugins, i, plugins)
+ discovery_verify_plugin(lilv_plugins_get(plugins, i));
+
+ TEST_ASSERT(discovery_plugin_found);
+ plugins = NULL;
+
+ cleanup_uris();
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_lv2_path(void)
+{
+#ifndef _WIN32
+ char* orig_lv2_path = lilv_strdup(getenv("LV2_PATH"));
+
+ setenv("LV2_PATH", "~/.lv2:/usr/local/lib/lv2:/usr/lib/lv2", 1);
+
+ world = lilv_world_new();
+ lilv_world_load_all(world);
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const size_t n_plugins = lilv_plugins_size(plugins);
+
+ lilv_world_free(world);
+
+ setenv("LV2_PATH", "$HOME/.lv2:/usr/local/lib/lv2:/usr/lib/lv2", 1);
+ world = lilv_world_new();
+ lilv_world_load_all(world);
+ plugins = lilv_world_get_all_plugins(world);
+ TEST_ASSERT(lilv_plugins_size(plugins) == n_plugins);
+ lilv_world_free(world);
+ world = NULL;
+
+ if (orig_lv2_path) {
+ setenv("LV2_PATH", orig_lv2_path, 1);
+ } else {
+ unsetenv("LV2_PATH");
+ }
+ free(orig_lv2_path);
+#endif
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_verify(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:port [ a lv2:ControlPort ; a lv2:InputPort ;"
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ] .")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* explug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(explug);
+ TEST_ASSERT(lilv_plugin_verify(explug));
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_no_verify(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin . ")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* explug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(explug);
+ TEST_ASSERT(!lilv_plugin_verify(explug));
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_classes(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"Foo\" ; "
+ "] .")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPluginClass* plugin = lilv_world_get_plugin_class(world);
+ const LilvPluginClasses* classes = lilv_world_get_plugin_classes(world);
+ LilvPluginClasses* children = lilv_plugin_class_get_children(plugin);
+
+ TEST_ASSERT(lilv_plugin_class_get_parent_uri(plugin) == NULL);
+ TEST_ASSERT(lilv_plugin_classes_size(classes) > lilv_plugin_classes_size(children));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_plugin_class_get_label(plugin)), "Plugin"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_plugin_class_get_uri(plugin)),
+ "http://lv2plug.in/ns/lv2core#Plugin"));
+
+ LILV_FOREACH(plugin_classes, i, children) {
+ TEST_ASSERT(lilv_node_equals(
+ lilv_plugin_class_get_parent_uri(lilv_plugin_classes_get(children, i)),
+ lilv_plugin_class_get_uri(plugin)));
+ }
+
+ LilvNode* some_uri = lilv_new_uri(world, "http://example.org/whatever");
+ TEST_ASSERT(lilv_plugin_classes_get_by_uri(classes, some_uri) == NULL);
+ lilv_node_free(some_uri);
+
+ lilv_plugin_classes_free(children);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_plugin(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:optionalFeature lv2:hardRTCapable ; "
+ "lv2:requiredFeature <http://lv2plug.in/ns/ext/event> ; "
+ "lv2:extensionData <http://example.org/extdata> ;"
+ ":foo 1.6180 ; "
+ ":bar true ; "
+ ":baz false ; "
+ ":blank [ a <http://example.org/blank> ] ; "
+ "doap:maintainer [ foaf:name \"David Robillard\" ; "
+ " foaf:homepage <http://drobilla.net> ; foaf:mbox <mailto:d@drobilla.net> ] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ ":thing doap:name \"Something else\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ const LilvPluginClass* klass = lilv_plugin_get_class(plug);
+ const LilvNode* klass_uri = lilv_plugin_class_get_uri(klass);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(klass_uri),
+ "http://lv2plug.in/ns/lv2core#CompressorPlugin"));
+
+ LilvNode* rdf_type = lilv_new_uri(
+ world, "http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
+ TEST_ASSERT(lilv_world_ask(world,
+ lilv_plugin_get_uri(plug),
+ rdf_type,
+ klass_uri));
+ lilv_node_free(rdf_type);
+
+ TEST_ASSERT(!lilv_plugin_is_replaced(plug));
+ TEST_ASSERT(!lilv_plugin_get_related(plug, NULL));
+
+ const LilvNode* plug_bundle_uri = lilv_plugin_get_bundle_uri(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(plug_bundle_uri), bundle_dir_uri));
+
+ const LilvNodes* data_uris = lilv_plugin_get_data_uris(plug);
+ TEST_ASSERT(lilv_nodes_size(data_uris) == 2);
+
+ LilvNode* project = lilv_plugin_get_project(plug);
+ TEST_ASSERT(!project);
+
+ char* manifest_uri = (char*)malloc(TEST_PATH_MAX);
+ char* data_uri = (char*)malloc(TEST_PATH_MAX);
+ snprintf(manifest_uri, TEST_PATH_MAX, "%s%s",
+ lilv_node_as_string(plug_bundle_uri), "manifest.ttl");
+ snprintf(data_uri, TEST_PATH_MAX, "%s%s",
+ lilv_node_as_string(plug_bundle_uri), "plugin.ttl");
+
+ LilvNode* manifest_uri_val = lilv_new_uri(world, manifest_uri);
+ TEST_ASSERT(lilv_nodes_contains(data_uris, manifest_uri_val));
+ lilv_node_free(manifest_uri_val);
+
+ LilvNode* data_uri_val = lilv_new_uri(world, data_uri);
+ TEST_ASSERT(lilv_nodes_contains(data_uris, data_uri_val));
+ lilv_node_free(data_uri_val);
+
+ LilvNode* unknown_uri_val = lilv_new_uri(world, "http://example.org/unknown");
+ TEST_ASSERT(!lilv_nodes_contains(data_uris, unknown_uri_val));
+ lilv_node_free(unknown_uri_val);
+
+ free(manifest_uri);
+ free(data_uri);
+
+ float mins[3];
+ float maxs[3];
+ float defs[3];
+ lilv_plugin_get_port_ranges_float(plug, mins, maxs, defs);
+ TEST_ASSERT(mins[0] == -1.0f);
+ TEST_ASSERT(maxs[0] == 1.0f);
+ TEST_ASSERT(defs[0] == 0.5f);
+
+ LilvNode* audio_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#AudioPort");
+ LilvNode* control_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#ControlPort");
+ LilvNode* in_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#InputPort");
+ LilvNode* out_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#OutputPort");
+
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, control_class, NULL) == 3);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, audio_class, NULL) == 0);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, in_class, NULL) == 2);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, out_class, NULL) == 1);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, control_class, in_class, NULL) == 2);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, control_class, out_class, NULL) == 1);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, audio_class, in_class, NULL) == 0);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, audio_class, out_class, NULL) == 0);
+
+ TEST_ASSERT(lilv_plugin_has_latency(plug));
+ TEST_ASSERT(lilv_plugin_get_latency_port_index(plug) == 2);
+
+ LilvNode* lv2_latency = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#latency");
+ const LilvPort* latency_port = lilv_plugin_get_port_by_designation(
+ plug, out_class, lv2_latency);
+ lilv_node_free(lv2_latency);
+
+ TEST_ASSERT(latency_port);
+ TEST_ASSERT(lilv_port_get_index(plug, latency_port) == 2);
+ TEST_ASSERT(lilv_node_is_blank(lilv_port_get_node(plug, latency_port)));
+
+ LilvNode* rt_feature = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#hardRTCapable");
+ LilvNode* event_feature = lilv_new_uri(world,
+ "http://lv2plug.in/ns/ext/event");
+ LilvNode* pretend_feature = lilv_new_uri(world,
+ "http://example.org/solvesWorldHunger");
+
+ TEST_ASSERT(lilv_plugin_has_feature(plug, rt_feature));
+ TEST_ASSERT(lilv_plugin_has_feature(plug, event_feature));
+ TEST_ASSERT(!lilv_plugin_has_feature(plug, pretend_feature));
+
+ lilv_node_free(rt_feature);
+ lilv_node_free(event_feature);
+ lilv_node_free(pretend_feature);
+
+ LilvNodes* supported = lilv_plugin_get_supported_features(plug);
+ LilvNodes* required = lilv_plugin_get_required_features(plug);
+ LilvNodes* optional = lilv_plugin_get_optional_features(plug);
+ TEST_ASSERT(lilv_nodes_size(supported) == 2);
+ TEST_ASSERT(lilv_nodes_size(required) == 1);
+ TEST_ASSERT(lilv_nodes_size(optional) == 1);
+ lilv_nodes_free(supported);
+ lilv_nodes_free(required);
+ lilv_nodes_free(optional);
+
+ LilvNode* foo_p = lilv_new_uri(world, "http://example.org/foo");
+ LilvNodes* foos = lilv_plugin_get_value(plug, foo_p);
+ TEST_ASSERT(lilv_nodes_size(foos) == 1);
+ TEST_ASSERT(fabs(lilv_node_as_float(lilv_nodes_get_first(foos)) - 1.6180) < FLT_EPSILON);
+ lilv_node_free(foo_p);
+ lilv_nodes_free(foos);
+
+ LilvNode* bar_p = lilv_new_uri(world, "http://example.org/bar");
+ LilvNodes* bars = lilv_plugin_get_value(plug, bar_p);
+ TEST_ASSERT(lilv_nodes_size(bars) == 1);
+ TEST_ASSERT(lilv_node_as_bool(lilv_nodes_get_first(bars)) == true);
+ lilv_node_free(bar_p);
+ lilv_nodes_free(bars);
+
+ LilvNode* baz_p = lilv_new_uri(world, "http://example.org/baz");
+ LilvNodes* bazs = lilv_plugin_get_value(plug, baz_p);
+ TEST_ASSERT(lilv_nodes_size(bazs) == 1);
+ TEST_ASSERT(lilv_node_as_bool(lilv_nodes_get_first(bazs)) == false);
+ lilv_node_free(baz_p);
+ lilv_nodes_free(bazs);
+
+ LilvNode* blank_p = lilv_new_uri(world, "http://example.org/blank");
+ LilvNodes* blanks = lilv_plugin_get_value(plug, blank_p);
+ TEST_ASSERT(lilv_nodes_size(blanks) == 1);
+ LilvNode* blank = lilv_nodes_get_first(blanks);
+ TEST_ASSERT(lilv_node_is_blank(blank));
+ const char* blank_str = lilv_node_as_blank(blank);
+ char* blank_tok = lilv_node_get_turtle_token(blank);
+ TEST_ASSERT(!strncmp(blank_tok, "_:", 2));
+ TEST_ASSERT(!strcmp(blank_tok + 2, blank_str));
+ lilv_free(blank_tok);
+ lilv_node_free(blank_p);
+ lilv_nodes_free(blanks);
+
+ LilvNode* author_name = lilv_plugin_get_author_name(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_name), "David Robillard"));
+ lilv_node_free(author_name);
+
+ LilvNode* author_email = lilv_plugin_get_author_email(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_email), "mailto:d@drobilla.net"));
+ lilv_node_free(author_email);
+
+ LilvNode* author_homepage = lilv_plugin_get_author_homepage(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_homepage), "http://drobilla.net"));
+ lilv_node_free(author_homepage);
+
+ LilvNode* thing_uri = lilv_new_uri(world, "http://example.org/thing");
+ LilvNode* name_p = lilv_new_uri(world, "http://usefulinc.com/ns/doap#name");
+ LilvNodes* thing_names = lilv_world_find_nodes(world, thing_uri, name_p, NULL);
+ TEST_ASSERT(lilv_nodes_size(thing_names) == 1);
+ LilvNode* thing_name = lilv_nodes_get_first(thing_names);
+ TEST_ASSERT(thing_name);
+ TEST_ASSERT(lilv_node_is_string(thing_name));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(thing_name), "Something else"));
+ LilvNode* thing_name2 = lilv_world_get(world, thing_uri, name_p, NULL);
+ TEST_ASSERT(lilv_node_equals(thing_name, thing_name2));
+
+ LilvUIs* uis = lilv_plugin_get_uis(plug);
+ TEST_ASSERT(lilv_uis_size(uis) == 0);
+ lilv_uis_free(uis);
+
+ LilvNode* extdata = lilv_new_uri(world, "http://example.org/extdata");
+ LilvNode* noextdata = lilv_new_uri(world, "http://example.org/noextdata");
+ LilvNodes* extdatas = lilv_plugin_get_extension_data(plug);
+ TEST_ASSERT(lilv_plugin_has_extension_data(plug, extdata));
+ TEST_ASSERT(!lilv_plugin_has_extension_data(plug, noextdata));
+ TEST_ASSERT(lilv_nodes_size(extdatas) == 1);
+ TEST_ASSERT(lilv_node_equals(lilv_nodes_get_first(extdatas), extdata));
+ lilv_node_free(noextdata);
+ lilv_node_free(extdata);
+ lilv_nodes_free(extdatas);
+
+ lilv_nodes_free(thing_names);
+ lilv_node_free(thing_uri);
+ lilv_node_free(thing_name2);
+ lilv_node_free(name_p);
+ lilv_node_free(control_class);
+ lilv_node_free(audio_class);
+ lilv_node_free(in_class);
+ lilv_node_free(out_class);
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_project(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin with project") " ; "
+ LICENSE_GPL " ; "
+ "lv2:project [ "
+ " doap:maintainer [ "
+ " foaf:name \"David Robillard\" ; "
+ " foaf:homepage <http://drobilla.net> ; foaf:mbox <mailto:d@drobilla.net> ] ; "
+ "] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ ":thing doap:name \"Something else\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvNode* author_name = lilv_plugin_get_author_name(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_name), "David Robillard"));
+ lilv_node_free(author_name);
+
+ LilvNode* author_email = lilv_plugin_get_author_email(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_email), "mailto:d@drobilla.net"));
+ lilv_node_free(author_email);
+
+ LilvNode* author_homepage = lilv_plugin_get_author_homepage(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_homepage), "http://drobilla.net"));
+ lilv_node_free(author_homepage);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_no_author(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin with project") " ; "
+ LICENSE_GPL " ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ ":thing doap:name \"Something else\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvNode* author_name = lilv_plugin_get_author_name(plug);
+ TEST_ASSERT(!author_name);
+
+ LilvNode* author_email = lilv_plugin_get_author_email(plug);
+ TEST_ASSERT(!author_email);
+
+ LilvNode* author_homepage = lilv_plugin_get_author_homepage(plug);
+ TEST_ASSERT(!author_homepage);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_project_no_author(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin with project") " ; "
+ LICENSE_GPL " ; "
+ "lv2:project [ "
+ " doap:name \"Fake project\" ;"
+ "] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ ":thing doap:name \"Something else\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvNode* author_name = lilv_plugin_get_author_name(plug);
+ TEST_ASSERT(!author_name);
+
+ LilvNode* author_email = lilv_plugin_get_author_email(plug);
+ TEST_ASSERT(!author_email);
+
+ LilvNode* author_homepage = lilv_plugin_get_author_homepage(plug);
+ TEST_ASSERT(!author_homepage);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_preset(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin with project") " ; "
+ LICENSE_GPL " ; "
+ "lv2:project [ "
+ " doap:name \"Fake project\" ;"
+ "] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ "<http://example.org/preset> a pset:Preset ;"
+ " lv2:appliesTo :plug ;"
+ " rdfs:label \"some preset\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvNode* pset_Preset = lilv_new_uri(world, LV2_PRESETS__Preset);
+ LilvNodes* related = lilv_plugin_get_related(plug, pset_Preset);
+
+ TEST_ASSERT(lilv_nodes_size(related) == 1);
+
+ lilv_node_free(pset_Preset);
+ lilv_nodes_free(related);
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_prototype(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":prot a lv2:PluginBase ; rdfs:seeAlso <plugin.ttl> .\n"
+ ":plug a lv2:Plugin ; lv2:binary <inst" SHLIB_EXT "> ; lv2:prototype :prot .\n",
+ BUNDLE_PREFIXES
+ ":prot a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ LICENSE_GPL " ; "
+ "lv2:project [ "
+ " doap:name \"Fake project\" ;"
+ "] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ ":plug doap:name \"Instance\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ // Test non-inherited property
+ LilvNode* name = lilv_plugin_get_name(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "Instance"));
+ lilv_node_free(name);
+
+ // Test inherited property
+ const LilvNode* binary = lilv_plugin_get_library_uri(plug);
+ TEST_ASSERT(strstr(lilv_node_as_string(binary), "inst" SHLIB_EXT));
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_port(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES PREFIX_LV2EV
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "doap:homepage <http://example.org/someplug> ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; "
+ " lv2:name \"store\" ; "
+ " lv2:name \"dépanneur\"@fr-ca ; lv2:name \"épicerie\"@fr-fr ; "
+ " lv2:name \"tienda\"@es ; "
+ " rdfs:comment \"comment\"@en , \"commentaires\"@fr ; "
+ " lv2:portProperty lv2:integer ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 ; "
+ " lv2:scalePoint [ rdfs:label \"Sin\"; rdf:value 3 ] ; "
+ " lv2:scalePoint [ rdfs:label \"Cos\"; rdf:value 4 ] "
+ "] , [\n"
+ " a lv2:EventPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"event_in\" ; "
+ " lv2:name \"Event Input\" ; "
+ " lv2ev:supportsEvent <http://example.org/event> ;"
+ " atom:supports <http://example.org/atomEvent> "
+ "] , [\n"
+ " a lv2:AudioPort ; a lv2:InputPort ; "
+ " lv2:index 2 ; lv2:symbol \"audio_in\" ; "
+ " lv2:name \"Audio Input\" ; "
+ "] , [\n"
+ " a lv2:AudioPort ; a lv2:OutputPort ; "
+ " lv2:index 3 ; lv2:symbol \"audio_out\" ; "
+ " lv2:name \"Audio Output\" ; "
+ "] .")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvNode* psym = lilv_new_string(world, "foo");
+ const LilvPort* p = lilv_plugin_get_port_by_index(plug, 0);
+ const LilvPort* p2 = lilv_plugin_get_port_by_symbol(plug, psym);
+ lilv_node_free(psym);
+ TEST_ASSERT(p != NULL);
+ TEST_ASSERT(p2 != NULL);
+ TEST_ASSERT(p == p2);
+
+ LilvNode* nopsym = lilv_new_string(world, "thisaintnoportfoo");
+ const LilvPort* p3 = lilv_plugin_get_port_by_symbol(plug, nopsym);
+ TEST_ASSERT(p3 == NULL);
+ lilv_node_free(nopsym);
+
+ // Try getting an invalid property
+ LilvNode* num = lilv_new_int(world, 1);
+ LilvNodes* nothing = lilv_port_get_value(plug, p, num);
+ TEST_ASSERT(!nothing);
+ lilv_node_free(num);
+
+ LilvNode* audio_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#AudioPort");
+ LilvNode* control_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#ControlPort");
+ LilvNode* in_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#InputPort");
+ LilvNode* out_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#OutputPort");
+
+ TEST_ASSERT(lilv_nodes_size(lilv_port_get_classes(plug, p)) == 2);
+ TEST_ASSERT(lilv_plugin_get_num_ports(plug) == 4);
+ TEST_ASSERT(lilv_port_is_a(plug, p, control_class));
+ TEST_ASSERT(lilv_port_is_a(plug, p, in_class));
+ TEST_ASSERT(!lilv_port_is_a(plug, p, audio_class));
+
+ LilvNodes* port_properties = lilv_port_get_properties(plug, p);
+ TEST_ASSERT(lilv_nodes_size(port_properties) == 1);
+ lilv_nodes_free(port_properties);
+
+ // Untranslated name (current locale is set to "C" in main)
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_port_get_symbol(plug, p)), "foo"));
+ LilvNode* name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "store"));
+ lilv_node_free(name);
+
+ // Exact language match
+ setenv("LANG", "fr_FR", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "épicerie"));
+ lilv_node_free(name);
+
+ // Exact language match (with charset suffix)
+ setenv("LANG", "fr_CA.utf8", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "dépanneur"));
+ lilv_node_free(name);
+
+ // Partial language match (choose value translated for different country)
+ setenv("LANG", "fr_BE", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT((!strcmp(lilv_node_as_string(name), "dépanneur"))
+ ||(!strcmp(lilv_node_as_string(name), "épicerie")));
+ lilv_node_free(name);
+
+ // Partial language match (choose country-less language tagged value)
+ setenv("LANG", "es_MX", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "tienda"));
+ lilv_node_free(name);
+
+ // No language match (choose untranslated value)
+ setenv("LANG", "cn", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "store"));
+ lilv_node_free(name);
+
+ // Invalid language
+ setenv("LANG", "1!", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "store"));
+ lilv_node_free(name);
+
+ setenv("LANG", "en_CA.utf-8", 1);
+
+ // Language tagged value with no untranslated values
+ LilvNode* rdfs_comment = lilv_new_uri(world, LILV_NS_RDFS "comment");
+ LilvNodes* comments = lilv_port_get_value(plug, p, rdfs_comment);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(comments)),
+ "comment"));
+ LilvNode* comment = lilv_port_get(plug, p, rdfs_comment);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(comment), "comment"));
+ lilv_node_free(comment);
+ lilv_nodes_free(comments);
+
+ setenv("LANG", "fr", 1);
+
+ comments = lilv_port_get_value(plug, p, rdfs_comment);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(comments)),
+ "commentaires"));
+ lilv_nodes_free(comments);
+
+ setenv("LANG", "cn", 1);
+
+ comments = lilv_port_get_value(plug, p, rdfs_comment);
+ TEST_ASSERT(!comments);
+ lilv_nodes_free(comments);
+
+ lilv_node_free(rdfs_comment);
+
+ setenv("LANG", "C", 1); // Reset locale
+
+ LilvScalePoints* points = lilv_port_get_scale_points(plug, p);
+ TEST_ASSERT(lilv_scale_points_size(points) == 2);
+
+ LilvIter* sp_iter = lilv_scale_points_begin(points);
+ const LilvScalePoint* sp0 = lilv_scale_points_get(points, sp_iter);
+ TEST_ASSERT(sp0);
+ sp_iter = lilv_scale_points_next(points, sp_iter);
+ const LilvScalePoint* sp1 = lilv_scale_points_get(points, sp_iter);
+ TEST_ASSERT(sp1);
+
+ TEST_ASSERT(
+ ((!strcmp(lilv_node_as_string(lilv_scale_point_get_label(sp0)), "Sin")
+ && lilv_node_as_float(lilv_scale_point_get_value(sp0)) == 3)
+ &&
+ (!strcmp(lilv_node_as_string(lilv_scale_point_get_label(sp1)), "Cos")
+ && lilv_node_as_float(lilv_scale_point_get_value(sp1)) == 4))
+ ||
+ ((!strcmp(lilv_node_as_string(lilv_scale_point_get_label(sp0)), "Cos")
+ && lilv_node_as_float(lilv_scale_point_get_value(sp0)) == 4)
+ &&
+ (!strcmp(lilv_node_as_string(lilv_scale_point_get_label(sp1)), "Sin")
+ && lilv_node_as_float(lilv_scale_point_get_value(sp1)) == 3)));
+
+ LilvNode* homepage_p = lilv_new_uri(world, "http://usefulinc.com/ns/doap#homepage");
+ LilvNodes* homepages = lilv_plugin_get_value(plug, homepage_p);
+ TEST_ASSERT(lilv_nodes_size(homepages) == 1);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(homepages)),
+ "http://example.org/someplug"));
+
+ LilvNode *min, *max, *def;
+ lilv_port_get_range(plug, p, &def, &min, &max);
+ TEST_ASSERT(def);
+ TEST_ASSERT(min);
+ TEST_ASSERT(max);
+ TEST_ASSERT(lilv_node_as_float(def) == 0.5);
+ TEST_ASSERT(lilv_node_as_float(min) == -1.0);
+ TEST_ASSERT(lilv_node_as_float(max) == 1.0);
+
+ LilvNode* integer_prop = lilv_new_uri(world, "http://lv2plug.in/ns/lv2core#integer");
+ LilvNode* toggled_prop = lilv_new_uri(world, "http://lv2plug.in/ns/lv2core#toggled");
+
+ TEST_ASSERT(lilv_port_has_property(plug, p, integer_prop));
+ TEST_ASSERT(!lilv_port_has_property(plug, p, toggled_prop));
+
+ const LilvPort* ep = lilv_plugin_get_port_by_index(plug, 1);
+
+ LilvNode* event_type = lilv_new_uri(world, "http://example.org/event");
+ LilvNode* event_type_2 = lilv_new_uri(world, "http://example.org/otherEvent");
+ LilvNode* atom_event = lilv_new_uri(world, "http://example.org/atomEvent");
+ TEST_ASSERT(lilv_port_supports_event(plug, ep, event_type));
+ TEST_ASSERT(!lilv_port_supports_event(plug, ep, event_type_2));
+ TEST_ASSERT(lilv_port_supports_event(plug, ep, atom_event));
+
+ LilvNode* name_p = lilv_new_uri(world, "http://lv2plug.in/ns/lv2core#name");
+ LilvNodes* names = lilv_port_get_value(plug, p, name_p);
+ TEST_ASSERT(lilv_nodes_size(names) == 1);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(names)),
+ "store"));
+ lilv_nodes_free(names);
+
+ LilvNode* true_val = lilv_new_bool(world, true);
+ LilvNode* false_val = lilv_new_bool(world, false);
+
+ TEST_ASSERT(!lilv_node_equals(true_val, false_val));
+
+ lilv_world_set_option(world, LILV_OPTION_FILTER_LANG, false_val);
+ names = lilv_port_get_value(plug, p, name_p);
+ TEST_ASSERT(lilv_nodes_size(names) == 4);
+ lilv_nodes_free(names);
+ lilv_world_set_option(world, LILV_OPTION_FILTER_LANG, true_val);
+
+ lilv_node_free(false_val);
+ lilv_node_free(true_val);
+
+ names = lilv_port_get_value(plug, ep, name_p);
+ TEST_ASSERT(lilv_nodes_size(names) == 1);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(names)),
+ "Event Input"));
+
+ const LilvPort* ap_in = lilv_plugin_get_port_by_index(plug, 2);
+
+ TEST_ASSERT(lilv_port_is_a(plug, ap_in, in_class));
+ TEST_ASSERT(!lilv_port_is_a(plug, ap_in, out_class));
+ TEST_ASSERT(lilv_port_is_a(plug, ap_in, audio_class));
+ TEST_ASSERT(!lilv_port_is_a(plug, ap_in, control_class));
+
+ const LilvPort* ap_out = lilv_plugin_get_port_by_index(plug, 3);
+
+ TEST_ASSERT(lilv_port_is_a(plug, ap_out, out_class));
+ TEST_ASSERT(!lilv_port_is_a(plug, ap_out, in_class));
+ TEST_ASSERT(lilv_port_is_a(plug, ap_out, audio_class));
+ TEST_ASSERT(!lilv_port_is_a(plug, ap_out, control_class));
+
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, control_class, in_class , NULL) == 1);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, audio_class , in_class , NULL) == 1);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, audio_class , out_class, NULL) == 1);
+
+ lilv_nodes_free(names);
+ lilv_node_free(name_p);
+
+ lilv_node_free(integer_prop);
+ lilv_node_free(toggled_prop);
+ lilv_node_free(event_type);
+ lilv_node_free(event_type_2);
+ lilv_node_free(atom_event);
+
+ lilv_node_free(min);
+ lilv_node_free(max);
+ lilv_node_free(def);
+
+ lilv_node_free(homepage_p);
+ lilv_nodes_free(homepages);
+
+ lilv_scale_points_free(points);
+ lilv_node_free(control_class);
+ lilv_node_free(audio_class);
+ lilv_node_free(out_class);
+ lilv_node_free(in_class);
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static unsigned
+ui_supported(const char* container_type_uri,
+ const char* ui_type_uri)
+{
+ return !strcmp(container_type_uri, ui_type_uri);
+}
+
+static int
+test_ui(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES PREFIX_LV2UI
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:optionalFeature lv2:hardRTCapable ; "
+ "lv2:requiredFeature <http://lv2plug.in/ns/ext/event> ; "
+ "lv2ui:ui :ui , :ui2 , :ui3 , :ui4 ; "
+ "doap:maintainer [ foaf:name \"David Robillard\" ; "
+ " foaf:homepage <http://drobilla.net> ; foaf:mbox <mailto:d@drobilla.net> ] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency "
+ "] .\n"
+ ":ui a lv2ui:GtkUI ; "
+ " lv2ui:requiredFeature lv2ui:makeResident ; "
+ " lv2ui:binary <ui" SHLIB_EXT "> ; "
+ " lv2ui:optionalFeature lv2ui:ext_presets . "
+ ":ui2 a lv2ui:GtkUI ; lv2ui:binary <ui2" SHLIB_EXT "> . "
+ ":ui3 a lv2ui:GtkUI ; lv2ui:binary <ui3" SHLIB_EXT "> . "
+ ":ui4 a lv2ui:GtkUI ; lv2ui:binary <ui4" SHLIB_EXT "> . ")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvUIs* uis = lilv_plugin_get_uis(plug);
+ TEST_ASSERT(lilv_uis_size(uis) == 4);
+
+ const LilvUI* ui0 = lilv_uis_get(uis, lilv_uis_begin(uis));
+ TEST_ASSERT(ui0);
+
+ LilvNode* ui_uri = lilv_new_uri(world, "http://example.org/ui");
+ LilvNode* ui2_uri = lilv_new_uri(world, "http://example.org/ui3");
+ LilvNode* ui3_uri = lilv_new_uri(world, "http://example.org/ui4");
+ LilvNode* noui_uri = lilv_new_uri(world, "http://example.org/notaui");
+
+ const LilvUI* ui0_2 = lilv_uis_get_by_uri(uis, ui_uri);
+ TEST_ASSERT(ui0 == ui0_2);
+ TEST_ASSERT(lilv_node_equals(lilv_ui_get_uri(ui0_2), ui_uri));
+
+ const LilvUI* ui2 = lilv_uis_get_by_uri(uis, ui2_uri);
+ TEST_ASSERT(ui2 != ui0);
+
+ const LilvUI* ui3 = lilv_uis_get_by_uri(uis, ui3_uri);
+ TEST_ASSERT(ui3 != ui0);
+
+ const LilvUI* noui = lilv_uis_get_by_uri(uis, noui_uri);
+ TEST_ASSERT(noui == NULL);
+
+ const LilvNodes* classes = lilv_ui_get_classes(ui0);
+ TEST_ASSERT(lilv_nodes_size(classes) == 1);
+
+ LilvNode* ui_class_uri = lilv_new_uri(world,
+ "http://lv2plug.in/ns/extensions/ui#GtkUI");
+
+ LilvNode* unknown_ui_class_uri = lilv_new_uri(world,
+ "http://example.org/mysteryUI");
+
+ TEST_ASSERT(lilv_node_equals(lilv_nodes_get_first(classes), ui_class_uri));
+ TEST_ASSERT(lilv_ui_is_a(ui0, ui_class_uri));
+
+ const LilvNode* ui_type = NULL;
+ TEST_ASSERT(lilv_ui_is_supported(ui0, ui_supported, ui_class_uri, &ui_type));
+ TEST_ASSERT(!lilv_ui_is_supported(ui0, ui_supported, unknown_ui_class_uri, &ui_type));
+ TEST_ASSERT(lilv_node_equals(ui_type, ui_class_uri));
+
+ const LilvNode* plug_bundle_uri = lilv_plugin_get_bundle_uri(plug);
+ const LilvNode* ui_bundle_uri = lilv_ui_get_bundle_uri(ui0);
+ TEST_ASSERT(lilv_node_equals(plug_bundle_uri, ui_bundle_uri));
+
+ char* ui_binary_uri_str = (char*)malloc(TEST_PATH_MAX);
+ snprintf(ui_binary_uri_str, TEST_PATH_MAX, "%s%s",
+ lilv_node_as_string(plug_bundle_uri), "ui" SHLIB_EXT);
+
+ const LilvNode* ui_binary_uri = lilv_ui_get_binary_uri(ui0);
+
+ LilvNode* expected_uri = lilv_new_uri(world, ui_binary_uri_str);
+ TEST_ASSERT(lilv_node_equals(expected_uri, ui_binary_uri));
+
+ free(ui_binary_uri_str);
+ lilv_node_free(unknown_ui_class_uri);
+ lilv_node_free(ui_class_uri);
+ lilv_node_free(ui_uri);
+ lilv_node_free(ui2_uri);
+ lilv_node_free(ui3_uri);
+ lilv_node_free(noui_uri);
+ lilv_node_free(expected_uri);
+ lilv_uis_free(uis);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+uint32_t atom_Float = 0;
+float in = 1.0;
+float out = 42.0;
+float control = 1234.0;
+
+static const void*
+get_port_value(const char* port_symbol,
+ void* user_data,
+ uint32_t* size,
+ uint32_t* type)
+{
+ if (!strcmp(port_symbol, "input")) {
+ *size = sizeof(float);
+ *type = atom_Float;
+ return &in;
+ } else if (!strcmp(port_symbol, "output")) {
+ *size = sizeof(float);
+ *type = atom_Float;
+ return &out;
+ } else if (!strcmp(port_symbol, "control")) {
+ *size = sizeof(float);
+ *type = atom_Float;
+ return &control;
+ } else {
+ fprintf(stderr, "error: get_port_value for nonexistent port `%s'\n",
+ port_symbol);
+ *size = *type = 0;
+ return NULL;
+ }
+}
+
+static void
+set_port_value(const char* port_symbol,
+ void* user_data,
+ const void* value,
+ uint32_t size,
+ uint32_t type)
+{
+ if (!strcmp(port_symbol, "input")) {
+ in = *(const float*)value;
+ } else if (!strcmp(port_symbol, "output")) {
+ out = *(const float*)value;
+ } else if (!strcmp(port_symbol, "control")) {
+ control = *(const float*)value;
+ } else {
+ fprintf(stderr, "error: set_port_value for nonexistent port `%s'\n",
+ port_symbol);
+ }
+}
+
+char** uris = NULL;
+size_t n_uris = 0;
+
+static LV2_URID
+map_uri(LV2_URID_Map_Handle handle,
+ const char* uri)
+{
+ for (size_t i = 0; i < n_uris; ++i) {
+ if (!strcmp(uris[i], uri)) {
+ return i + 1;
+ }
+ }
+
+ assert(serd_uri_string_has_scheme((const uint8_t*)uri));
+ uris = (char**)realloc(uris, ++n_uris * sizeof(char*));
+ uris[n_uris - 1] = lilv_strdup(uri);
+ return n_uris;
+}
+
+static const char*
+unmap_uri(LV2_URID_Map_Handle handle,
+ LV2_URID urid)
+{
+ if (urid > 0 && urid <= n_uris) {
+ return uris[urid - 1];
+ }
+ return NULL;
+}
+
+static char* temp_dir = NULL;
+
+static char*
+lilv_make_path(LV2_State_Make_Path_Handle handle,
+ const char* path)
+{
+ return lilv_path_join(temp_dir, path);
+}
+
+static int
+test_state(void)
+{
+ init_world();
+
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(LILV_TEST_BUNDLE);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ LilvNode* plugin_uri = lilv_new_uri(world,
+ "http://example.org/lilv-test-plugin");
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ LV2_URID_Map map = { NULL, map_uri };
+ LV2_Feature map_feature = { LV2_URID_MAP_URI, &map };
+ LV2_URID_Unmap unmap = { NULL, unmap_uri };
+ LV2_Feature unmap_feature = { LV2_URID_UNMAP_URI, &unmap };
+ const LV2_Feature* features[] = { &map_feature, &unmap_feature, NULL };
+
+ atom_Float = map.map(map.handle, "http://lv2plug.in/ns/ext/atom#Float");
+
+ LilvNode* num = lilv_new_int(world, 5);
+ LilvState* nostate = lilv_state_new_from_file(world, &map, num, "/junk");
+ TEST_ASSERT(!nostate);
+
+ LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, features);
+ TEST_ASSERT(instance);
+ lilv_instance_activate(instance);
+ lilv_instance_connect_port(instance, 0, &in);
+ lilv_instance_connect_port(instance, 1, &out);
+ lilv_instance_run(instance, 1);
+ TEST_ASSERT(in == 1.0);
+ TEST_ASSERT(out == 1.0);
+
+ temp_dir = lilv_realpath("temp");
+
+ const char* file_dir = NULL;
+ char* copy_dir = NULL;
+ char* link_dir = NULL;
+ char* save_dir = NULL;
+
+ // Get instance state state
+ LilvState* state = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, save_dir,
+ get_port_value, world, 0, NULL);
+
+ // Get another instance state
+ LilvState* state2 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, save_dir,
+ get_port_value, world, 0, NULL);
+
+ // Ensure they are equal
+ TEST_ASSERT(lilv_state_equals(state, state2));
+
+ // Check that we can't delete unsaved state
+ TEST_ASSERT(lilv_state_delete(world, state));
+
+ // Check that state has no URI
+ TEST_ASSERT(!lilv_state_get_uri(state));
+
+ // Check that we can't save a state with no URI
+ char* bad_state_str = lilv_state_to_string(
+ world, &map, &unmap, state, NULL, NULL);
+ TEST_ASSERT(!bad_state_str);
+
+ // Check that we can't restore the NULL string (and it doesn't crash)
+ LilvState* bad_state = lilv_state_new_from_string(world, &map, NULL);
+ TEST_ASSERT(!bad_state);
+
+ // Save state to a string
+ char* state1_str = lilv_state_to_string(
+ world, &map, &unmap, state, "http://example.org/state1", NULL);
+
+ // Restore from string
+ LilvState* from_str = lilv_state_new_from_string(world, &map, state1_str);
+
+ // Ensure they are equal
+ TEST_ASSERT(lilv_state_equals(state, from_str));
+ free(state1_str);
+
+ const LilvNode* state_plugin_uri = lilv_state_get_plugin_uri(state);
+ TEST_ASSERT(lilv_node_equals(state_plugin_uri, plugin_uri));
+
+ // Tinker with the label of the first state
+ TEST_ASSERT(lilv_state_get_label(state) == NULL);
+ lilv_state_set_label(state, "Test State Old Label");
+ TEST_ASSERT(!strcmp(lilv_state_get_label(state), "Test State Old Label"));
+ lilv_state_set_label(state, "Test State");
+ TEST_ASSERT(!strcmp(lilv_state_get_label(state), "Test State"));
+
+ TEST_ASSERT(!lilv_state_equals(state, state2)); // Label changed
+
+ // Run and get a new instance state (which should now differ)
+ lilv_instance_run(instance, 1);
+ LilvState* state3 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, save_dir,
+ get_port_value, world, 0, NULL);
+ TEST_ASSERT(!lilv_state_equals(state2, state3)); // num_runs changed
+
+ // Restore instance state to original state
+ lilv_state_restore(state2, instance, set_port_value, NULL, 0, NULL);
+
+ // Take a new snapshot and ensure it matches the set state
+ LilvState* state4 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, save_dir,
+ get_port_value, world, 0, NULL);
+ TEST_ASSERT(lilv_state_equals(state2, state4));
+
+ // Set some metadata properties
+ lilv_state_set_metadata(state, map.map(map.handle, LILV_NS_RDFS "comment"),
+ "This is a comment",
+ strlen("This is a comment") + 1,
+ map.map(map.handle, "http://lv2plug.in/ns/ext/atom#Literal"),
+ LV2_STATE_IS_POD);
+ lilv_state_set_metadata(state, map.map(map.handle, "http://example.org/metablob"),
+ "LIVEBEEF",
+ strlen("LIVEBEEF") + 1,
+ map.map(map.handle, "http://example.org/MetaBlob"),
+ 0);
+
+ // Save state to a directory
+ int ret = lilv_state_save(world, &map, &unmap, state, NULL,
+ "state/state.lv2", "state.ttl");
+ TEST_ASSERT(!ret);
+
+ // Load state from directory
+ LilvState* state5 = lilv_state_new_from_file(world, &map, NULL,
+ "state/state.lv2/state.ttl");
+
+ TEST_ASSERT(lilv_state_equals(state, state5)); // Round trip accuracy
+ TEST_ASSERT(lilv_state_get_num_properties(state) == 8);
+
+ // Attempt to save state to nowhere (error)
+ ret = lilv_state_save(world, &map, &unmap, state, NULL, NULL, NULL);
+ TEST_ASSERT(ret);
+
+ // Save another state to the same directory (update manifest)
+ ret = lilv_state_save(world, &map, &unmap, state, NULL,
+ "state/state.lv2", "state2.ttl");
+ TEST_ASSERT(!ret);
+
+ // Save state with URI to a directory
+ const char* state_uri = "http://example.org/state";
+ ret = lilv_state_save(world, &map, &unmap, state, state_uri,
+ "state/state6.lv2", "state6.ttl");
+ TEST_ASSERT(!ret);
+
+ // Load default bundle into world and load state from it
+ uint8_t* state6_path = (uint8_t*)lilv_path_absolute("state/state6.lv2/");
+ SerdNode state6_uri = serd_node_new_file_uri(state6_path, 0, 0, true);
+ LilvNode* test_state_bundle = lilv_new_uri(world, (const char*)state6_uri.buf);
+ LilvNode* test_state_node = lilv_new_uri(world, state_uri);
+ lilv_world_load_bundle(world, test_state_bundle);
+ lilv_world_load_resource(world, test_state_node);
+ serd_node_free(&state6_uri);
+ free(state6_path);
+
+ LilvState* state6 = lilv_state_new_from_world(world, &map, test_state_node);
+ TEST_ASSERT(lilv_state_equals(state, state6)); // Round trip accuracy
+
+ // Check that loaded state has correct URI
+ TEST_ASSERT(lilv_state_get_uri(state6));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_state_get_uri(state6)),
+ state_uri));
+
+ lilv_world_unload_resource(world, test_state_node);
+ lilv_world_unload_bundle(world, test_state_bundle);
+
+ LilvState* state6_2 = lilv_state_new_from_world(world, &map, test_state_node);
+ TEST_ASSERT(!state6_2); // No longer present
+ lilv_state_free(state6_2);
+
+ lilv_node_free(test_state_bundle);
+ lilv_node_free(test_state_node);
+
+ unsetenv("LV2_STATE_BUNDLE");
+
+ // Make directories and test files support
+ mkdir("temp", 0700);
+ file_dir = temp_dir;
+ mkdir("files", 0700);
+ copy_dir = lilv_realpath("files");
+ mkdir("links", 0700);
+ link_dir = lilv_realpath("links");
+
+ LV2_State_Make_Path make_path = { NULL, lilv_make_path };
+ LV2_Feature make_path_feature = { LV2_STATE__makePath, &make_path };
+ const LV2_Feature* ffeatures[] = { &make_path_feature, &map_feature, NULL };
+
+ lilv_instance_deactivate(instance);
+ lilv_instance_free(instance);
+ instance = lilv_plugin_instantiate(plugin, 48000.0, ffeatures);
+ lilv_instance_activate(instance);
+ lilv_instance_connect_port(instance, 0, &in);
+ lilv_instance_connect_port(instance, 1, &out);
+ lilv_instance_run(instance, 1);
+
+ // Test instantiating twice
+ LilvInstance* instance2 = lilv_plugin_instantiate(plugin, 48000.0, ffeatures);
+ if (!instance2) {
+ fatal_error("Failed to create multiple instances of <%s>\n",
+ lilv_node_as_uri(state_plugin_uri));
+ return 0;
+ }
+ lilv_instance_free(instance2);
+
+ // Get instance state state
+ LilvState* fstate = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, "state/fstate.lv2",
+ get_port_value, world, 0, ffeatures);
+
+ // Get another instance state
+ LilvState* fstate2 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, "state/fstate2.lv2",
+ get_port_value, world, 0, ffeatures);
+
+ // Should be identical
+ TEST_ASSERT(lilv_state_equals(fstate, fstate2));
+
+ // Run, writing more to rec file
+ lilv_instance_run(instance, 2);
+
+ // Get yet another instance state
+ LilvState* fstate3 = lilv_state_new_from_instance(
+ plugin, instance, &map, file_dir, copy_dir, link_dir, "state/fstate3.lv2",
+ get_port_value, world, 0, ffeatures);
+
+ // Should be different
+ TEST_ASSERT(!lilv_state_equals(fstate, fstate3));
+
+ // Save state to a directory
+ ret = lilv_state_save(world, &map, &unmap, fstate, NULL,
+ "state/fstate.lv2", "fstate.ttl");
+ TEST_ASSERT(!ret);
+
+ // Load state from directory
+ LilvState* fstate4 = lilv_state_new_from_file(world, &map, NULL,
+ "state/fstate.lv2/fstate.ttl");
+ TEST_ASSERT(lilv_state_equals(fstate, fstate4)); // Round trip accuracy
+
+ // Restore instance state to loaded state
+ lilv_state_restore(fstate4, instance, set_port_value, NULL, 0, ffeatures);
+
+ // Take a new snapshot and ensure it matches
+ LilvState* fstate5 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, "state/fstate5.lv2",
+ get_port_value, world, 0, ffeatures);
+ TEST_ASSERT(lilv_state_equals(fstate3, fstate5));
+
+ // Save state to a (different) directory again
+ ret = lilv_state_save(world, &map, &unmap, fstate, NULL,
+ "state/fstate6.lv2", "fstate6.ttl");
+ TEST_ASSERT(!ret);
+
+ // Reload it and ensure it's identical to the other loaded version
+ LilvState* fstate6 = lilv_state_new_from_file(world, &map, NULL,
+ "state/fstate6.lv2/fstate6.ttl");
+ TEST_ASSERT(lilv_state_equals(fstate4, fstate6));
+
+ // Run, changing rec file (without changing size)
+ lilv_instance_run(instance, 3);
+
+ // Take a new snapshot
+ LilvState* fstate7 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, "state/fstate7.lv2",
+ get_port_value, world, 0, ffeatures);
+ TEST_ASSERT(!lilv_state_equals(fstate6, fstate7));
+
+ // Save the changed state to a (different) directory again
+ ret = lilv_state_save(world, &map, &unmap, fstate7, NULL,
+ "state/fstate7.lv2", "fstate7.ttl");
+ TEST_ASSERT(!ret);
+
+ // Reload it and ensure it's changed
+ LilvState* fstate72 = lilv_state_new_from_file(world, &map, NULL,
+ "state/fstate7.lv2/fstate7.ttl");
+ TEST_ASSERT(lilv_state_equals(fstate72, fstate7));
+ TEST_ASSERT(!lilv_state_equals(fstate6, fstate72));
+
+ // Delete saved state
+ lilv_state_delete(world, fstate7);
+
+ lilv_instance_deactivate(instance);
+ lilv_instance_free(instance);
+
+ lilv_node_free(num);
+
+ lilv_state_free(state);
+ lilv_state_free(from_str);
+ lilv_state_free(state2);
+ lilv_state_free(state3);
+ lilv_state_free(state4);
+ lilv_state_free(state5);
+ lilv_state_free(state6);
+ lilv_state_free(fstate);
+ lilv_state_free(fstate2);
+ lilv_state_free(fstate3);
+ lilv_state_free(fstate4);
+ lilv_state_free(fstate5);
+ lilv_state_free(fstate6);
+ lilv_state_free(fstate7);
+ lilv_state_free(fstate72);
+
+ // Free URI map
+ for (size_t i = 0; i < n_uris; ++i) {
+ free(uris[i]);
+ }
+ free(uris);
+ n_uris = 0;
+
+ lilv_node_free(plugin_uri);
+ lilv_node_free(bundle_uri);
+ free(link_dir);
+ free(copy_dir);
+ free(temp_dir);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_bad_port_symbol(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES PREFIX_LV2EV
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "doap:homepage <http://example.org/someplug> ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"0invalid\" ;"
+ " lv2:name \"Invalid\" ; "
+ "] .")) {
+ return 0;
+ }
+
+ init_uris();
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+
+ uint32_t n_ports = lilv_plugin_get_num_ports(plug);
+ TEST_ASSERT(n_ports == 0);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_bad_port_index(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES PREFIX_LV2EV
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "doap:homepage <http://example.org/someplug> ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index \"notaninteger\" ; lv2:symbol \"invalid\" ;"
+ " lv2:name \"Invalid\" ; "
+ "] .")) {
+ return 0;
+ }
+
+ init_uris();
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+
+ uint32_t n_ports = lilv_plugin_get_num_ports(plug);
+ TEST_ASSERT(n_ports == 0);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_string(void)
+{
+ char* s = NULL;
+
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/foo/bar")), "/foo")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/foo/bar/")), "/foo")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/foo///bar/")), "/foo")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/foo///bar//")), "/foo")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("foo")), ".")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/foo")), "/")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/")), "/")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("//")), "/")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_relative_to("/a/b", "/a/")), "b")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_relative_to("/a", "/b/c/")), "/a")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_relative_to("/a/b/c", "/a/b/d/")), "../c")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_relative_to("/a/b/c", "/a/b/d/e/")), "../../c")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join("/a", "b")), "/a/b")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join("/a", "/b")), "/a/b")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join("/a/", "/b")), "/a/b")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join("/a/", "b")), "/a/b")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join("/a", NULL)), "/a/")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join(NULL, "/b")), "/b")); free(s);
+
+#ifndef _WIN32
+ setenv("LILV_TEST_1", "test", 1);
+ char* home_foo = lilv_strjoin(getenv("HOME"), "/foo", NULL);
+ TEST_ASSERT(!strcmp((s = lilv_expand("$LILV_TEST_1")), "test")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_expand("~")), getenv("HOME"))); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_expand("~foo")), "~foo")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_expand("~/foo")), home_foo)); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_expand("$NOT_A_VAR")), "$NOT_A_VAR")); free(s);
+ free(home_foo);
+ unsetenv("LILV_TEST_1");
+#endif
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_world(void)
+{
+ if (!init_world()) {
+ return 0;
+ }
+
+ LilvNode* num = lilv_new_int(world, 4);
+ LilvNode* uri = lilv_new_uri(world, "http://example.org/object");
+
+ LilvNodes* matches = lilv_world_find_nodes(world, num, NULL, NULL);
+ TEST_ASSERT(!matches);
+
+ matches = lilv_world_find_nodes(world, NULL, num, NULL);
+ TEST_ASSERT(!matches);
+
+ matches = lilv_world_find_nodes(world, NULL, uri, NULL);
+ TEST_ASSERT(!matches);
+
+ lilv_node_free(uri);
+ lilv_node_free(num);
+
+ lilv_world_unload_bundle(world, NULL);
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_reload_bundle(void)
+{
+ // Create a simple plugin bundle
+ create_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("First name") " .");
+
+ if (!init_world()) {
+ return 0;
+ }
+
+ init_uris();
+ lilv_world_load_specifications(world);
+
+ // Load bundle
+ LilvNode* bundle_uri = lilv_new_uri(world, bundle_dir_uri);
+ lilv_world_load_bundle(world, bundle_uri);
+
+ // Check that plugin is present
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ // Check that plugin name is correct
+ LilvNode* name = lilv_plugin_get_name(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "First name"));
+ lilv_node_free(name);
+
+ // Unload bundle from world and delete it
+ lilv_world_unload_bundle(world, bundle_uri);
+ delete_bundle();
+
+ // Create a new version of the same bundle, but with a different name
+ create_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Second name") " .");
+
+ // Check that plugin is no longer in the world's plugin list
+ TEST_ASSERT(lilv_plugins_size(plugins) == 0);
+
+ // Load new bundle
+ lilv_world_load_bundle(world, bundle_uri);
+
+ // Check that plugin is present again and is the same LilvPlugin
+ const LilvPlugin* plug2 = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug2);
+ TEST_ASSERT(plug2 == plug);
+
+ // Check that plugin now has new name
+ LilvNode* name2 = lilv_plugin_get_name(plug2);
+ TEST_ASSERT(name2);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name2), "Second name"));
+ lilv_node_free(name2);
+
+ // Load new bundle again (noop)
+ lilv_world_load_bundle(world, bundle_uri);
+
+ cleanup_uris();
+ lilv_node_free(bundle_uri);
+ lilv_world_free(world);
+ world = NULL;
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_replace_version(void)
+{
+ if (!init_world()) {
+ return 0;
+ }
+
+ LilvNode* plug_uri = lilv_new_uri(world, "http://example.org/versioned");
+ LilvNode* lv2_minorVersion = lilv_new_uri(world, LV2_CORE__minorVersion);
+ LilvNode* lv2_microVersion = lilv_new_uri(world, LV2_CORE__microVersion);
+ LilvNode* minor = NULL;
+ LilvNode* micro = NULL;
+
+ char* old_bundle_path = lilv_strjoin(LILV_TEST_DIR, "old_version.lv2/", 0);
+
+ // Load plugin from old bundle
+ LilvNode* old_bundle = lilv_new_file_uri(world, NULL, old_bundle_path);
+ lilv_world_load_bundle(world, old_bundle);
+ lilv_world_load_resource(world, plug_uri);
+
+ // Check version
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* old_plug = lilv_plugins_get_by_uri(plugins, plug_uri);
+ TEST_ASSERT(old_plug);
+ minor = lilv_world_get(world, plug_uri, lv2_minorVersion, 0);
+ micro = lilv_world_get(world, plug_uri, lv2_microVersion, 0);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(minor), "1"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(micro), "0"));
+ lilv_node_free(micro);
+ lilv_node_free(minor);
+
+ char* new_bundle_path = lilv_strjoin(LILV_TEST_DIR, "new_version.lv2/", 0);
+
+ // Load plugin from new bundle
+ LilvNode* new_bundle = lilv_new_file_uri(world, NULL, new_bundle_path);
+ lilv_world_load_bundle(world, new_bundle);
+ lilv_world_load_resource(world, plug_uri);
+
+ // Check that version in the world model has changed
+ plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* new_plug = lilv_plugins_get_by_uri(plugins, plug_uri);
+ TEST_ASSERT(new_plug);
+ TEST_ASSERT(lilv_node_equals(lilv_plugin_get_bundle_uri(new_plug), new_bundle));
+ minor = lilv_world_get(world, plug_uri, lv2_minorVersion, 0);
+ micro = lilv_world_get(world, plug_uri, lv2_microVersion, 0);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(minor), "2"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(micro), "1"));
+ lilv_node_free(micro);
+ lilv_node_free(minor);
+
+ // Try to load the old version again
+ lilv_world_load_bundle(world, old_bundle);
+ lilv_world_load_resource(world, plug_uri);
+
+ // Check that version in the world model has not changed
+ plugins = lilv_world_get_all_plugins(world);
+ new_plug = lilv_plugins_get_by_uri(plugins, plug_uri);
+ TEST_ASSERT(new_plug);
+ minor = lilv_world_get(world, plug_uri, lv2_minorVersion, 0);
+ micro = lilv_world_get(world, plug_uri, lv2_microVersion, 0);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(minor), "2"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(micro), "1"));
+ lilv_node_free(micro);
+ lilv_node_free(minor);
+
+ lilv_node_free(new_bundle);
+ lilv_node_free(old_bundle);
+ free(new_bundle_path);
+ free(old_bundle_path);
+ lilv_node_free(plug_uri);
+ lilv_node_free(lv2_minorVersion);
+ lilv_node_free(lv2_microVersion);
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_get_symbol(void)
+{
+ if (!start_bundle(
+ MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:symbol \"plugsym\" ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES PREFIX_LV2EV
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ "lv2:symbol \"plugsym\" .")) {
+ return 0;
+ }
+
+ init_uris();
+
+ LilvNode* plug_sym = lilv_world_get_symbol(world, plugin_uri_value);
+ LilvNode* path = lilv_new_uri(world, "http://example.org/foo");
+ LilvNode* path_sym = lilv_world_get_symbol(world, path);
+ LilvNode* query = lilv_new_uri(world, "http://example.org/foo?bar=baz");
+ LilvNode* query_sym = lilv_world_get_symbol(world, query);
+ LilvNode* frag = lilv_new_uri(world, "http://example.org/foo#bar");
+ LilvNode* frag_sym = lilv_world_get_symbol(world, frag);
+ LilvNode* queryfrag = lilv_new_uri(world, "http://example.org/foo?bar=baz#quux");
+ LilvNode* queryfrag_sym = lilv_world_get_symbol(world, queryfrag);
+ LilvNode* nonuri = lilv_new_int(world, 42);
+
+ TEST_ASSERT(lilv_world_get_symbol(world, nonuri) == NULL);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(plug_sym), "plugsym"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(path_sym), "foo"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(query_sym), "bar_baz"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(frag_sym), "bar"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(queryfrag_sym), "quux"));
+
+ lilv_node_free(nonuri);
+ lilv_node_free(queryfrag_sym);
+ lilv_node_free(queryfrag);
+ lilv_node_free(frag_sym);
+ lilv_node_free(frag);
+ lilv_node_free(query_sym);
+ lilv_node_free(query);
+ lilv_node_free(path_sym);
+ lilv_node_free(path);
+ lilv_node_free(plug_sym);
+ cleanup_uris();
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+/* add tests here */
+static struct TestCase tests[] = {
+ TEST_CASE(util),
+ TEST_CASE(value),
+ TEST_CASE(verify),
+ TEST_CASE(no_verify),
+ TEST_CASE(discovery),
+ TEST_CASE(lv2_path),
+ TEST_CASE(classes),
+ TEST_CASE(plugin),
+ TEST_CASE(project),
+ TEST_CASE(no_author),
+ TEST_CASE(project_no_author),
+ TEST_CASE(preset),
+ TEST_CASE(prototype),
+ TEST_CASE(port),
+ TEST_CASE(ui),
+ TEST_CASE(bad_port_symbol),
+ TEST_CASE(bad_port_index),
+ TEST_CASE(bad_port_index),
+ TEST_CASE(string),
+ TEST_CASE(world),
+ TEST_CASE(state),
+ TEST_CASE(reload_bundle),
+ TEST_CASE(replace_version),
+ TEST_CASE(get_symbol),
+ { NULL, NULL }
+};
+
+static void
+run_tests(void)
+{
+ int i;
+ for (i = 0; tests[i].title; i++) {
+ printf("*** Test %s\n", tests[i].title);
+ if (!tests[i].func()) {
+ printf("\nTest failed\n");
+ /* test case that wasn't able to be executed at all counts as 1 test + 1 error */
+ error_count++;
+ test_count++;
+ }
+ unload_bundle();
+ cleanup();
+ }
+}
+
+int
+main(int argc, char* argv[])
+{
+ if (argc != 1) {
+ printf("Syntax: %s\n", argv[0]);
+ return 0;
+ }
+ setenv("LANG", "C", 1);
+ init_tests();
+ run_tests();
+ cleanup();
+ printf("\n*** Test Results: %d tests, %d errors\n\n", test_count, error_count);
+ return error_count ? 1 : 0;
+}