summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/ctor.cc78
-rw-r--r--test/cycle_test.cc87
-rw-r--r--test/deps_test.cc97
-rw-r--r--test/deps_test_data/empty.d0
-rw-r--r--test/deps_test_data/missing_colon.d1
-rw-r--r--test/deps_test_data/multiline.d4
-rw-r--r--test/deps_test_data/no_deps.d1
-rw-r--r--test/deps_test_data/no_newline.d1
-rw-r--r--test/deps_test_data/spaces.d1
-rw-r--r--test/deps_test_data/trivial.d1
-rw-r--r--test/execute_test.cc83
-rw-r--r--test/pointerlist_test.cc320
-rw-r--r--test/tasks_test.cc173
-rw-r--r--test/testprog.cc53
-rw-r--r--test/tmpfile.h48
-rw-r--r--test/tools_test.cc69
m---------test/uunit0
17 files changed, 947 insertions, 70 deletions
diff --git a/test/ctor.cc b/test/ctor.cc
index a2ad64d..31c63ab 100644
--- a/test/ctor.cc
+++ b/test/ctor.cc
@@ -11,12 +11,65 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
{
{
.type = ctor::target_type::unit_test,
+ .system = ctor::output_system::build,
+ .target = "pointerlist_test",
+ .sources = {
+ "pointerlist_test.cc",
+ "testmain.cc",
+ "../src/pointerlist.cc",
+ },
+ .flags = {
+ .cxxflags = {
+ "-std=c++20", "-O3", "-Wall", "-Werror",
+ "-I../src", "-Iuunit",
+ "-DOUTPUT=\"pointerlist\"",
+ },
+ },
+ },
+ {
+ .type = ctor::target_type::unit_test,
+ .system = ctor::output_system::build,
+ .target = "deps_test",
+ .sources = {
+ "deps_test.cc",
+ "testmain.cc",
+ "../src/deps.cc",
+ "../src/util.cc",
+ },
+ .depends = { "testprog", },
+ .flags = {
+ .cxxflags = {
+ "-std=c++20", "-O3", "-Wall", "-Werror",
+ "-I../src", "-Iuunit",
+ "-DOUTPUT=\"deps\"",
+ },
+ },
+ },
+ {
+ .type = ctor::target_type::unit_test,
+ .system = ctor::output_system::build,
+ .target = "testprog",
+ .sources = {
+ "testprog.cc",
+ },
+ .flags = {
+ .cxxflags = {
+ "-std=c++20", "-O3", "-Wall", "-Werror",
+ },
+ },
+ },
+ {
+ .type = ctor::target_type::unit_test,
+ .system = ctor::output_system::build,
.target = "execute_test",
.sources = {
"execute_test.cc",
"testmain.cc",
"../src/execute.cc",
+ "../src/pointerlist.cc",
+ "../src/util.cc",
},
+ .depends = { "testprog", },
.flags = {
.cxxflags = {
"-std=c++20", "-O3", "-Wall", "-Werror",
@@ -28,6 +81,7 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
},
{
.type = ctor::target_type::unit_test,
+ .system = ctor::output_system::build,
.target = "tasks_test",
.sources = {
"tasks_test.cc",
@@ -45,6 +99,25 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
},
{
.type = ctor::target_type::unit_test,
+ .system = ctor::output_system::build,
+ .target = "cycle_test",
+ .sources = {
+ "cycle_test.cc",
+ "testmain.cc",
+ },
+ .depends = { "libctor_nomain.a" },
+ .flags = {
+ .cxxflags = {
+ "-std=c++20", "-O3", "-Wall", "-Werror",
+ "-I../src", "-Iuunit",
+ "-DOUTPUT=\"cycle\"",
+ },
+ .ldflags = { "-pthread" },
+ },
+ },
+ {
+ .type = ctor::target_type::unit_test,
+ .system = ctor::output_system::build,
.target = "source_type_test",
.sources = {
"source_type_test.cc",
@@ -62,10 +135,12 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
},
{
.type = ctor::target_type::unit_test,
+ .system = ctor::output_system::build,
.target = "tools_test",
.sources = {
"tools_test.cc",
"testmain.cc",
+ "../src/util.cc",
"../src/tools.cc",
},
//.depends = { "libctor_nomain.a" },
@@ -79,11 +154,14 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
},
{
.type = ctor::target_type::unit_test_library,
+ .system = ctor::output_system::build,
.target = "libctor_nomain.a",
.sources = {
"../src/build.cc",
"../src/configure.cc",
+ "../src/deps.cc",
"../src/execute.cc",
+ "../src/pointerlist.cc",
"../src/rebuild.cc",
"../src/tasks.cc",
"../src/task.cc",
diff --git a/test/cycle_test.cc b/test/cycle_test.cc
new file mode 100644
index 0000000..3b45632
--- /dev/null
+++ b/test/cycle_test.cc
@@ -0,0 +1,87 @@
+#include <uunit.h>
+
+#include <ctor.h>
+#include <build.h>
+
+namespace
+{
+ctor::build_configurations ctorTestConfigsCyclic(const ctor::settings&)
+{
+ return
+ {
+ // No dependency
+ {
+ .target = "target0",
+ },
+
+ // Direct (self-depends)
+ {
+ .target = "target1",
+ .depends = { "target1" },
+ },
+
+ // Indirect cyclic depends
+ {
+ .target = "target2",
+ .depends = { "target3" },
+ },
+ {
+ .target = "target3",
+ .depends = { "target2" },
+ },
+ };
+}
+}
+
+REG(ctorTestConfigsCyclic);
+
+class CycleTest
+ : public uUnit
+{
+public:
+ CycleTest()
+ {
+ uTEST(CycleTest::getTargets_test);
+ }
+
+ void getTargets_test()
+ {
+ using namespace std::string_literals;
+ ctor::settings settings{};
+ const auto& tasks = getTasks(settings);
+ uASSERT_EQUAL(4u, tasks.size());
+
+ uASSERT_EQUAL("target0"s, tasks[0]->target());
+ uASSERT_EQUAL("target1"s, tasks[1]->target());
+ uASSERT_EQUAL("target2"s, tasks[2]->target());
+ uASSERT_EQUAL("target3"s, tasks[3]->target());
+
+ for(auto task : tasks)
+ {
+ if(task->registerDepTasks(tasks))
+ {
+ uASSERT(false);
+ }
+ }
+
+ bool exc;
+ exc = false;
+ try { getDepTasks(tasks[0]); } catch(...) { exc = true; }
+ uASSERT(!exc);
+
+ exc = false;
+ try { getDepTasks(tasks[1]); } catch(...) { exc = true; }
+ uASSERT(exc);
+
+ exc = false;
+ try { getDepTasks(tasks[2]); } catch(...) { exc = true; }
+ uASSERT(exc);
+
+ exc = false;
+ try { getDepTasks(tasks[3]); } catch(...) { exc = true; }
+ uASSERT(exc);
+ }
+};
+
+// Registers the fixture into the 'registry'
+static CycleTest test;
diff --git a/test/deps_test.cc b/test/deps_test.cc
new file mode 100644
index 0000000..762b0e5
--- /dev/null
+++ b/test/deps_test.cc
@@ -0,0 +1,97 @@
+#include <uunit.h>
+
+#include <fstream>
+#include <map>
+#include <vector>
+
+#include <deps.h>
+#include <algorithm>
+
+#include "paths.h"
+#include "tmpfile.h"
+
+class DepsTest
+ : public uUnit
+{
+public:
+ DepsTest()
+ {
+ uTEST(DepsTest::parser_test);
+ }
+
+ void parser_test()
+ {
+ using namespace std::string_literals;
+
+ auto test_data = paths::top_srcdir / "test" / "deps_test_data";
+
+ auto empty = test_data / "empty.d";
+ auto trivial = test_data / "trivial.d";
+ auto no_newline = test_data / "no_newline.d";
+ auto no_deps = test_data / "no_deps.d";
+ auto trailing_whitespace = test_data / "trailing_whitespace.d";
+ auto spaces = test_data / "spaces.d";
+ auto multiline = test_data / "multiline.d";
+ auto no_such_file = test_data / "no_such_file.d"; // doesn't exist
+ auto missing_colon = test_data / "missing_colon.d";
+
+ {
+ auto res = readDeps(empty.string(), ctor::toolchain::gcc);
+ uASSERT(res.empty());
+ }
+
+ {
+ auto res = readDeps(trivial.string(), ctor::toolchain::gcc);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("x.cc"s, res[0]);
+ }
+
+ {
+ auto res = readDeps(no_newline.string(), ctor::toolchain::gcc);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("x.cc"s, res[0]);
+ }
+
+ {
+ auto res = readDeps(no_deps.string(), ctor::toolchain::gcc);
+ uASSERT_EQUAL(0u, res.size());
+ }
+
+ {
+ auto res = readDeps(spaces.string(), ctor::toolchain::gcc);
+ uASSERT_EQUAL(2u, res.size());
+ uASSERT_EQUAL("x y.cc"s, res[0]);
+ uASSERT_EQUAL("x y.h"s, res[1]);
+ }
+
+ {
+ auto res = readDeps(multiline.string(), ctor::toolchain::gcc);
+ uASSERT_EQUAL(12u, res.size());
+ uASSERT_EQUAL("src/configure.cc"s, res[0]);
+ uASSERT_EQUAL("src/configure.h"s, res[1]);
+ uASSERT_EQUAL("src/getoptpp/getoptpp.hpp"s, res[2]);
+ uASSERT_EQUAL("src/execute.h"s, res[3]);
+ uASSERT_EQUAL("src/ctor.h"s, res[4]);
+ uASSERT_EQUAL("src/tasks.h"s, res[5]);
+ uASSERT_EQUAL("src/task.h"s, res[6]);
+ uASSERT_EQUAL("src/rebuild.h"s, res[7]);
+ uASSERT_EQUAL("src/externals.h"s, res[8]);
+ uASSERT_EQUAL("src/externals_manual.h"s, res[9]);
+ uASSERT_EQUAL("src/tools.h"s, res[10]);
+ uASSERT_EQUAL("src/util.h"s, res[11]);
+ }
+
+ {
+ auto res = readDeps(no_such_file.string(), ctor::toolchain::gcc);
+ uASSERT(res.empty());
+ }
+
+ {
+ auto res = readDeps(missing_colon.string(), ctor::toolchain::gcc);
+ uASSERT(res.empty());
+ }
+ }
+};
+
+// Registers the fixture into the 'registry'
+static DepsTest test;
diff --git a/test/deps_test_data/empty.d b/test/deps_test_data/empty.d
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/deps_test_data/empty.d
diff --git a/test/deps_test_data/missing_colon.d b/test/deps_test_data/missing_colon.d
new file mode 100644
index 0000000..e46996c
--- /dev/null
+++ b/test/deps_test_data/missing_colon.d
@@ -0,0 +1 @@
+x.cc x.h
diff --git a/test/deps_test_data/multiline.d b/test/deps_test_data/multiline.d
new file mode 100644
index 0000000..8862ab0
--- /dev/null
+++ b/test/deps_test_data/multiline.d
@@ -0,0 +1,4 @@
+build/src/libctor_a-configure_cc.o: src/configure.cc src/configure.h \
+ src/getoptpp/getoptpp.hpp src/execute.h src/ctor.h src/tasks.h \
+ src/task.h src/rebuild.h src/externals.h src/externals_manual.h \
+ src/tools.h src/util.h
diff --git a/test/deps_test_data/no_deps.d b/test/deps_test_data/no_deps.d
new file mode 100644
index 0000000..e7cccf6
--- /dev/null
+++ b/test/deps_test_data/no_deps.d
@@ -0,0 +1 @@
+x.o:
diff --git a/test/deps_test_data/no_newline.d b/test/deps_test_data/no_newline.d
new file mode 100644
index 0000000..88829ea
--- /dev/null
+++ b/test/deps_test_data/no_newline.d
@@ -0,0 +1 @@
+x.o: x.cc \ No newline at end of file
diff --git a/test/deps_test_data/spaces.d b/test/deps_test_data/spaces.d
new file mode 100644
index 0000000..c53fd64
--- /dev/null
+++ b/test/deps_test_data/spaces.d
@@ -0,0 +1 @@
+x\ y.o: x\ y.cc x\ y.h
diff --git a/test/deps_test_data/trivial.d b/test/deps_test_data/trivial.d
new file mode 100644
index 0000000..15a0c29
--- /dev/null
+++ b/test/deps_test_data/trivial.d
@@ -0,0 +1 @@
+x.o: x.cc
diff --git a/test/execute_test.cc b/test/execute_test.cc
index 9da18dc..722b6ea 100644
--- a/test/execute_test.cc
+++ b/test/execute_test.cc
@@ -1,6 +1,15 @@
#include <uunit.h>
+#include <fstream>
+#include <map>
+#include <vector>
+
#include <execute.h>
+#include <util.h>
+#include <algorithm>
+
+#include "paths.h"
+#include "tmpfile.h"
class ExecuteTest
: public uUnit
@@ -8,14 +17,78 @@ class ExecuteTest
public:
ExecuteTest()
{
- uTEST(ExecuteTest::runit);
+ uTEST(ExecuteTest::return_value);
+ uTEST(ExecuteTest::env);
+ }
+
+ void return_value()
+ {
+ ctor::settings s;
+ auto cur_path = std::filesystem::path(paths::argv_0).parent_path();
+ std::vector<std::string> paths{{cur_path.string()}};
+ auto cmd = locate("testprog", paths);
+ uASSERT(!cmd.empty());
+
+ auto value = execute(s, cmd, {"retval", "0"}, {}, false);
+ uASSERT_EQUAL(0, value);
+ value = execute(s, cmd, {"retval", "1"}, {}, false);
+ uASSERT_EQUAL(1, value);
+ value = execute(s, "no-such-binary", {}, {}, false);
+ uASSERT_EQUAL(1, value);
+ value = execute(s, cmd, {"segfault"}, {}, false);
+ uASSERT_EQUAL(11, value);
+ value = execute(s, cmd, {"throw"}, {}, false);
+ uASSERT_EQUAL(6, value);
+ value = execute(s, cmd, {"abort"}, {}, false);
+ uASSERT_EQUAL(6, value);
}
- void runit()
+ void env()
{
- uASSERT_EQUAL(0, execute("/bin/true", {}, false));
- uASSERT_EQUAL(1, execute("/bin/false", {}, false));
- uASSERT_EQUAL(1, execute("no-such-binary", {}, false));
+ using namespace std::string_literals;
+
+ ctor::settings s;
+ auto cur_path = std::filesystem::path(paths::argv_0).parent_path();
+ std::vector<std::string> paths{{cur_path.string()}};
+ auto cmd = locate("testprog", paths);
+ uASSERT(!cmd.empty());
+
+ tmp_file tmp;
+
+ std::map<std::string, std::string> env;
+
+ // New env vars
+ env["foo"] = "bar";
+ env["bar"] = "42";
+
+ // Overwrite the exiting LANG var
+ env["LANG"] = "foo";
+
+ auto value = execute(s, cmd, {"envdump", tmp.get()}, env, false);
+ uASSERT_EQUAL(0, value);
+
+ std::vector<std::string> vars;
+ {
+ std::ifstream infile(tmp.get());
+ std::string line;
+ while (std::getline(infile, line))
+ {
+ vars.push_back(line);
+ }
+ }
+
+ // Check the two explicitly set vars
+ auto chk = std::find(vars.begin(), vars.end(), "foo=bar"s);
+ uASSERT(chk != vars.end());
+ chk = std::find(vars.begin(), vars.end(), "bar=42"s);
+ uASSERT(chk != vars.end());
+
+ // Check the one that should have overwritten the existing one (probably LANG=en_US.UTF-8 or something)
+ chk = std::find(vars.begin(), vars.end(), "LANG=foo"s);
+ uASSERT(chk != vars.end());
+
+ // Check that other vars are also there (ie. the env wasn't cleared on entry)
+ uASSERT(vars.size() > 3);
}
};
diff --git a/test/pointerlist_test.cc b/test/pointerlist_test.cc
new file mode 100644
index 0000000..4274473
--- /dev/null
+++ b/test/pointerlist_test.cc
@@ -0,0 +1,320 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include <uunit.h>
+
+#include <set>
+#include <algorithm>
+
+#include "pointerlist.h"
+
+class PointerListTest
+ : public uUnit
+{
+public:
+ PointerListTest()
+ {
+ uTEST(PointerListTest::test_zom_pointerlist_push);
+ uTEST(PointerListTest::test_zom_pointerlist_from_args);
+ uTEST(PointerListTest::test_zom_envmap_insert);
+ uTEST(PointerListTest::test_zom_envmap_from_env);
+ uTEST(PointerListTest::test_exceptional_env);
+ }
+
+ void test_zom_pointerlist_push()
+ {
+ using namespace std::string_literals;
+
+ { // Zero
+ PointerList args;
+ uASSERT_EQUAL(0u, args.size());
+ auto [argc, argv] = args.get();
+ uASSERT_EQUAL(0, argc);
+ uASSERT(nullptr != argv);
+ uASSERT_EQUAL(nullptr, argv[0]);
+ }
+
+ { // One
+ PointerList args;
+ args.push_back("hello");
+ uASSERT_EQUAL(1u, args.size());
+ auto [argc, argv] = args.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello"s, std::string(argv[0]));
+ }
+
+ { // Many
+ PointerList args;
+ args.push_back("hello");
+ args.push_back("dear");
+ args.push_back("world");
+ uASSERT_EQUAL(3u, args.size());
+ auto [argc, argv] = args.get();
+ uASSERT_EQUAL(3, argc);
+ uASSERT_EQUAL("hello"s, std::string(argv[0]));
+ uASSERT_EQUAL("dear"s, std::string(argv[1]));
+ uASSERT_EQUAL("world"s, std::string(argv[2]));
+ }
+ }
+
+ void test_zom_pointerlist_from_args()
+ {
+ using namespace std::string_literals;
+
+ { // Zero
+ PointerList args(0, nullptr);
+ uASSERT_EQUAL(0u, args.size());
+ auto [argc, argv] = args.get();
+ uASSERT_EQUAL(0, argc);
+ uASSERT(nullptr != argv);
+ uASSERT_EQUAL(nullptr, argv[0]);
+ }
+
+ { // One
+ int _argc{1};
+ const char* _argv[] = { "hello" };
+ PointerList args(_argc, _argv);
+ uASSERT_EQUAL(1u, args.size());
+ auto [argc, argv] = args.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello"s, std::string(argv[0]));
+ }
+
+ { // Many
+ int _argc{3};
+ const char* _argv[] = { "hello", "dear", "world" };
+ PointerList args(_argc, _argv);
+ uASSERT_EQUAL(3u, args.size());
+ auto [argc, argv] = args.get();
+ uASSERT_EQUAL(3, argc);
+ // order must be preserved
+ uASSERT_EQUAL("hello"s, std::string(argv[0]));
+ uASSERT_EQUAL("dear"s, std::string(argv[1]));
+ uASSERT_EQUAL("world"s, std::string(argv[2]));
+ }
+ }
+
+ void test_zom_envmap_insert()
+ {
+ using namespace std::string_literals;
+
+ { // Zero
+ EnvMap env;
+ uASSERT_EQUAL(0u, env.size());
+
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(0, argc);
+ uASSERT(nullptr != argv);
+ uASSERT_EQUAL(nullptr, argv[0]);
+
+ auto str = env.stringify();
+ uASSERT_EQUAL(1u, str.size());
+ uASSERT_EQUAL('\0', str[0]);
+ }
+
+ { // One (key only)
+ EnvMap env;
+ env.insert("hello");
+ uASSERT_EQUAL(1u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello="s, std::string(argv[0]));
+ uASSERT_EQUAL(""s, env["hello"]);
+ }
+ { // One (with value)
+ EnvMap env;
+ env.insert("hello=world");
+ uASSERT_EQUAL(1u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello=world"s, std::string(argv[0]));
+ uASSERT_EQUAL("world"s, env["hello"]);
+
+ uASSERT_EQUAL("hello=world\0\0"s, env.stringify());
+ }
+
+ { // Overwrite one
+ EnvMap env;
+ env.insert("hello=world");
+ uASSERT_EQUAL(1u, env.size());
+ uASSERT_EQUAL("world"s, env["hello"s]);
+
+ env.insert("hello=foo");
+ uASSERT_EQUAL(1u, env.size());
+ uASSERT_EQUAL("foo"s, env["hello"s]);
+ }
+
+ { // Many
+ EnvMap env;
+ env.insert("hello=world");
+ env.insert("world=leader");
+ env.insert("dear=boar");
+ uASSERT_EQUAL(3u, env.size());
+
+ uASSERT_EQUAL("boar"s, env["dear"s]);
+ uASSERT_EQUAL("world"s, env["hello"s]);
+ uASSERT_EQUAL("leader"s, env["world"s]);
+
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(3, argc);
+ // store and sort to verify unordered
+ std::vector<std::string> vals{argv[0], argv[1], argv[2]};
+ std::ranges::sort(vals);
+ uASSERT_EQUAL("dear=boar"s, vals[0]);
+ uASSERT_EQUAL("hello=world"s, vals[1]);
+ uASSERT_EQUAL("world=leader"s, vals[2]);
+
+ // test all combinations since ordering is not a requirement
+ // exactly one of the must be true (boolean XOR)
+ auto str = env.stringify();
+ uASSERT(((((("dear=boar\0hello=world\0world=leader\0\0"s == str) !=
+ ("dear=boar\0world=leader\0hello=world\0\0"s == str)) !=
+ ("hello=world\0dear=boar\0world=leader\0\0"s == str)) !=
+ ("hello=world\0world=leader\0dear=boar\0\0"s == str)) !=
+ ("world=leader\0dear=boar\0hello=world\0\0"s == str)) !=
+ ("world=leader\0hello=world\0dear=boar\0\0"s == str));
+ }
+ }
+
+ void test_zom_envmap_from_env()
+ {
+ using namespace std::string_literals;
+
+ { // Zero
+ const char* penv = nullptr;
+ EnvMap env(penv);
+ uASSERT_EQUAL(0u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(0, argc);
+ uASSERT(nullptr != argv);
+ uASSERT_EQUAL(nullptr, argv[0]);
+ }
+
+ { // Zero
+ const char** ppenv = nullptr;
+ EnvMap env(ppenv);
+ uASSERT_EQUAL(0u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(0, argc);
+ uASSERT(nullptr != argv);
+ uASSERT_EQUAL(nullptr, argv[0]);
+ }
+
+ { // One (key only)
+ const char* ptr = "hello\0\0";
+ EnvMap env(ptr);
+ uASSERT_EQUAL(1u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello="s, std::string(argv[0]));
+ uASSERT_EQUAL(""s, env["hello"]);
+ }
+
+ { // One (key only)
+ const char* ptr[] = {"hello", nullptr};
+ EnvMap env(ptr);
+ uASSERT_EQUAL(1u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello="s, std::string(argv[0]));
+ uASSERT_EQUAL(""s, env["hello"]);
+ }
+
+ { // One (with value)
+ const char* ptr = "hello=world\0\0";
+ EnvMap env(ptr);
+ uASSERT_EQUAL(1u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello=world"s, std::string(argv[0]));
+ uASSERT_EQUAL("world"s, env["hello"]);
+
+ uASSERT_EQUAL("hello=world\0\0"s, env.stringify());
+ }
+
+ { // One (with value)
+ const char* ptr[] = {"hello=world\0", nullptr};
+ EnvMap env(ptr);
+ uASSERT_EQUAL(1u, env.size());
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(1, argc);
+ uASSERT_EQUAL("hello=world"s, std::string(argv[0]));
+ uASSERT_EQUAL("world"s, env["hello"]);
+
+ uASSERT_EQUAL("hello=world\0\0"s, env.stringify());
+ }
+
+ { // Many
+ const char* ptr = "hello=world\0world=leader\0dear=boar\0\0";
+ EnvMap env(ptr);
+ uASSERT_EQUAL(3u, env.size());
+
+ uASSERT_EQUAL("boar"s, env["dear"s]);
+ uASSERT_EQUAL("world"s, env["hello"s]);
+ uASSERT_EQUAL("leader"s, env["world"s]);
+
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(3, argc);
+ // store and sort to verify unordered
+ std::vector<std::string> vals{argv[0], argv[1], argv[2]};
+ std::ranges::sort(vals);
+ uASSERT_EQUAL("dear=boar"s, vals[0]);
+ uASSERT_EQUAL("hello=world"s, vals[1]);
+ uASSERT_EQUAL("world=leader"s, vals[2]);
+
+ // test all combinations since ordering is not a requirement
+ // exactly one of the must be true (boolean XOR)
+ auto str = env.stringify();
+ uASSERT(((((("dear=boar\0hello=world\0world=leader\0\0"s == str) !=
+ ("dear=boar\0world=leader\0hello=world\0\0"s == str)) !=
+ ("hello=world\0dear=boar\0world=leader\0\0"s == str)) !=
+ ("hello=world\0world=leader\0dear=boar\0\0"s == str)) !=
+ ("world=leader\0dear=boar\0hello=world\0\0"s == str)) !=
+ ("world=leader\0hello=world\0dear=boar\0\0"s == str));
+ }
+
+ { // Many
+ const char* ptr[] =
+ {"hello=world\0", "world=leader\0", "dear=boar\0", nullptr};
+ EnvMap env(ptr);
+ uASSERT_EQUAL(3u, env.size());
+
+ uASSERT_EQUAL("boar"s, env["dear"s]);
+ uASSERT_EQUAL("world"s, env["hello"s]);
+ uASSERT_EQUAL("leader"s, env["world"s]);
+
+ auto [argc, argv] = env.get();
+ uASSERT_EQUAL(3, argc);
+ // store and sort to verify unordered
+ std::vector<std::string> vals{argv[0], argv[1], argv[2]};
+ std::ranges::sort(vals);
+ uASSERT_EQUAL("dear=boar"s, vals[0]);
+ uASSERT_EQUAL("hello=world"s, vals[1]);
+ uASSERT_EQUAL("world=leader"s, vals[2]);
+
+ // test all combinations since ordering is not a requirement
+ // exactly one of the must be true (boolean XOR)
+ auto str = env.stringify();
+ uASSERT(((((("dear=boar\0hello=world\0world=leader\0\0"s == str) !=
+ ("dear=boar\0world=leader\0hello=world\0\0"s == str)) !=
+ ("hello=world\0dear=boar\0world=leader\0\0"s == str)) !=
+ ("hello=world\0world=leader\0dear=boar\0\0"s == str)) !=
+ ("world=leader\0dear=boar\0hello=world\0\0"s == str)) !=
+ ("world=leader\0hello=world\0dear=boar\0\0"s == str));
+ }
+ }
+
+ void test_exceptional_env()
+ {
+ using namespace std::string_literals;
+
+ { // Zero
+ EnvMap env;
+ uASSERT_EQUAL(0u, env.size());
+ uASSERT_EQUAL(""s, env["foo"]); // lookup of non-existing key
+ }
+ }
+};
+
+// Registers the fixture into the 'registry'
+static PointerListTest test;
diff --git a/test/tasks_test.cc b/test/tasks_test.cc
index 5f1db26..b444bd5 100644
--- a/test/tasks_test.cc
+++ b/test/tasks_test.cc
@@ -10,6 +10,7 @@ ctor::build_configurations ctorTestConfigs1(const ctor::settings&)
return
{
{
+ .name = "Target1",
.target = "target1",
.sources = {"foo.cc", "bar.c"},
},
@@ -33,16 +34,29 @@ ctor::build_configurations ctorTestConfigs2(const ctor::settings&)
}
}
+namespace test_global {
+ctor::toolchain toolchain{};
+ctor::arch arch{};
+}
+const ctor::configuration& ctor::get_configuration()
+{
+ static ctor::configuration cfg{};
+ cfg.build_toolchain = test_global::toolchain;
+ cfg.build_arch = test_global::arch;
+ return cfg;
+}
+
+
REG(ctorTestConfigs1);
REG(ctorTestConfigs2);
-std::size_t count(const std::set<std::shared_ptr<Task>>& tasks,
- const std::string& name)
+std::size_t count(const std::vector<std::shared_ptr<Task>>& tasks,
+ const std::filesystem::path& name)
{
auto cnt{0u};
for(const auto& task : tasks)
{
- if(task->target() == name)
+ if(task->target() == name.string())
{
cnt++;
}
@@ -54,16 +68,17 @@ class TestTask
: public Task
{
public:
- TestTask(const std::string& name, bool dirty,
+ TestTask(const ctor::build_configuration& config,
+ const ctor::settings& settings,
+ const std::string& name, bool dirty,
const std::vector<std::string>& deps = {})
- : Task({}, {}, {})
+ : Task(config, settings, {})
, task_name(name)
, task_dirty(dirty)
, task_deps(deps)
{
}
- std::string name() const override { return task_name; }
int clean() override { return 0; }
std::vector<std::string> depends() const override { return task_deps; }
std::string target() const override { return task_name; }
@@ -81,11 +96,14 @@ class TasksTest
: public uUnit
{
public:
+ using fs = std::filesystem::path;
+
TasksTest()
{
uTEST(TasksTest::getTargets_test);
uTEST(TasksTest::getTasks_test);
uTEST(TasksTest::getNextTask_test);
+ uTEST(TasksTest::comparison_test);
}
void getTargets_test()
@@ -108,27 +126,29 @@ public:
void getTasks_test()
{
+ test_global::toolchain = ctor::toolchain::gcc;
+ test_global::arch = ctor::arch::unix;
+
using namespace std::string_literals;
ctor::settings settings{ .builddir = "foo" };
{
auto tasks = getTasks(settings);
uASSERT_EQUAL(6u, tasks.size());
- // Note: count() is used here because the order of
- // std::set<std::shared_ptr<T>> is not deterministic.
- uASSERT_EQUAL(1u, count(tasks, "target1"s));
- uASSERT_EQUAL(1u, count(tasks, "target2"s));
- uASSERT_EQUAL(1u, count(tasks, "target3"s));
- uASSERT_EQUAL(1u, count(tasks, "target4"s));
- uASSERT_EQUAL(1u, count(tasks, "test/target1-foo_cc.o"s));
- uASSERT_EQUAL(1u, count(tasks, "test/target1-bar_c.o"s));
+ // Note: count() is used here because the order doesn't matter
+ uASSERT_EQUAL(1u, count(tasks, fs("target1")));
+ uASSERT_EQUAL(1u, count(tasks, fs("target2")));
+ uASSERT_EQUAL(1u, count(tasks, fs("target3")));
+ uASSERT_EQUAL(1u, count(tasks, fs("target4")));
+ uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-foo_cc.o"));
+ uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-bar_c.o"));
}
{
auto tasks = getTasks(settings, {"target1", "target3"});
uASSERT_EQUAL(4u, tasks.size());
- uASSERT_EQUAL(1u, count(tasks, "target1"s));
- uASSERT_EQUAL(1u, count(tasks, "target3"s));
- uASSERT_EQUAL(1u, count(tasks, "test/target1-foo_cc.o"s));
- uASSERT_EQUAL(1u, count(tasks, "test/target1-bar_c.o"s));
+ uASSERT_EQUAL(1u, count(tasks, fs("target1")));
+ uASSERT_EQUAL(1u, count(tasks, fs("target3")));
+ uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-foo_cc.o"));
+ uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-bar_c.o"));
}
{
auto tasks = getTasks(settings, {"no-such-target"});
@@ -142,8 +162,8 @@ public:
ctor::settings settings{};
{ // Zero (Empty)
- std::set<std::shared_ptr<Task>> allTasks;
- std::set<std::shared_ptr<Task>> dirtyTasks;
+ std::vector<std::shared_ptr<Task>> allTasks;
+ std::vector<std::shared_ptr<Task>> dirtyTasks;
for(auto& task : dirtyTasks)
{
@@ -154,12 +174,13 @@ public:
}
{ // Zero (One task, no dirty)
- auto task1 = std::make_shared<TestTask>("task1", false);
+ ctor::build_configuration config;
+ auto task1 = std::make_shared<TestTask>(config, settings, "task1", false);
- std::set<std::shared_ptr<Task>> allTasks;
- allTasks.insert(task1);
+ std::vector<std::shared_ptr<Task>> allTasks;
+ allTasks.push_back(task1);
- std::set<std::shared_ptr<Task>> dirtyTasks;
+ std::vector<std::shared_ptr<Task>> dirtyTasks;
for(auto& task : dirtyTasks)
{
@@ -170,13 +191,14 @@ public:
}
{ // One (One task, one dirty)
- auto task1 = std::make_shared<TestTask>("task1", true);
+ ctor::build_configuration config;
+ auto task1 = std::make_shared<TestTask>(config, settings, "task1", true);
- std::set<std::shared_ptr<Task>> allTasks;
- allTasks.insert(task1);
+ std::vector<std::shared_ptr<Task>> allTasks;
+ allTasks.push_back(task1);
- std::set<std::shared_ptr<Task>> dirtyTasks;
- dirtyTasks.insert(task1);
+ std::vector<std::shared_ptr<Task>> dirtyTasks;
+ dirtyTasks.push_back(task1);
for(auto& task : dirtyTasks)
{
@@ -188,15 +210,16 @@ public:
}
{ // One (Two tasks, one dirty)
- auto task1 = std::make_shared<TestTask>("task1", false);
- auto task2 = std::make_shared<TestTask>("task2", true);
+ ctor::build_configuration config;
+ auto task1 = std::make_shared<TestTask>(config, settings, "task1", false);
+ auto task2 = std::make_shared<TestTask>(config, settings, "task2", true);
- std::set<std::shared_ptr<Task>> allTasks;
- allTasks.insert(task1);
- allTasks.insert(task2);
+ std::vector<std::shared_ptr<Task>> allTasks;
+ allTasks.push_back(task1);
+ allTasks.push_back(task2);
- std::set<std::shared_ptr<Task>> dirtyTasks;
- dirtyTasks.insert(task2);
+ std::vector<std::shared_ptr<Task>> dirtyTasks;
+ dirtyTasks.push_back(task2);
for(auto& task : dirtyTasks)
{
@@ -208,17 +231,19 @@ public:
}
{ // One (Two tasks, one dirty which depends on the other)
- auto task1 = std::make_shared<TestTask>("task1", false);
+ ctor::build_configuration config;
+ auto task1 = std::make_shared<TestTask>(config, settings, "task1", false);
std::vector<std::string> deps = {"task1"};
- auto task2 = std::make_shared<TestTask>("task2", true, deps);
+ auto task2 =
+ std::make_shared<TestTask>(config, settings, "task2", true, deps);
- std::set<std::shared_ptr<Task>> allTasks;
- allTasks.insert(task1);
- allTasks.insert(task2);
+ std::vector<std::shared_ptr<Task>> allTasks;
+ allTasks.push_back(task1);
+ allTasks.push_back(task2);
- std::set<std::shared_ptr<Task>> dirtyTasks;
- dirtyTasks.insert(task2);
+ std::vector<std::shared_ptr<Task>> dirtyTasks;
+ dirtyTasks.push_back(task2);
for(auto& task : dirtyTasks)
{
@@ -230,18 +255,20 @@ public:
}
{ // One (Two tasks, Both dirty, one depends on the other)
- auto task1 = std::make_shared<TestTask>("task1", true);
+ ctor::build_configuration config{};
+ auto task1 = std::make_shared<TestTask>(config, settings, "task1", true);
std::vector<std::string> deps = {"task1"};
- auto task2 = std::make_shared<TestTask>("task2", true, deps);
+ auto task2 =
+ std::make_shared<TestTask>(config, settings, "task2", true, deps);
- std::set<std::shared_ptr<Task>> allTasks;
- allTasks.insert(task2);
- allTasks.insert(task1);
+ std::vector<std::shared_ptr<Task>> allTasks;
+ allTasks.push_back(task2);
+ allTasks.push_back(task1);
- std::set<std::shared_ptr<Task>> dirtyTasks;
- dirtyTasks.insert(task2);
- dirtyTasks.insert(task1);
+ std::vector<std::shared_ptr<Task>> dirtyTasks;
+ dirtyTasks.push_back(task2);
+ dirtyTasks.push_back(task1);
for(auto& task : dirtyTasks)
{
@@ -251,7 +278,51 @@ public:
uASSERT_EQUAL(task1, getNextTask(allTasks, dirtyTasks));
uASSERT_EQUAL(1u, dirtyTasks.size());
}
+ }
+ void comparison_test()
+ {
+ test_global::toolchain = ctor::toolchain::gcc;
+ test_global::arch = ctor::arch::unix;
+
+ using namespace std::string_literals;
+ ctor::settings settings{ .builddir = "foo" };
+ { // Test Task::operator==
+ auto tasks = getTasks(settings, {"target1"});
+ uASSERT_EQUAL(3u, tasks.size());
+ uASSERT_EQUAL(1u, count(tasks, fs("target1")));
+ uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-foo_cc.o"));
+ uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-bar_c.o"));
+
+ int cnt1{};
+ int cnt2{};
+ int cnt3{};
+ for(const auto& task : tasks)
+ {
+ if(task->target() == fs("target1"))
+ {
+ ++cnt1;
+ uASSERT(*task == "target1");
+ uASSERT(*task == "Target1");
+ }
+ if(task->target() == fs("test")/"target1-foo_cc.o")
+ {
+ ++cnt2;
+ uASSERT(*task != "target1");
+ uASSERT(*task != "Target1");
+ }
+ if(task->target() == fs("test")/"target1-bar_c.o")
+ {
+ ++cnt3;
+ uASSERT(*task != "target1");
+ uASSERT(*task != "Target1");
+ }
+ }
+ // Assert that we did actually perform all three tests exactly once
+ uASSERT_EQUAL(1, cnt1);
+ uASSERT_EQUAL(1, cnt2);
+ uASSERT_EQUAL(1, cnt3);
+ }
}
};
diff --git a/test/testprog.cc b/test/testprog.cc
new file mode 100644
index 0000000..faf1498
--- /dev/null
+++ b/test/testprog.cc
@@ -0,0 +1,53 @@
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <signal.h>
+
+int main(int argc, const char* argv[])
+{
+ if(argc < 2)
+ {
+ return 0;
+ }
+
+ std::string cmd = argv[1];
+
+ if(cmd == "envdump")
+ {
+ if(argc < 3)
+ {
+ return 0;
+ }
+ std::ofstream ostrm(argv[2], std::ios::binary);
+ for(auto current = environ; *current; ++current)
+ {
+ ostrm << (*current) << "\n";
+ }
+ }
+
+ if(cmd == "retval")
+ {
+ if(argc < 3)
+ {
+ return 0;
+ }
+ return std::stoi(argv[2]);
+ }
+
+ if(cmd == "abort")
+ {
+ abort();
+ }
+
+ if(cmd == "segfault")
+ {
+ raise(SIGSEGV);
+ }
+
+ if(cmd == "throw")
+ {
+ throw "ouch";
+ }
+
+ return 0;
+}
diff --git a/test/tmpfile.h b/test/tmpfile.h
new file mode 100644
index 0000000..5d114d0
--- /dev/null
+++ b/test/tmpfile.h
@@ -0,0 +1,48 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#pragma once
+
+#include <cstdlib>
+#include <unistd.h>
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#endif
+
+struct tmp_file
+{
+ tmp_file(const std::string& data = {})
+ {
+ int fd;
+#ifdef _WIN32
+ char templ[] = "ctor_tmp_file-XXXXXX"; // buffer for filename
+ _mktemp_s(templ, sizeof(templ));
+ fd = open(templ, O_CREAT | O_RDWR);
+#else
+ char templ[] = "/tmp/ctor_tmp_file-XXXXXX"; // buffer for filename
+ fd = mkstemp(templ);
+#endif
+ filename = templ;
+ auto sz = write(fd, data.data(), data.size());
+ (void)sz;
+ close(fd);
+ }
+
+ ~tmp_file()
+ {
+ unlink(filename.data());
+ }
+
+ const std::string& get() const
+ {
+ return filename;
+ }
+
+private:
+ std::string filename;
+};
diff --git a/test/tools_test.cc b/test/tools_test.cc
index 1d377b7..46ab084 100644
--- a/test/tools_test.cc
+++ b/test/tools_test.cc
@@ -114,33 +114,27 @@ bool operator!=(const ctor::asm_flag& a, const ctor::asm_flag& b)
#include <uunit.h>
-namespace {
-std::string conf_host_cxx{};
-std::string conf_build_cxx{};
-}
-
const ctor::configuration& ctor::get_configuration()
{
static ctor::configuration cfg;
return cfg;
}
-const std::string& ctor::configuration::get(const std::string& key, const std::string& defval) const
+std::string ctor::configuration::get(const std::string& key, [[maybe_unused]]const std::string& default_value) const
{
if(key == ctor::cfg::host_cxx)
{
- return conf_host_cxx;
+ return {};
}
if(key == ctor::cfg::build_cxx)
{
- return conf_build_cxx;
+ return {};
}
assert(false); // bad key
- static std::string res{};
- return res;
+ return {};
}
class ToolsTest
@@ -172,9 +166,24 @@ public:
void getToolChain_test()
{
+ //
+ // gcc
+ //
+ uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/gcc"));
+ uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/gcc-10"));
+ uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/x86_64-pc-linux-gnu-g++-9.3.0"));
+
uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/g++"));
uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/g++-10"));
uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/x86_64-pc-linux-gnu-g++-9.3.0"));
+
+ //
+ // clang
+ //
+ uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang"));
+ uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang-16"));
+ uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/lib/llvm/16/bin/i686-pc-linux-gnu-clang-16"));
+
uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang++"));
uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang++-16"));
uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/lib/llvm/16/bin/i686-pc-linux-gnu-clang++-16"));
@@ -666,22 +675,54 @@ public:
//
// gcc
//
+ exp = { "-r" };
+ act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::replace);
+ uASSERT_EQUAL(exp, act);
+
+ exp = { "-s" };
+ act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::add_index);
+ uASSERT_EQUAL(exp, act);
+
+ exp = { "-c" };
+ act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::create);
+ uASSERT_EQUAL(exp, act);
+
+ exp = { "foo" };
+ act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::output, "foo");
+ uASSERT_EQUAL(exp, act);
+
exp = { "-foo" };
- act = asm_option(ctor::toolchain::gcc, ctor::asm_opt::custom, "-foo");
+ act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::custom, "-foo");
uASSERT_EQUAL(exp, act);
//
// clang
//
+ exp = { "-r" };
+ act = ar_option(ctor::toolchain::clang, ctor::ar_opt::replace);
+ uASSERT_EQUAL(exp, act);
+
+ exp = { "-s" };
+ act = ar_option(ctor::toolchain::clang, ctor::ar_opt::add_index);
+ uASSERT_EQUAL(exp, act);
+
+ exp = { "-c" };
+ act = ar_option(ctor::toolchain::clang, ctor::ar_opt::create);
+ uASSERT_EQUAL(exp, act);
+
+ exp = { "foo" };
+ act = ar_option(ctor::toolchain::clang, ctor::ar_opt::output, "foo");
+ uASSERT_EQUAL(exp, act);
+
exp = { "-foo" };
- act = asm_option(ctor::toolchain::clang, ctor::asm_opt::custom, "-foo");
+ act = ar_option(ctor::toolchain::clang, ctor::ar_opt::custom, "-foo");
uASSERT_EQUAL(exp, act);
//
// any
//
- exp = { "{ctor::asm_opt::custom, \"-foo\"}" };
- act = asm_option(ctor::toolchain::any, ctor::asm_opt::custom, "-foo");
+ exp = { "{ctor::ar_opt::custom, \"-foo\"}" };
+ act = ar_option(ctor::toolchain::any, ctor::ar_opt::custom, "-foo");
uASSERT_EQUAL(exp, act);
}
diff --git a/test/uunit b/test/uunit
-Subproject bc078da645412c6b36ef59e635d6c35d11088c9
+Subproject 0f371777e02dd068f9675a05a29230221d5d6a7