diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/ctor.cc | 78 | ||||
-rw-r--r-- | test/cycle_test.cc | 87 | ||||
-rw-r--r-- | test/deps_test.cc | 97 | ||||
-rw-r--r-- | test/deps_test_data/empty.d | 0 | ||||
-rw-r--r-- | test/deps_test_data/missing_colon.d | 1 | ||||
-rw-r--r-- | test/deps_test_data/multiline.d | 4 | ||||
-rw-r--r-- | test/deps_test_data/no_deps.d | 1 | ||||
-rw-r--r-- | test/deps_test_data/no_newline.d | 1 | ||||
-rw-r--r-- | test/deps_test_data/spaces.d | 1 | ||||
-rw-r--r-- | test/deps_test_data/trivial.d | 1 | ||||
-rw-r--r-- | test/execute_test.cc | 83 | ||||
-rw-r--r-- | test/pointerlist_test.cc | 320 | ||||
-rw-r--r-- | test/tasks_test.cc | 173 | ||||
-rw-r--r-- | test/testprog.cc | 53 | ||||
-rw-r--r-- | test/tmpfile.h | 48 | ||||
-rw-r--r-- | test/tools_test.cc | 69 | ||||
m--------- | test/uunit | 0 |
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 |