summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/ROOT0
-rw-r--r--tests/TARGETS6
-rw-r--r--tests/test_cases/cflags/TARGETS19
-rw-r--r--tests/test_cases/cflags/public/TARGETS49
-rw-r--r--tests/test_cases/cflags/public/half.cpp5
-rw-r--r--tests/test_cases/cflags/public/half.hpp12
-rw-r--r--tests/test_cases/cflags/public/half3.cpp3
-rw-r--r--tests/test_cases/cflags/public/half3.hpp8
-rw-r--r--tests/test_cases/cflags/public/half3f.cpp7
-rw-r--r--tests/test_cases/cflags/public/half3f.hpp6
-rw-r--r--tests/test_cases/cflags/public/main_use_half3.cpp7
-rw-r--r--tests/test_cases/cflags/public/main_use_half3f.cpp11
-rw-r--r--tests/test_cases/cflags/public/test_use_half3.cpp6
-rw-r--r--tests/test_cases/cflags/public/test_use_half3f.cpp10
-rw-r--r--tests/test_cases/deps/TARGETS197
-rw-r--r--tests/test_cases/deps/cmake/TARGETS76
-rw-r--r--tests/test_cases/deps/cmake/main.cpp8
-rw-r--r--tests/test_cases/deps/cmake/test.cpp3
-rw-r--r--tests/test_cases/deps/cmake/test.sh5
-rw-r--r--tests/test_cases/deps/prebuilt/TARGETS158
-rw-r--r--tests/test_cases/deps/prebuilt/bar.hpp8
-rw-r--r--tests/test_cases/deps/prebuilt/foo.hpp8
-rw-r--r--tests/test_cases/deps/private/TARGETS36
-rw-r--r--tests/test_cases/deps/private/bar.cpp8
-rw-r--r--tests/test_cases/deps/private/bar.hpp6
-rw-r--r--tests/test_cases/deps/private/foo.cpp7
-rw-r--r--tests/test_cases/deps/private/foo.hpp6
-rw-r--r--tests/test_cases/deps/private/main_includes_foo.cpp9
-rw-r--r--tests/test_cases/deps/private/main_links_bar_foo.cpp9
-rw-r--r--tests/test_cases/deps/private/main_links_foo.cpp10
-rw-r--r--tests/test_cases/deps/public/TARGETS36
-rw-r--r--tests/test_cases/deps/public/bar.cpp7
-rw-r--r--tests/test_cases/deps/public/bar.hpp8
-rw-r--r--tests/test_cases/deps/public/foo.cpp7
-rw-r--r--tests/test_cases/deps/public/foo.hpp8
-rw-r--r--tests/test_cases/deps/public/main_includes_foo.cpp9
-rw-r--r--tests/test_cases/deps/public/main_links_bar_foo.cpp10
-rw-r--r--tests/test_cases/deps/public/main_links_foo.cpp10
-rw-r--r--tests/test_cases/deps/shared/TARGETS104
-rw-r--r--tests/test_cases/deps/shared/bar.cpp8
-rw-r--r--tests/test_cases/deps/shared/bar.hpp6
-rw-r--r--tests/test_cases/deps/shared/baz.cpp8
-rw-r--r--tests/test_cases/deps/shared/baz.hpp6
-rw-r--r--tests/test_cases/deps/shared/foo.cpp7
-rw-r--r--tests/test_cases/deps/shared/foo.hpp6
-rw-r--r--tests/test_cases/deps/shared/main_uses_bar.cpp8
-rw-r--r--tests/test_cases/deps/shared/main_uses_foo.cpp8
-rw-r--r--tests/test_cases/deps/shared/test_diamond.cpp4
-rw-r--r--tests/test_cases/deps/shared/test_uses_bar.cpp3
-rw-r--r--tests/test_cases/deps/shared/test_uses_baz.cpp3
-rw-r--r--tests/test_cases/deps/shared/test_uses_foo.cpp3
-rw-r--r--tests/test_cases/deps/shared/test_uses_main.sh7
-rw-r--r--tests/test_rules/EXPRESSIONS46
-rw-r--r--tests/test_rules/README.md28
-rw-r--r--tests/test_rules/RULES204
-rw-r--r--tests/test_rules/TARGETS1
-rwxr-xr-xtests/test_rules/test_runner.py89
57 files changed, 1347 insertions, 0 deletions
diff --git a/tests/ROOT b/tests/ROOT
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/ROOT
diff --git a/tests/TARGETS b/tests/TARGETS
new file mode 100644
index 0000000..c64245e
--- /dev/null
+++ b/tests/TARGETS
@@ -0,0 +1,6 @@
+{ "ALL":
+ { "type": "install"
+ , "deps": [["test_cases/deps", "ALL"], ["test_cases/cflags", "ALL"]]
+ , "tainted": ["test"]
+ }
+}
diff --git a/tests/test_cases/cflags/TARGETS b/tests/test_cases/cflags/TARGETS
new file mode 100644
index 0000000..44c99f1
--- /dev/null
+++ b/tests/test_cases/cflags/TARGETS
@@ -0,0 +1,19 @@
+{ "public":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["cflags_public"]
+ , "targets":
+ [ "+main_use_half3"
+ , "+test_use_half3"
+ , "+main_use_half3f"
+ , "+test_use_half3f"
+ ]
+ , "asserts":
+ [ "./main_use_half3/main_use_half3 | grep 1.5"
+ , "[ \"$(cat ./test_use_half3/result)\" = \"PASS\" ]"
+ , "./main_use_half3f/main_use_half3f | grep 1.5"
+ , "[ \"$(cat ./test_use_half3f/result)\" = \"PASS\" ]"
+ ]
+ , "data": [["TREE", null, "public"]]
+ }
+, "ALL": {"type": "install", "deps": ["public"], "tainted": ["test"]}
+}
diff --git a/tests/test_cases/cflags/public/TARGETS b/tests/test_cases/cflags/public/TARGETS
new file mode 100644
index 0000000..79aadb3
--- /dev/null
+++ b/tests/test_cases/cflags/public/TARGETS
@@ -0,0 +1,49 @@
+{ "half":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["half"]
+ , "hdrs": ["half.hpp"]
+ , "srcs": ["half.cpp"]
+ , "cflags": ["-DHALF_PRECISION_DOUBLE"]
+ , "stage": ["half"]
+ }
+, "half3":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["half3"]
+ , "hdrs": ["half3.hpp"]
+ , "srcs": ["half3.cpp"]
+ , "deps": ["half"]
+ , "stage": ["half3"]
+ }
+, "half3f":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["half3f"]
+ , "hdrs": ["half3f.hpp"]
+ , "srcs": ["half3f.cpp"]
+ , "private-deps": ["half3"]
+ , "stage": ["half3f"]
+ }
+, "main_use_half3":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main_use_half3"]
+ , "srcs": ["main_use_half3.cpp"]
+ , "private-deps": ["half3"]
+ }
+, "test_use_half3":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_use_half3"]
+ , "srcs": ["test_use_half3.cpp"]
+ , "private-deps": ["half3"]
+ }
+, "main_use_half3f":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main_use_half3f"]
+ , "srcs": ["main_use_half3f.cpp"]
+ , "private-deps": ["half3f"]
+ }
+, "test_use_half3f":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_use_half3f"]
+ , "srcs": ["test_use_half3f.cpp"]
+ , "private-deps": ["half3f"]
+ }
+}
diff --git a/tests/test_cases/cflags/public/half.cpp b/tests/test_cases/cflags/public/half.cpp
new file mode 100644
index 0000000..e26ff6e
--- /dev/null
+++ b/tests/test_cases/cflags/public/half.cpp
@@ -0,0 +1,5 @@
+#include "half.hpp"
+
+HALF_RESULT_TYPE half(int val) {
+ return static_cast<HALF_RESULT_TYPE>(val / 2.0);
+}
diff --git a/tests/test_cases/cflags/public/half.hpp b/tests/test_cases/cflags/public/half.hpp
new file mode 100644
index 0000000..0baebcb
--- /dev/null
+++ b/tests/test_cases/cflags/public/half.hpp
@@ -0,0 +1,12 @@
+#ifndef HALF_HPP
+#define HALF_HPP
+
+#ifdef HALF_PRECISION_DOUBLE
+#define HALF_RESULT_TYPE double
+#else
+#define HALF_RESULT_TYPE int
+#endif
+
+HALF_RESULT_TYPE half(int val);
+
+#endif
diff --git a/tests/test_cases/cflags/public/half3.cpp b/tests/test_cases/cflags/public/half3.cpp
new file mode 100644
index 0000000..484f7f8
--- /dev/null
+++ b/tests/test_cases/cflags/public/half3.cpp
@@ -0,0 +1,3 @@
+#include "half3.hpp"
+
+HALF_RESULT_TYPE half3() { return half(3); }
diff --git a/tests/test_cases/cflags/public/half3.hpp b/tests/test_cases/cflags/public/half3.hpp
new file mode 100644
index 0000000..271ed68
--- /dev/null
+++ b/tests/test_cases/cflags/public/half3.hpp
@@ -0,0 +1,8 @@
+#ifndef HALF3_HPP
+#define HALF3_HPP
+
+#include "half/half.hpp"
+
+HALF_RESULT_TYPE half3();
+
+#endif
diff --git a/tests/test_cases/cflags/public/half3f.cpp b/tests/test_cases/cflags/public/half3f.cpp
new file mode 100644
index 0000000..88b6d55
--- /dev/null
+++ b/tests/test_cases/cflags/public/half3f.cpp
@@ -0,0 +1,7 @@
+#include "half3/half3.hpp"
+
+#ifndef HALF_PRECISION_DOUBLE
+#error should be defined
+#endif
+
+float half3f() { return static_cast<float>(half3()); }
diff --git a/tests/test_cases/cflags/public/half3f.hpp b/tests/test_cases/cflags/public/half3f.hpp
new file mode 100644
index 0000000..1207f63
--- /dev/null
+++ b/tests/test_cases/cflags/public/half3f.hpp
@@ -0,0 +1,6 @@
+#ifndef BAZ_HPP
+#define BAZ_HPP
+
+float half3f();
+
+#endif
diff --git a/tests/test_cases/cflags/public/main_use_half3.cpp b/tests/test_cases/cflags/public/main_use_half3.cpp
new file mode 100644
index 0000000..1c9d6ba
--- /dev/null
+++ b/tests/test_cases/cflags/public/main_use_half3.cpp
@@ -0,0 +1,7 @@
+#include "half3/half3.hpp"
+#include <iostream>
+
+int main() {
+ std::cout << half3() << std::endl;
+ return 0;
+}
diff --git a/tests/test_cases/cflags/public/main_use_half3f.cpp b/tests/test_cases/cflags/public/main_use_half3f.cpp
new file mode 100644
index 0000000..3e855cd
--- /dev/null
+++ b/tests/test_cases/cflags/public/main_use_half3f.cpp
@@ -0,0 +1,11 @@
+#include "half3f/half3f.hpp"
+#include <iostream>
+
+#ifdef HALF_PRECISION_DOUBLE
+#error should not be defined
+#endif
+
+int main() {
+ std::cout << half3f() << std::endl;
+ return 0;
+}
diff --git a/tests/test_cases/cflags/public/test_use_half3.cpp b/tests/test_cases/cflags/public/test_use_half3.cpp
new file mode 100644
index 0000000..1cf50ad
--- /dev/null
+++ b/tests/test_cases/cflags/public/test_use_half3.cpp
@@ -0,0 +1,6 @@
+#include "half3/half3.hpp"
+
+int main() {
+ auto result = half3();
+ return result > 1 and result < 2 ? 0 : 1;
+}
diff --git a/tests/test_cases/cflags/public/test_use_half3f.cpp b/tests/test_cases/cflags/public/test_use_half3f.cpp
new file mode 100644
index 0000000..078e69c
--- /dev/null
+++ b/tests/test_cases/cflags/public/test_use_half3f.cpp
@@ -0,0 +1,10 @@
+#include "half3f/half3f.hpp"
+
+#ifdef HALF_PRECISION_DOUBLE
+#error should not be defined
+#endif
+
+int main() {
+ auto result = half3f();
+ return result > 1 and result < 2 ? 0 : 1;
+}
diff --git a/tests/test_cases/deps/TARGETS b/tests/test_cases/deps/TARGETS
new file mode 100644
index 0000000..10a6749
--- /dev/null
+++ b/tests/test_cases/deps/TARGETS
@@ -0,0 +1,197 @@
+{ "private":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["deps_private"]
+ , "targets":
+ [ "+foo"
+ , "-main_includes_foo"
+ , "+main_links_foo"
+ , "+main_links_bar_foo"
+ , "+install_bar"
+ ]
+ , "asserts":
+ [ "test -f foo/foo/libfoo.a"
+ , "test -f foo/foo/foo.hpp"
+ , "! test -f foo/bar/bar.hpp"
+ , "./main_links_foo/main | grep foo"
+ , "./main_links_bar_foo/main | grep bar"
+ , "./main_links_bar_foo/main | grep foo"
+ , "test -f install_bar/lib/bar/libbar.a"
+ , "test -f install_bar/include/bar/bar.hpp"
+ , "test -f install_bar/lib/foo/libfoo.a"
+ , "! test -f install_bar/include/foo/foo.hpp"
+ ]
+ , "data": [["TREE", null, "private"]]
+ }
+, "public":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["deps_public"]
+ , "targets":
+ [ "+foo"
+ , "+main_includes_foo"
+ , "+main_links_foo"
+ , "+main_links_bar_foo"
+ , "+install_bar"
+ ]
+ , "asserts":
+ [ "test -f foo/foo/libfoo.a"
+ , "test -f foo/foo/foo.hpp"
+ , "! test -f foo/bar/bar.hpp"
+ , "./main_includes_foo/main | grep main"
+ , "./main_links_foo/main | grep foo"
+ , "./main_links_bar_foo/main | grep bar"
+ , "./main_links_bar_foo/main | grep foo"
+ , "test -f install_bar/lib/bar/libbar.a"
+ , "test -f install_bar/include/bar/bar.hpp"
+ , "test -f install_bar/lib/foo/libfoo.a"
+ , "test -f install_bar/include/foo/foo.hpp"
+ ]
+ , "data": [["TREE", null, "public"]]
+ }
+, "shared":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["deps_shared"]
+ , "targets":
+ [ "+foo"
+ , "+bar"
+ , "+main_uses_foo"
+ , "+test_uses_foo"
+ , "+main_uses_bar"
+ , "+test_uses_bar"
+ , "+test_uses_bar_s"
+ , "+test_uses_baz"
+ , "+test_uses_main"
+ , "+test_diamond"
+ , "+install_foo"
+ , "+install_bar_s"
+ , "+install_baz"
+ , "+install_main"
+ ]
+ , "asserts":
+ [ "test -f foo/libfoo.so.1.2.3"
+ , "test -f foo/foo/foo.hpp"
+ , "test -f bar/libbar.so"
+ , "test -f bar/bar/bar.hpp"
+ , "! test -f bar/foo/foo.hpp"
+ , "test -f install_foo/lib/libfoo.so.1.2.3"
+ , "test -f install_foo/include/foo/foo.hpp"
+ , "grep 'Name: foo' install_foo/lib/pkgconfig/foo.pc"
+ , "grep 'Version: 1.2.3' install_foo/lib/pkgconfig/foo.pc"
+ , "grep -- '-L${libdir}' install_foo/lib/pkgconfig/foo.pc"
+ , "grep -- '-l:libfoo.so.1.2.3' install_foo/lib/pkgconfig/foo.pc"
+ , "test -f install_bar_s/lib/bar/libbar.a"
+ , "test -f install_bar_s/include/bar/bar.hpp"
+ , "test -f install_bar_s/lib/libfoo.so.1.2.3"
+ , "! test -f install_bar_s/include/foo/foo.hpp"
+ , "grep 'Name: bar' install_bar_s/lib/pkgconfig/bar.pc"
+ , "grep '${libdir}/bar/libbar.a' install_bar_s/lib/pkgconfig/bar.pc"
+ , "grep '${libdir}/libfoo.so.1.2.3' install_bar_s/lib/pkgconfig/bar.pc"
+ , "test -f install_baz/lib/libbaz.so"
+ , "test -f install_baz/include/baz/baz.hpp"
+ , "! test -f install_baz/lib/foo/libfoo.a"
+ , "! test -f install_baz/include/foo/foo.hpp"
+ , "./install_main/bin/main_uses_bar | grep main"
+ , "./install_main/bin/main_uses_bar | grep bar"
+ , "./install_main/bin/main_uses_bar | grep foo"
+ , "! test -d install_main/include"
+ ]
+ , "data": [["TREE", null, "shared"]]
+ }
+, "prebuilt_tests":
+ { "type": "tree"
+ , "deps":
+ [ ["test_cases/deps/prebuilt", "foo.hpp"]
+ , ["test_cases/deps/prebuilt", "bar.hpp"]
+ , ["test_cases/deps/prebuilt", "TARGETS"]
+ , ["TREE", null, "shared"]
+ ]
+ }
+, "prebuilt":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["deps_prebuilt"]
+ , "targets":
+ [ "+foo"
+ , "+bar"
+ , "+main_uses_foo"
+ , "+test_uses_foo"
+ , "+main_uses_bar"
+ , "+test_uses_bar"
+ , "+test_uses_bar_s"
+ , "+test_uses_main"
+ , "+install_foo"
+ , "+install_bar"
+ , "+install_main"
+ , "+test_uses_foobar"
+ , "+test_uses_foobar_s"
+ , "+install_foobar"
+ ]
+ , "asserts":
+ [ "test -f foo/libfoo.so.1.2.3"
+ , "test -f foo/foo/foo.hpp"
+ , "test -f bar/libbar.so"
+ , "test -f bar/bar/bar.hpp"
+ , "test -f install_foo/lib/pkgconfig/foo/foo.cflags"
+ , "test -f install_foo/lib/pkgconfig/foo/foo.ldflags"
+ , "test -f install_bar/lib/pkgconfig/foo/foo.cflags"
+ , "test -f install_bar/lib/pkgconfig/foo/foo.ldflags"
+ , "test -f install_bar/lib/pkgconfig/bar/bar.cflags"
+ , "test -f install_bar/lib/pkgconfig/bar/bar.ldflags"
+ , "./install_main/bin/main_uses_bar | grep main"
+ , "./install_main/bin/main_uses_bar | grep bar"
+ , "./install_main/bin/main_uses_bar | grep foo"
+ , "test -f install_foobar/lib/libfoo.so.1.2.3"
+ , "test -f install_foobar/lib/libbar.so"
+ , "test -f install_foobar/lib/pkgconfig/foobar.pc"
+ , "grep 'Cflags:.*@${prefix}/lib/pkgconfig/bar/foobar.cflags' install_foobar/lib/pkgconfig/foobar.pc"
+ , "grep -- '-DUSE_BAR=1 -DUSE_FOO=1' install_foobar/lib/pkgconfig/bar/foobar.cflags"
+ , "grep 'Libs:.*@${prefix}/lib/pkgconfig/bar/foobar.ldflags' install_foobar/lib/pkgconfig/foobar.pc"
+ , "grep -- '-lm -lpthread' install_foobar/lib/pkgconfig/bar/foobar.ldflags"
+ ]
+ , "data": ["prebuilt_tests"]
+ }
+, "cmake":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["deps_cmake"]
+ , "libs": ["googletest", "libz", "libcurl"]
+ , "targets":
+ [ "+gtest"
+ , "+test"
+ , "+gtest_main"
+ , "+testbin"
+ , "+shell_test"
+ , "+install_gtest"
+ , "+install_gtest_main"
+ , "+install_testbin"
+ , "+install_libcurl"
+ ]
+ , "asserts":
+ [ "test -f gtest/libgtest.a"
+ , "test -f gtest/gtest/gtest.h"
+ , "grep 'PASSED.*1 test' test/stdout"
+ , "test -f gtest_main/libgtest.so.1.13.0"
+ , "test -f gtest_main/libgtest_main.so.1.13.0"
+ , "test -f gtest_main/gtest/gtest.h"
+ , "test -f ./testbin/test"
+ , "grep 'PASSED.*1 test' shell_test/stdout"
+ , "test -f install_gtest/lib/libgtest.a"
+ , "test -f install_gtest/include/gtest/gtest.h"
+ , "grep 'Cflags.*lib/pkgconfig/gtest.cflags' install_gtest/lib/pkgconfig/gtest.pc"
+ , "grep 'Libs.*libgtest.a' install_gtest/lib/pkgconfig/gtest.pc"
+ , "grep 'Libs.*lib/pkgconfig/gtest.ldflags' install_gtest/lib/pkgconfig/gtest.pc"
+ , "test -f install_gtest_main/lib/libgtest.so.1.13.0"
+ , "test -f install_gtest_main/lib/libgtest_main.so.1.13.0"
+ , "test -f install_gtest_main/include/gtest/gtest.h"
+ , "grep 'Cflags.*lib/pkgconfig/gtest_main.cflags' install_gtest_main/lib/pkgconfig/gtest_main.pc"
+ , "grep 'Libs.*libgtest.so.1.13.0' install_gtest_main/lib/pkgconfig/gtest_main.pc"
+ , "grep 'Libs.*libgtest_main.so.1.13.0' install_gtest_main/lib/pkgconfig/gtest_main.pc"
+ , "grep 'Libs.*lib/pkgconfig/gtest_main.ldflags' install_gtest_main/lib/pkgconfig/gtest_main.pc"
+ , "./install_testbin/bin/test | grep 'PASSED.*1 test'"
+ , "path=$(ldd install_libcurl/lib/libcurl.so.4.8.0 | awk '/libz/{print $3}') && test -z \"${path##$(pwd)*}\""
+ ]
+ , "data": [["TREE", null, "cmake"]]
+ }
+, "ALL":
+ { "type": "install"
+ , "deps": ["private", "public", "shared", "prebuilt", "cmake"]
+ , "tainted": ["test"]
+ }
+}
diff --git a/tests/test_cases/deps/cmake/TARGETS b/tests/test_cases/deps/cmake/TARGETS
new file mode 100644
index 0000000..2c02f53
--- /dev/null
+++ b/tests/test_cases/deps/cmake/TARGETS
@@ -0,0 +1,76 @@
+{ "gtest":
+ { "type": ["@", "rules", "CC/foreign/cmake", "library"]
+ , "name": ["gtest"]
+ , "version": ["1", "13", "0"]
+ , "project": [["@", "googletest", "", "tree"]]
+ , "out_hdr_dirs": ["gtest"]
+ , "out_libs": ["libgtest.a"]
+ , "pkg-config": ["gtest.pc"]
+ }
+, "testlib":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["testlib"]
+ , "shared": ["yes"]
+ , "srcs": ["main.cpp"]
+ , "private-deps": ["gtest"]
+ }
+, "test":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test"]
+ , "private-deps": ["testlib", "gtest"]
+ }
+, "gtest_main":
+ { "type": ["@", "rules", "CC/foreign/cmake", "library"]
+ , "name": ["gtest_main"]
+ , "version": ["1", "13", "0"]
+ , "project": [["@", "googletest", "", "tree"]]
+ , "defines": ["BUILD_SHARED_LIBS=ON"]
+ , "out_hdr_dirs": ["gtest"]
+ , "out_libs": ["libgtest_main.so.1.13.0", "libgtest.so.1.13.0"]
+ , "pkg-config": ["gtest_main.pc", "gtest.pc"]
+ }
+, "testbin":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["test"]
+ , "srcs": ["test.cpp"]
+ , "private-deps": ["gtest_main"]
+ }
+, "shell_test":
+ { "type": ["@", "rules", "shell/test", "script"]
+ , "name": ["shell_test"]
+ , "test": ["test.sh"]
+ , "deps": ["testbin"]
+ }
+, "install_gtest":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["gtest"]}
+, "install_gtest_main":
+ { "type": ["@", "rules", "CC", "install-with-deps"]
+ , "targets": ["gtest_main"]
+ }
+, "install_testbin":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["testbin"]}
+, "libz":
+ { "type": ["@", "rules", "CC/foreign/cmake", "library"]
+ , "name": ["libz"]
+ , "version": ["1", "2", "13"]
+ , "project": [["@", "libz", "", "tree"]]
+ , "defines": ["BUILD_SHARED_LIBS=ON"]
+ , "out_hdrs": ["zconf.h", "zlib.h"]
+ , "out_libs": ["libz.so", "libz.so.1", "libz.so.1.2.13"]
+ , "pc_prefix": ["share/pkgconfig"]
+ , "pkg-config": ["zlib.pc"]
+ }
+, "libcurl":
+ { "type": ["@", "rules", "CC/foreign/cmake", "library"]
+ , "name": ["libcurl"]
+ , "version": ["8", "0", "1"]
+ , "project": [["@", "libcurl", "", "tree"]]
+ , "defines": ["BUILD_SHARED_LIBS=ON", "CURL_ENABLE_SSL=OFF", "USE_ZLIB=ON"]
+ , "out_hdr_dirs": ["curl"]
+ , "out_libs": ["libcurl.so.4.8.0"]
+ , "pkg-config": ["libcurl.pc"]
+ , "deps": ["libz"]
+ }
+, "install_libcurl":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["libcurl"]}
+}
diff --git a/tests/test_cases/deps/cmake/main.cpp b/tests/test_cases/deps/cmake/main.cpp
new file mode 100644
index 0000000..575a358
--- /dev/null
+++ b/tests/test_cases/deps/cmake/main.cpp
@@ -0,0 +1,8 @@
+#include <gtest/gtest.h>
+
+TEST(CastTest, float) { EXPECT_EQ(42.0f, float(42)); }
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/tests/test_cases/deps/cmake/test.cpp b/tests/test_cases/deps/cmake/test.cpp
new file mode 100644
index 0000000..b1811d8
--- /dev/null
+++ b/tests/test_cases/deps/cmake/test.cpp
@@ -0,0 +1,3 @@
+#include <gtest/gtest.h>
+
+TEST(CastTest, double) { EXPECT_EQ(42.0, double(42)); }
diff --git a/tests/test_cases/deps/cmake/test.sh b/tests/test_cases/deps/cmake/test.sh
new file mode 100644
index 0000000..80b7965
--- /dev/null
+++ b/tests/test_cases/deps/cmake/test.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+set -e
+
+./test | grep PASSED
diff --git a/tests/test_cases/deps/prebuilt/TARGETS b/tests/test_cases/deps/prebuilt/TARGETS
new file mode 100644
index 0000000..c923995
--- /dev/null
+++ b/tests/test_cases/deps/prebuilt/TARGETS
@@ -0,0 +1,158 @@
+{ "foo.pc":
+ { "type": "file_gen"
+ , "name": "foo.pc"
+ , "data":
+ { "type": "join"
+ , "separator": "\n"
+ , "$1":
+ [ "Name: foo"
+ , "Version: 1.2.3"
+ , "Description: test prebuilt foo"
+ , "URL: unknown"
+ , "Cflags: -I/usr/include -DUSE_FOO=1"
+ , "Libs: -L/usr/lib -lfoo -lpthread"
+ ]
+ }
+ }
+, "foo":
+ { "type": ["@", "rules", "CC/prebuilt", "library"]
+ , "name": ["foo"]
+ , "version": ["1", "2", "3"]
+ , "hdrs": ["foo.hpp"]
+ , "lib": [["shared", "foo"]]
+ , "pkg-config": ["foo.pc"]
+ , "stage": ["foo"]
+ }
+, "foo_s":
+ { "type": ["@", "rules", "CC/prebuilt", "library"]
+ , "name": ["foo"]
+ , "version": ["1", "2", "3"]
+ , "hdrs": ["foo.hpp"]
+ , "lib": [["shared", "foo_s"]]
+ , "pkg-config": ["foo.pc"]
+ , "stage": ["foo"]
+ }
+, "bar.pc":
+ { "type": "file_gen"
+ , "name": "bar.pc"
+ , "data":
+ { "type": "join"
+ , "separator": "\n"
+ , "$1":
+ [ "Name: bar"
+ , "Version: unknown"
+ , "Description: test prebuilt bar"
+ , "URL: unknown"
+ , "Cflags: -I/usr/include -DUSE_BAR=1"
+ , "Libs: -L/usr/lib -lbar -lpthread -lm"
+ ]
+ }
+ }
+, "bar":
+ { "type": ["@", "rules", "CC/prebuilt", "library"]
+ , "name": ["bar"]
+ , "hdrs": ["bar.hpp"]
+ , "lib": [["shared", "bar"]]
+ , "pkg-config": ["bar.pc"]
+ , "stage": ["bar"]
+ , "deps": ["foo"]
+ }
+, "bar_s":
+ { "type": ["@", "rules", "CC/prebuilt", "library"]
+ , "name": ["bar"]
+ , "hdrs": ["bar.hpp"]
+ , "lib": [["shared", "bar_s"]]
+ , "pkg-config": ["bar.pc"]
+ , "stage": ["bar"]
+ , "deps": ["foo"]
+ }
+, "main_uses_foo":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main_uses_foo"]
+ , "srcs": [["shared", "main_uses_foo.cpp"]]
+ , "private-deps": ["foo"]
+ }
+, "test_uses_foo":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_foo"]
+ , "srcs": [["shared", "test_uses_foo.cpp"]]
+ , "private-deps": ["foo"]
+ }
+, "main_uses_bar":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main_uses_bar"]
+ , "srcs": [["shared", "main_uses_bar.cpp"]]
+ , "private-deps": ["bar"]
+ }
+, "test_uses_bar":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_bar"]
+ , "srcs": [["shared", "test_uses_bar.cpp"]]
+ , "private-deps": ["bar"]
+ }
+, "test_uses_bar_s":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_bar"]
+ , "srcs": [["shared", "test_uses_bar.cpp"]]
+ , "private-deps": ["bar_s"]
+ }
+, "test_uses_main":
+ { "type": ["@", "rules", "shell/test", "script"]
+ , "name": ["test_uses_main"]
+ , "test": [["shared", "test_uses_main.sh"]]
+ , "deps": ["main_uses_bar"]
+ }
+, "install_foo":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["foo"]}
+, "install_bar":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["bar"]}
+, "install_main":
+ { "type": ["@", "rules", "CC", "install-with-deps"]
+ , "targets": ["main_uses_bar"]
+ }
+, "foobar.pc":
+ { "type": "file_gen"
+ , "name": "foobar.pc"
+ , "data":
+ { "type": "join"
+ , "separator": "\n"
+ , "$1":
+ [ "Name: foobar"
+ , "Version: unknown"
+ , "Description: test compound prebuilt foobar"
+ , "URL: unknown"
+ , "Requires: bar, foo >= 1.2.3"
+ ]
+ }
+ }
+, "foobar":
+ { "type": ["@", "rules", "CC/prebuilt", "library"]
+ , "name": ["foobar"]
+ , "hdrs": ["bar.hpp"]
+ , "lib": [["shared", "bar"], ["shared", "foo"]]
+ , "pkg-config": ["foobar.pc", "foo.pc", "bar.pc"]
+ , "stage": ["bar"]
+ }
+, "foobar_s":
+ { "type": ["@", "rules", "CC/prebuilt", "library"]
+ , "name": ["foobar"]
+ , "hdrs": ["bar.hpp"]
+ , "lib": [["shared", "bar_s"], ["shared", "foo_s"]]
+ , "pkg-config": ["foobar.pc", "foo.pc", "bar.pc"]
+ , "stage": ["bar"]
+ }
+, "test_uses_foobar":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_bar"]
+ , "srcs": [["shared", "test_uses_bar.cpp"]]
+ , "private-deps": ["foobar"]
+ }
+, "test_uses_foobar_s":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_bar"]
+ , "srcs": [["shared", "test_uses_bar.cpp"]]
+ , "private-deps": ["foobar_s"]
+ }
+, "install_foobar":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["foobar"]}
+}
diff --git a/tests/test_cases/deps/prebuilt/bar.hpp b/tests/test_cases/deps/prebuilt/bar.hpp
new file mode 100644
index 0000000..243dba6
--- /dev/null
+++ b/tests/test_cases/deps/prebuilt/bar.hpp
@@ -0,0 +1,8 @@
+#ifndef BAR_HPP
+#define BAR_HPP
+
+#ifdef USE_BAR
+int bar();
+#endif
+
+#endif
diff --git a/tests/test_cases/deps/prebuilt/foo.hpp b/tests/test_cases/deps/prebuilt/foo.hpp
new file mode 100644
index 0000000..4f040d4
--- /dev/null
+++ b/tests/test_cases/deps/prebuilt/foo.hpp
@@ -0,0 +1,8 @@
+#ifndef FOO_HPP
+#define FOO_HPP
+
+#ifdef USE_FOO
+int foo();
+#endif
+
+#endif
diff --git a/tests/test_cases/deps/private/TARGETS b/tests/test_cases/deps/private/TARGETS
new file mode 100644
index 0000000..dd72948
--- /dev/null
+++ b/tests/test_cases/deps/private/TARGETS
@@ -0,0 +1,36 @@
+{ "foo":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["foo"]
+ , "hdrs": ["foo.hpp"]
+ , "srcs": ["foo.cpp"]
+ , "stage": ["foo"]
+ }
+, "bar":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["bar"]
+ , "hdrs": ["bar.hpp"]
+ , "srcs": ["bar.cpp"]
+ , "private-deps": ["foo"]
+ , "stage": ["bar"]
+ }
+, "main_includes_foo":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main"]
+ , "srcs": ["main_includes_foo.cpp"]
+ , "private-deps": ["bar"]
+ }
+, "main_links_foo":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main"]
+ , "srcs": ["main_links_foo.cpp"]
+ , "private-deps": ["bar"]
+ }
+, "main_links_bar_foo":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main"]
+ , "srcs": ["main_links_bar_foo.cpp"]
+ , "private-deps": ["bar"]
+ }
+, "install_bar":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["bar"]}
+}
diff --git a/tests/test_cases/deps/private/bar.cpp b/tests/test_cases/deps/private/bar.cpp
new file mode 100644
index 0000000..dfce0e8
--- /dev/null
+++ b/tests/test_cases/deps/private/bar.cpp
@@ -0,0 +1,8 @@
+#include "foo/foo.hpp"
+#include "bar.hpp"
+#include <iostream>
+
+int bar() {
+ std::cout << "bar\n";
+ return foo();
+}
diff --git a/tests/test_cases/deps/private/bar.hpp b/tests/test_cases/deps/private/bar.hpp
new file mode 100644
index 0000000..94d4f16
--- /dev/null
+++ b/tests/test_cases/deps/private/bar.hpp
@@ -0,0 +1,6 @@
+#ifndef BAR_HPP
+#define BAR_HPP
+
+int bar();
+
+#endif
diff --git a/tests/test_cases/deps/private/foo.cpp b/tests/test_cases/deps/private/foo.cpp
new file mode 100644
index 0000000..f985022
--- /dev/null
+++ b/tests/test_cases/deps/private/foo.cpp
@@ -0,0 +1,7 @@
+#include "foo.hpp"
+#include <iostream>
+
+int foo() {
+ std::cout << "foo\n";
+ return 0;
+}
diff --git a/tests/test_cases/deps/private/foo.hpp b/tests/test_cases/deps/private/foo.hpp
new file mode 100644
index 0000000..1a28686
--- /dev/null
+++ b/tests/test_cases/deps/private/foo.hpp
@@ -0,0 +1,6 @@
+#ifndef FOO_HPP
+#define FOO_HPP
+
+int foo();
+
+#endif
diff --git a/tests/test_cases/deps/private/main_includes_foo.cpp b/tests/test_cases/deps/private/main_includes_foo.cpp
new file mode 100644
index 0000000..16701c7
--- /dev/null
+++ b/tests/test_cases/deps/private/main_includes_foo.cpp
@@ -0,0 +1,9 @@
+// test that foo.hpp not available
+
+#include "foo/foo.hpp"
+#include <iostream>
+
+int main() {
+ std::cout << "main\n";
+ return 0;
+}
diff --git a/tests/test_cases/deps/private/main_links_bar_foo.cpp b/tests/test_cases/deps/private/main_links_bar_foo.cpp
new file mode 100644
index 0000000..e936074
--- /dev/null
+++ b/tests/test_cases/deps/private/main_links_bar_foo.cpp
@@ -0,0 +1,9 @@
+// test that foo is linked after bar
+
+#include "bar/bar.hpp"
+#include <iostream>
+
+int main() {
+ std::cout << "main\n";
+ return bar();
+}
diff --git a/tests/test_cases/deps/private/main_links_foo.cpp b/tests/test_cases/deps/private/main_links_foo.cpp
new file mode 100644
index 0000000..1e1e07a
--- /dev/null
+++ b/tests/test_cases/deps/private/main_links_foo.cpp
@@ -0,0 +1,10 @@
+// test that foo is linked
+
+#include <iostream>
+
+int foo(); // forward declare
+
+int main() {
+ std::cout << "main\n";
+ return foo();
+}
diff --git a/tests/test_cases/deps/public/TARGETS b/tests/test_cases/deps/public/TARGETS
new file mode 100644
index 0000000..bc1807a
--- /dev/null
+++ b/tests/test_cases/deps/public/TARGETS
@@ -0,0 +1,36 @@
+{ "foo":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["foo"]
+ , "hdrs": ["foo.hpp"]
+ , "srcs": ["foo.cpp"]
+ , "stage": ["foo"]
+ }
+, "bar":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["bar"]
+ , "hdrs": ["bar.hpp"]
+ , "srcs": ["bar.cpp"]
+ , "deps": ["foo"]
+ , "stage": ["bar"]
+ }
+, "main_includes_foo":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main"]
+ , "srcs": ["main_includes_foo.cpp"]
+ , "private-deps": ["bar"]
+ }
+, "main_links_foo":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main"]
+ , "srcs": ["main_links_foo.cpp"]
+ , "private-deps": ["bar"]
+ }
+, "main_links_bar_foo":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main"]
+ , "srcs": ["main_links_bar_foo.cpp"]
+ , "private-deps": ["bar"]
+ }
+, "install_bar":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["bar"]}
+}
diff --git a/tests/test_cases/deps/public/bar.cpp b/tests/test_cases/deps/public/bar.cpp
new file mode 100644
index 0000000..f2a779a
--- /dev/null
+++ b/tests/test_cases/deps/public/bar.cpp
@@ -0,0 +1,7 @@
+#include "bar.hpp"
+#include <iostream>
+
+int bar(Foo *foo) {
+ std::cout << "bar\n";
+ return (foo == nullptr) ? -1 : foo->foo();
+}
diff --git a/tests/test_cases/deps/public/bar.hpp b/tests/test_cases/deps/public/bar.hpp
new file mode 100644
index 0000000..ec9c165
--- /dev/null
+++ b/tests/test_cases/deps/public/bar.hpp
@@ -0,0 +1,8 @@
+#ifndef BAR_HPP
+#define BAR_HPP
+
+#include "foo/foo.hpp"
+
+int bar(Foo *foo);
+
+#endif
diff --git a/tests/test_cases/deps/public/foo.cpp b/tests/test_cases/deps/public/foo.cpp
new file mode 100644
index 0000000..6899c7a
--- /dev/null
+++ b/tests/test_cases/deps/public/foo.cpp
@@ -0,0 +1,7 @@
+#include "foo.hpp"
+#include <iostream>
+
+int Foo::foo() {
+ std::cout << "foo\n";
+ return 0;
+}
diff --git a/tests/test_cases/deps/public/foo.hpp b/tests/test_cases/deps/public/foo.hpp
new file mode 100644
index 0000000..025f3ef
--- /dev/null
+++ b/tests/test_cases/deps/public/foo.hpp
@@ -0,0 +1,8 @@
+#ifndef FOO_HPP
+#define FOO_HPP
+
+struct Foo {
+ int foo();
+};
+
+#endif
diff --git a/tests/test_cases/deps/public/main_includes_foo.cpp b/tests/test_cases/deps/public/main_includes_foo.cpp
new file mode 100644
index 0000000..e8a85b6
--- /dev/null
+++ b/tests/test_cases/deps/public/main_includes_foo.cpp
@@ -0,0 +1,9 @@
+// test that foo.hpp is available (despite unused here)
+
+#include "foo/foo.hpp"
+#include <iostream>
+
+int main() {
+ std::cout << "main\n";
+ return 0;
+}
diff --git a/tests/test_cases/deps/public/main_links_bar_foo.cpp b/tests/test_cases/deps/public/main_links_bar_foo.cpp
new file mode 100644
index 0000000..02c2966
--- /dev/null
+++ b/tests/test_cases/deps/public/main_links_bar_foo.cpp
@@ -0,0 +1,10 @@
+// test that foo is linked after bar
+
+#include "bar/bar.hpp"
+#include <iostream>
+
+int main() {
+ std::cout << "main\n";
+ Foo foo{};
+ return bar(&foo);
+}
diff --git a/tests/test_cases/deps/public/main_links_foo.cpp b/tests/test_cases/deps/public/main_links_foo.cpp
new file mode 100644
index 0000000..f9cc308
--- /dev/null
+++ b/tests/test_cases/deps/public/main_links_foo.cpp
@@ -0,0 +1,10 @@
+// test that foo is linked
+
+#include "foo/foo.hpp"
+#include <iostream>
+
+int main() {
+ std::cout << "main\n";
+ Foo{}.foo();
+ return 0;
+}
diff --git a/tests/test_cases/deps/shared/TARGETS b/tests/test_cases/deps/shared/TARGETS
new file mode 100644
index 0000000..9db7fb8
--- /dev/null
+++ b/tests/test_cases/deps/shared/TARGETS
@@ -0,0 +1,104 @@
+{ "foo":
+ { "type": ["@", "rules", "CC", "library"]
+ , "shared": ["yes"]
+ , "name": ["foo"]
+ , "soversion": ["1", "2", "3"]
+ , "hdrs": ["foo.hpp"]
+ , "srcs": ["foo.cpp"]
+ , "stage": ["foo"]
+ }
+, "foo_s":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["foo"]
+ , "hdrs": ["foo.hpp"]
+ , "srcs": ["foo.cpp"]
+ , "stage": ["foo"]
+ }
+, "bar":
+ { "type": ["@", "rules", "CC", "library"]
+ , "shared": ["yes"]
+ , "name": ["bar"]
+ , "hdrs": ["bar.hpp"]
+ , "srcs": ["bar.cpp"]
+ , "stage": ["bar"]
+ , "private-deps": ["foo"]
+ }
+, "bar_s":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["bar"]
+ , "hdrs": ["bar.hpp"]
+ , "srcs": ["bar.cpp"]
+ , "stage": ["bar"]
+ , "private-deps": ["foo"]
+ }
+, "baz":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["baz"]
+ , "shared": ["yes"]
+ , "hdrs": ["baz.hpp"]
+ , "srcs": ["baz.cpp"]
+ , "stage": ["baz"]
+ , "private-deps": ["foo_s"]
+ }
+, "main_uses_foo":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main_uses_foo"]
+ , "srcs": ["main_uses_foo.cpp"]
+ , "private-deps": ["foo"]
+ }
+, "test_uses_foo":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_foo"]
+ , "srcs": ["test_uses_foo.cpp"]
+ , "private-deps": ["foo"]
+ }
+, "main_uses_bar":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main_uses_bar"]
+ , "srcs": ["main_uses_bar.cpp"]
+ , "private-deps": ["bar"]
+ }
+, "test_uses_bar":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_bar"]
+ , "srcs": ["test_uses_bar.cpp"]
+ , "private-deps": ["bar"]
+ }
+, "test_uses_bar_s":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_bar_s"]
+ , "srcs": ["test_uses_bar.cpp"]
+ , "private-deps": ["bar_s"]
+ }
+, "test_uses_baz":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_baz"]
+ , "srcs": ["test_uses_baz.cpp"]
+ , "private-deps": ["baz"]
+ }
+, "test_uses_main":
+ { "type": ["@", "rules", "shell/test", "script"]
+ , "name": ["test_uses_main"]
+ , "test": ["test_uses_main.sh"]
+ , "deps": ["main_uses_bar"]
+ }
+, "test_diamond":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_diamond"]
+ , "srcs": ["test_diamond.cpp"]
+ , "private-deps": ["foo_s", "baz"]
+ }
+, "install_foo":
+ { "type": ["@", "rules", "CC", "install-with-deps"]
+ , "targets": ["foo"]
+ , "flat-libs": ["yes"]
+ }
+, "install_bar_s":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["bar_s"]}
+, "install_baz":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["baz"]}
+, "install_main":
+ { "type": ["@", "rules", "CC", "install-with-deps"]
+ , "targets": ["main_uses_bar"]
+ }
+}
diff --git a/tests/test_cases/deps/shared/bar.cpp b/tests/test_cases/deps/shared/bar.cpp
new file mode 100644
index 0000000..dfce0e8
--- /dev/null
+++ b/tests/test_cases/deps/shared/bar.cpp
@@ -0,0 +1,8 @@
+#include "foo/foo.hpp"
+#include "bar.hpp"
+#include <iostream>
+
+int bar() {
+ std::cout << "bar\n";
+ return foo();
+}
diff --git a/tests/test_cases/deps/shared/bar.hpp b/tests/test_cases/deps/shared/bar.hpp
new file mode 100644
index 0000000..94d4f16
--- /dev/null
+++ b/tests/test_cases/deps/shared/bar.hpp
@@ -0,0 +1,6 @@
+#ifndef BAR_HPP
+#define BAR_HPP
+
+int bar();
+
+#endif
diff --git a/tests/test_cases/deps/shared/baz.cpp b/tests/test_cases/deps/shared/baz.cpp
new file mode 100644
index 0000000..fae4845
--- /dev/null
+++ b/tests/test_cases/deps/shared/baz.cpp
@@ -0,0 +1,8 @@
+#include "foo/foo.hpp"
+#include "baz.hpp"
+#include <iostream>
+
+int baz() {
+ std::cout << "baz\n";
+ return foo();
+}
diff --git a/tests/test_cases/deps/shared/baz.hpp b/tests/test_cases/deps/shared/baz.hpp
new file mode 100644
index 0000000..643cf65
--- /dev/null
+++ b/tests/test_cases/deps/shared/baz.hpp
@@ -0,0 +1,6 @@
+#ifndef BAZ_HPP
+#define BAZ_HPP
+
+int baz();
+
+#endif
diff --git a/tests/test_cases/deps/shared/foo.cpp b/tests/test_cases/deps/shared/foo.cpp
new file mode 100644
index 0000000..f985022
--- /dev/null
+++ b/tests/test_cases/deps/shared/foo.cpp
@@ -0,0 +1,7 @@
+#include "foo.hpp"
+#include <iostream>
+
+int foo() {
+ std::cout << "foo\n";
+ return 0;
+}
diff --git a/tests/test_cases/deps/shared/foo.hpp b/tests/test_cases/deps/shared/foo.hpp
new file mode 100644
index 0000000..1a28686
--- /dev/null
+++ b/tests/test_cases/deps/shared/foo.hpp
@@ -0,0 +1,6 @@
+#ifndef FOO_HPP
+#define FOO_HPP
+
+int foo();
+
+#endif
diff --git a/tests/test_cases/deps/shared/main_uses_bar.cpp b/tests/test_cases/deps/shared/main_uses_bar.cpp
new file mode 100644
index 0000000..9cfc855
--- /dev/null
+++ b/tests/test_cases/deps/shared/main_uses_bar.cpp
@@ -0,0 +1,8 @@
+#include "bar/bar.hpp"
+#include <iostream>
+
+int main() {
+ std::cout << "main\n";
+ bar();
+ return 0;
+}
diff --git a/tests/test_cases/deps/shared/main_uses_foo.cpp b/tests/test_cases/deps/shared/main_uses_foo.cpp
new file mode 100644
index 0000000..0fb3bb6
--- /dev/null
+++ b/tests/test_cases/deps/shared/main_uses_foo.cpp
@@ -0,0 +1,8 @@
+#include "foo/foo.hpp"
+#include <iostream>
+
+int main() {
+ std::cout << "main\n";
+ foo();
+ return 0;
+}
diff --git a/tests/test_cases/deps/shared/test_diamond.cpp b/tests/test_cases/deps/shared/test_diamond.cpp
new file mode 100644
index 0000000..9298230
--- /dev/null
+++ b/tests/test_cases/deps/shared/test_diamond.cpp
@@ -0,0 +1,4 @@
+#include "baz/baz.hpp"
+#include "foo/foo.hpp"
+
+int main() { return foo() + baz(); }
diff --git a/tests/test_cases/deps/shared/test_uses_bar.cpp b/tests/test_cases/deps/shared/test_uses_bar.cpp
new file mode 100644
index 0000000..c2076ec
--- /dev/null
+++ b/tests/test_cases/deps/shared/test_uses_bar.cpp
@@ -0,0 +1,3 @@
+#include "bar/bar.hpp"
+
+int main() { return bar(); }
diff --git a/tests/test_cases/deps/shared/test_uses_baz.cpp b/tests/test_cases/deps/shared/test_uses_baz.cpp
new file mode 100644
index 0000000..02d67f9
--- /dev/null
+++ b/tests/test_cases/deps/shared/test_uses_baz.cpp
@@ -0,0 +1,3 @@
+#include "baz/baz.hpp"
+
+int main() { return baz(); }
diff --git a/tests/test_cases/deps/shared/test_uses_foo.cpp b/tests/test_cases/deps/shared/test_uses_foo.cpp
new file mode 100644
index 0000000..88feb93
--- /dev/null
+++ b/tests/test_cases/deps/shared/test_uses_foo.cpp
@@ -0,0 +1,3 @@
+#include "foo/foo.hpp"
+
+int main() { return foo(); }
diff --git a/tests/test_cases/deps/shared/test_uses_main.sh b/tests/test_cases/deps/shared/test_uses_main.sh
new file mode 100644
index 0000000..ab8672f
--- /dev/null
+++ b/tests/test_cases/deps/shared/test_uses_main.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+set -e
+
+./main_uses_bar | grep main
+./main_uses_bar | grep bar
+./main_uses_bar | grep foo
diff --git a/tests/test_rules/EXPRESSIONS b/tests/test_rules/EXPRESSIONS
new file mode 100644
index 0000000..f1ca6f4
--- /dev/null
+++ b/tests/test_rules/EXPRESSIONS
@@ -0,0 +1,46 @@
+{ "stage_singleton_field":
+ { "vars": ["fieldname", "transition", "location"]
+ , "expression":
+ { "type": "assert_non_empty"
+ , "msg":
+ ["No artifact specified in field", {"type": "var", "name": "fieldname"}]
+ , "$1":
+ { "type": "disjoint_map_union"
+ , "msg":
+ [ "Expecting (essentially) a single artifact in field"
+ , {"type": "var", "name": "fieldname"}
+ ]
+ , "$1":
+ { "type": "foreach"
+ , "var": "src"
+ , "range":
+ {"type": "FIELD", "name": {"type": "var", "name": "fieldname"}}
+ , "body":
+ { "type": "disjoint_map_union"
+ , "$1":
+ { "type": "foreach"
+ , "var": "artifact"
+ , "range":
+ { "type": "values"
+ , "$1":
+ { "type": "DEP_ARTIFACTS"
+ , "dep": {"type": "var", "name": "src"}
+ , "transition":
+ { "type": "var"
+ , "name": "transition"
+ , "default": {"type": "empty_map"}
+ }
+ }
+ }
+ , "body":
+ { "type": "singleton_map"
+ , "key": {"type": "var", "name": "location"}
+ , "value": {"type": "var", "name": "artifact"}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/test_rules/README.md b/tests/test_rules/README.md
new file mode 100644
index 0000000..5a1baed
--- /dev/null
+++ b/tests/test_rules/README.md
@@ -0,0 +1,28 @@
+# Test Rules
+
+This is a test rule that supports building and installing multiple targets with
+a given set of rules. For each target, it can be specified whether building it
+should fail or succeed. After processing all targets, additional assertions
+(list of shell commands) can be run.
+
+## Setup
+
+The test rules expect to find the following three bindings:
+ - `[["@", "test-rules", "", "tree"]]`, which contains a single tree artifact
+ with the rules to test.
+ - `[["@", "test-just", "", ""]]`, which contains a single executable artifact
+ that is the JustBuild binary to use for the tests.
+ - `[["@", "test-libs", "", "tree"]]`, which contains a single tree artifact
+ that holds the file trees of external libraries.
+
+## Rule `["test_rules", "test_case"]`
+
+Define a test case for rule tests.
+
+| Field | Description |
+| ----- | ----------- |
+| `"name"` | Name of the test (multiple entries are joined). |
+| `"libs"` | Paths to external libraries' trees provided by the `"test-libs"` repository. From within a test cases, the library's tree can be accessed via `["@", "<libname>", "", "tree"]`. |
+| `"targets"` | Target names to build and install. Each target name is prefixed by `"+"` or `"-"`, indicating if the build should fail or not. Targets that build successfully will be installed to a directory named identical to the target name (without the prefix). |
+| `"asserts"` | List of commands to execute after all targets were processed. To access artifacts from installed targets, use the corresponding target name as prefix dir (e.g., target `"+foo"` installs to `"./foo/"`). |
+| `"data"` | The directory that contains the project with the targets to test. |
diff --git a/tests/test_rules/RULES b/tests/test_rules/RULES
new file mode 100644
index 0000000..f53efba
--- /dev/null
+++ b/tests/test_rules/RULES
@@ -0,0 +1,204 @@
+{ "test_case":
+ { "doc": ["Define a test case for rule tests."]
+ , "string_fields": ["name", "libs", "targets", "asserts"]
+ , "target_fields": ["data"]
+ , "field_doc":
+ { "name": ["Name of the test (multiple entries are joined)."]
+ , "libs":
+ [ "Paths to external libraries' trees provided by the \"test-libs\""
+ , "repository. From within a test cases, the library's tree can be"
+ , "accessed via [\"@\", \"<libpath>\", \"\", \"tree\"]."
+ ]
+ , "targets":
+ [ "Target names to build and install. Each target name is prefixed by"
+ , "\"+\" or \"-\", indicating if the build should fail or not."
+ , "Targets that build successfully will be installed to a directory"
+ , "named identical to the target name (without the prefix)."
+ ]
+ , "asserts":
+ [ "List of commands to execute after all targets were processed. To"
+ , "access artifacts from installed targets, use the corresponding target"
+ , "name as prefix dir (e.g., target \"+foo\" installs to \"./foo/\")."
+ ]
+ , "data":
+ ["The directory that contains the project with the targets to test."]
+ }
+ , "tainted": ["test"]
+ , "implicit":
+ { "runner": ["test_runner.py"]
+ , "rules": [["@", "test-rules", "", "tree"]]
+ , "just": [["@", "test-just", "", ""]]
+ , "libs_tree": [["@", "test-libs", "", "tree"]]
+ }
+ , "imports": {"stage_artifact": "stage_singleton_field"}
+ , "expression":
+ { "type": "let*"
+ , "bindings":
+ [ ["name", {"type": "join", "$1": {"type": "FIELD", "name": "name"}}]
+ , ["fieldname", "just"]
+ , ["location", "bin/just"]
+ , ["just", {"type": "CALL_EXPRESSION", "name": "stage_artifact"}]
+ , ["fieldname", "rules"]
+ , ["location", "rules"]
+ , ["rules", {"type": "CALL_EXPRESSION", "name": "stage_artifact"}]
+ , [ "imports"
+ , { "type": "singleton_map"
+ , "key": "imports/TARGETS"
+ , "value":
+ { "type": "BLOB"
+ , "data": "{\"tree\":{\"type\":\"install\",\"dirs\":[[[\"TREE\",null,\".\"],\".\"]]}}"
+ }
+ }
+ ]
+ , ["fieldname", "libs_tree"]
+ , ["location", "libs"]
+ , ["libs", {"type": "CALL_EXPRESSION", "name": "stage_artifact"}]
+ , ["fieldname", "data"]
+ , ["location", "work"]
+ , ["work", {"type": "CALL_EXPRESSION", "name": "stage_artifact"}]
+ , ["fieldname", "runner"]
+ , ["location", "runner"]
+ , ["runner", {"type": "CALL_EXPRESSION", "name": "stage_artifact"}]
+ , ["targets", {"type": "FIELD", "name": "targets"}]
+ , ["asserts", {"type": "FIELD", "name": "asserts"}]
+ , [ "work_bindings"
+ , { "type": "map_union"
+ , "$1":
+ { "type": "++"
+ , "$1":
+ [ [{"type": "singleton_map", "key": "rules", "value": "rules"}]
+ , { "type": "foreach"
+ , "range": {"type": "FIELD", "name": "libs"}
+ , "var": "libpath"
+ , "body":
+ { "type": "singleton_map"
+ , "key": {"type": "var", "name": "libpath"}
+ , "value": {"type": "var", "name": "libpath"}
+ }
+ }
+ ]
+ }
+ }
+ ]
+ , [ "work_deps"
+ , { "type": "map_union"
+ , "$1":
+ { "type": "++"
+ , "$1":
+ [ [ { "type": "let*"
+ , "bindings":
+ [ ["workspace_root", ["file", "rules"]]
+ , ["rules", {"type": "env", "vars": ["workspace_root"]}]
+ ]
+ , "body": {"type": "env", "vars": ["rules"]}
+ }
+ ]
+ , { "type": "foreach"
+ , "range": {"type": "FIELD", "name": "libs"}
+ , "var": "libpath"
+ , "body":
+ { "type": "let*"
+ , "bindings":
+ [ [ "workspace_root"
+ , [ "file"
+ , { "type": "join"
+ , "$1": ["libs/", {"type": "var", "name": "libpath"}]
+ }
+ ]
+ ]
+ , ["target_root", ["file", "imports"]]
+ ]
+ , "body":
+ { "type": "singleton_map"
+ , "key": {"type": "var", "name": "libpath"}
+ , "value":
+ { "type": "env"
+ , "vars": ["workspace_root", "target_root"]
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+ , [ "repos"
+ , { "type": "singleton_map"
+ , "key": "repos.json"
+ , "value":
+ { "type": "BLOB"
+ , "data":
+ { "type": "json_encode"
+ , "$1":
+ { "type": "let*"
+ , "bindings":
+ [ ["workspace_root", ["file", "work"]]
+ , ["bindings", {"type": "var", "name": "work_bindings"}]
+ , [ "work"
+ , {"type": "env", "vars": ["workspace_root", "bindings"]}
+ ]
+ , [ "repositories"
+ , { "type": "map_union"
+ , "$1":
+ [ {"type": "env", "vars": ["work"]}
+ , {"type": "var", "name": "work_deps"}
+ ]
+ }
+ ]
+ , ["main", "work"]
+ ]
+ , "body": {"type": "env", "vars": ["main", "repositories"]}
+ }
+ }
+ }
+ }
+ ]
+ , [ "config"
+ , { "type": "singleton_map"
+ , "key": "config.json"
+ , "value":
+ { "type": "BLOB"
+ , "data":
+ { "type": "json_encode"
+ , "$1": {"type": "env", "vars": ["targets", "asserts"]}
+ }
+ }
+ }
+ ]
+ , [ "results"
+ , { "type": "ACTION"
+ , "inputs":
+ { "type": "map_union"
+ , "$1":
+ [ {"type": "var", "name": "runner"}
+ , {"type": "var", "name": "rules"}
+ , {"type": "var", "name": "just"}
+ , {"type": "var", "name": "imports"}
+ , {"type": "var", "name": "libs"}
+ , {"type": "var", "name": "repos"}
+ , {"type": "var", "name": "work"}
+ , {"type": "var", "name": "config"}
+ ]
+ }
+ , "outs": ["stdout", "stderr", "result", "time-start", "time-stop"]
+ , "cmd": ["./runner"]
+ , "may_fail": ["test"]
+ , "fail_message":
+ { "type": "join"
+ , "$1": ["Rule test ", {"type": "var", "name": "name"}, " failed"]
+ }
+ }
+ ]
+ ]
+ , "body":
+ { "type": "RESULT"
+ , "artifacts": {"type": "var", "name": "results"}
+ , "runfiles":
+ { "type": "singleton_map"
+ , "key": {"type": "var", "name": "name"}
+ , "value": {"type": "TREE", "$1": {"type": "var", "name": "results"}}
+ }
+ }
+ }
+ }
+}
diff --git a/tests/test_rules/TARGETS b/tests/test_rules/TARGETS
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/tests/test_rules/TARGETS
@@ -0,0 +1 @@
+{}
diff --git a/tests/test_rules/test_runner.py b/tests/test_rules/test_runner.py
new file mode 100755
index 0000000..6fd2d8c
--- /dev/null
+++ b/tests/test_rules/test_runner.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+# Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import os
+import subprocess
+import time
+
+time_start: float = time.time()
+time_stop: float = 0
+result: str = "UNKNOWN"
+stderr: str = ""
+stdout: str = ""
+
+
+def dump_results() -> None:
+ with open("result", "w") as f:
+ f.write("%s\n" % (result, ))
+ with open("time-start", "w") as f:
+ f.write("%d\n" % (time_start, ))
+ with open("time-stop", "w") as f:
+ f.write("%d\n" % (time_stop, ))
+ with open("stdout", "w") as f:
+ f.write("%s\n" % (stdout, ))
+ with open("stderr", "w") as f:
+ f.write("%s\n" % (stderr, ))
+
+
+dump_results()
+
+with open('config.json') as f:
+ config = json.load(f)
+
+os.makedirs("./outs")
+
+# install targets
+failed_targets = 0
+stdout += "Test targets:\n"
+for t in config.get('targets', []):
+ target = t[1:]
+ should_fail = t[0] == "-"
+ ret = subprocess.run([
+ "./bin/just", "install", "--local-build-root", "./build_root", "-C",
+ "repos.json", "-o", "/".join(["./outs", target]), target
+ ],
+ capture_output=True)
+ success = ret.returncode != 0 if should_fail else ret.returncode == 0
+ failed_targets += 0 if success else 1
+ stdout += f" [{'PASS' if success else 'FAIL'}] {target}\n"
+ stderr += "".join([
+ f"stdout/stderr of test target '{target}':\n",
+ ret.stdout.decode("utf-8"),
+ ret.stderr.decode("utf-8"), "\n"
+ ])
+stdout += f" {failed_targets} targets failed\n"
+
+# run asserts
+failed_asserts = 0
+stdout += "Test asserts:\n"
+for cmd in config.get('asserts', []):
+ ret = subprocess.run(cmd, cwd="./outs", shell=True, capture_output=True)
+ success = ret.returncode == 0
+ failed_asserts += 0 if success else 1
+ stdout += f" [{'PASS' if success else 'FAIL'}] {cmd}\n"
+ stderr += "".join([
+ f"stdout/stderr of test assert '{cmd}':\n",
+ ret.stdout.decode("utf-8"),
+ ret.stderr.decode("utf-8"), "\n"
+ ])
+stdout += f" {failed_asserts} asserts failed\n"
+
+retval = min(failed_targets + failed_asserts, 125)
+result = "PASS" if retval == 0 else "FAIL"
+
+time_stop = time.time()
+dump_results()
+exit(retval)