summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/argparser_test.cc1019
-rw-r--r--test/argsplit_test.cc203
-rw-r--r--test/ctor.cc90
-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.cc22
-rw-r--r--test/pointerlist_test.cc320
-rwxr-xr-xtest/suite/test.sh24
-rw-r--r--test/tasks_test.cc130
-rw-r--r--test/testprog.cc8
-rw-r--r--test/tmpfile.h44
-rw-r--r--test/tools_test.cc28
m---------test/uunit0
20 files changed, 1987 insertions, 94 deletions
diff --git a/test/argparser_test.cc b/test/argparser_test.cc
new file mode 100644
index 0000000..b649e8c
--- /dev/null
+++ b/test/argparser_test.cc
@@ -0,0 +1,1019 @@
+#include <argparser.h>
+
+#include <iostream>
+#include <string>
+
+std::ostream& operator<<(std::ostream& ostr, const arg::error& err)
+{
+ switch(err)
+ {
+ case arg::error::invalid_arg:
+ ostr << "arg::error::invalid_arg";
+ break;
+ case arg::error::invalid_opt:
+ ostr << "arg::error::invalid_opt";
+ break;
+ case arg::error::missing_arg:
+ ostr << "arg::error::missing_arg";
+ break;
+ }
+ return ostr;
+}
+
+#include <uunit.h>
+
+class ArgParserTest
+ : public uUnit
+{
+public:
+ ArgParserTest()
+ {
+ uTEST(ArgParserTest::test_zero);
+ uTEST(ArgParserTest::test_one);
+ uTEST(ArgParserTest::test_many);
+ uTEST(ArgParserTest::test_exceptional);
+ uTEST(ArgParserTest::test_err_callback);
+ uTEST(ArgParserTest::test_pos_callback);
+ uTEST(ArgParserTest::test_grouped);
+ uTEST(ArgParserTest::test_nullprogram);
+ }
+
+ void test_zero()
+ {
+ const char* const argv[] = { "app-name" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int> args(argc, argv);
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ }
+
+ void test_one()
+ {
+ const char* argv[] = { "app-name", "-x", "42" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(42, x);
+ }
+
+ void test_many()
+ {
+ const char* argv[] = { "app-name", "-x", "42", "-y17", "-z",
+ "--long-X=12", "--long-Y", "18" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ int y{};
+ bool z{false};
+ int X{};
+ int Y{};
+
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](int i){ y = i; return 0;}), "Help y");
+
+ args.add('z', "--long-z",
+ std::function([&](){ z = true; return 0;}), "Help z");
+
+ args.add('X', "--long-X",
+ std::function([&](int i){ X = i; return 0;}), "Help X");
+
+ args.add('Y', "--long-Y",
+ std::function([&](int i){ Y = i; return 0;}), "Help Y");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(42, x);
+ uASSERT_EQUAL(17, y);
+ uASSERT_EQUAL(true, z);
+ uASSERT_EQUAL(12, X);
+ uASSERT_EQUAL(18, Y);
+ }
+
+ void test_exceptional()
+ {
+
+ { // Missing arg at trailing opt
+ const char* argv[] = { "app-name", "-x" };
+ int argc = sizeof(argv)/sizeof(*argv);
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ int y{};
+
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](int i){ y = i; return 0;}), "Help y");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(1, res);
+ }
+
+ { // Missing arg before other opt
+ const char* argv[] = { "app-name", "-x", "-y" };
+ int argc = sizeof(argv)/sizeof(*argv);
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ int y{};
+
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](int i){ y = i; return 0;}), "Help y");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(1, res);
+ }
+
+ { // Unknown arg
+ const char* argv[] = { "app-name", "-T" };
+ int argc = sizeof(argv)/sizeof(*argv);
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ int y{};
+
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](int i){ y = i; return 0;}), "Help y");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(1, res);
+ }
+ }
+
+ void test_err_callback()
+ {
+ using namespace std::string_literals;
+
+ { // Missing arg at trailing opt
+ const char* argv[] = { "app-name", "-x" };
+ int argc = sizeof(argv)/sizeof(*argv);
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ bool called{false};
+ args.set_err_cb(
+ [&](arg::error err, std::string_view opt)
+ {
+ called = true;
+ uASSERT_EQUAL(arg::error::missing_arg, err);
+ uASSERT_EQUAL("-x"s, opt);
+ });
+ auto res = args.parse();
+ uASSERT_EQUAL(1, res);
+ uASSERT(called);
+ }
+
+ { // Invalid arg format
+ const char* argv[] = { "app-name", "-x", "abc" };
+ int argc = sizeof(argv)/sizeof(*argv);
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ bool called{false};
+ args.set_err_cb(
+ [&](arg::error err, std::string_view opt)
+ {
+ called = true;
+ uASSERT_EQUAL(arg::error::invalid_arg, err);
+ uASSERT_EQUAL("abc"s, opt);
+ });
+ auto res = args.parse();
+ uASSERT_EQUAL(1, res);
+ uASSERT(called);
+ }
+
+ { // Invalid opt
+ const char* argv[] = { "app-name", "-y" };
+ int argc = sizeof(argv)/sizeof(*argv);
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ bool called{false};
+ args.set_err_cb(
+ [&](arg::error err, std::string_view opt)
+ {
+ called = true;
+ uASSERT_EQUAL(arg::error::invalid_opt, err);
+ uASSERT_EQUAL("-y"s, opt);
+ });
+ auto res = args.parse();
+ uASSERT_EQUAL(1, res);
+ uASSERT(called);
+ }
+ }
+
+ void test_pos_callback()
+ {
+ using namespace std::string_literals;
+ const char* argv[] =
+ { "app-name", "foo", "-x", "42", "bar", "-X43", "-Y", "42" };
+ int argc = sizeof(argv)/sizeof(*argv);
+ arg::Parser<int, std::optional<int>> args(argc, argv);
+
+ int x{};
+ int X{};
+ int Y{};
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ args.add('X', "--opt-x",
+ std::function([&](std::optional<int> i)
+ {
+ uASSERT(i.has_value());
+ X = *i;
+ return 0;
+ }),
+ "Help X");
+
+ args.add('Y', "--opt-y",
+ std::function([&](std::optional<int> i)
+ {
+ uASSERT(!i.has_value());
+ Y = 1;
+ return 0;
+ }),
+ "Help X");
+
+ std::vector<std::string> pos;
+ args.set_pos_cb(
+ [&](std::string_view sv)
+ {
+ pos.push_back(std::string(sv));
+ return 0;
+ });
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(3u, pos.size());
+ uASSERT_EQUAL("foo"s, pos[0]);
+ uASSERT_EQUAL("bar"s, pos[1]);
+ uASSERT_EQUAL("42"s, pos[2]);
+ uASSERT_EQUAL(42, x);
+ uASSERT_EQUAL(43, X);
+ uASSERT_EQUAL(1, Y);
+ }
+
+ void test_grouped()
+ {
+ {
+ const char* argv[] = { "app-name", "-xyz", "42" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int> args(argc, argv);
+
+ bool x{false};
+ bool y{false};
+ int z{};
+ args.add('x', "--long-x",
+ std::function([&](){ x = true; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](){ y = true; return 0;}), "Help y");
+
+ args.add('z', "--long-z",
+ std::function([&](int i){ z = i; return 0;}), "Help z");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT(x);
+ uASSERT(y);
+ uASSERT_EQUAL(42, z);
+ }
+
+ {
+ const char* argv[] = { "app-name", "-xyz42" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int> args(argc, argv);
+
+ bool x{false};
+ bool y{false};
+ int z{};
+ args.add('x', "--long-x",
+ std::function([&](){ x = true; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](){ y = true; return 0;}), "Help y");
+
+ args.add('z', "--long-z",
+ std::function([&](int i){ z = i; return 0;}), "Help z");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT(x);
+ uASSERT(y);
+ uASSERT_EQUAL(42, z);
+ }
+
+
+ {
+ const char* argv[] = { "app-name", "-xyz42" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int, std::optional<int>> args(argc, argv);
+
+ bool x{false};
+ bool y{false};
+ int z{};
+ args.add('x', "--long-x",
+ std::function([&](){ x = true; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](){ y = true; return 0;}), "Help y");
+
+ args.add('z', "--long-z",
+ std::function([&](std::optional<int> i)
+ {
+ uASSERT(i.has_value());
+ z = *i; return 0;
+ }), "Help z");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT(x);
+ uASSERT(y);
+ uASSERT_EQUAL(42, z);
+ }
+
+ {
+ const char* argv[] = { "app-name", "-xyz" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int, std::optional<int>> args(argc, argv);
+
+ bool x{false};
+ bool y{false};
+ int z{};
+ args.add('x', "--long-x",
+ std::function([&](){ x = true; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](){ y = true; return 0;}), "Help y");
+
+ args.add('z', "--long-z",
+ std::function([&](std::optional<int> i)
+ {
+ uASSERT(!i.has_value());
+ z = 1; return 0;
+ }), "Help z");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT(x);
+ uASSERT(y);
+ uASSERT_EQUAL(1, z);
+ }
+
+ {
+ const char* argv[] = { "app-name", "-xyz", "42" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int, std::optional<int>> args(argc, argv);
+
+ bool x{false};
+ bool y{false};
+ int z{};
+ args.add('x', "--long-x",
+ std::function([&](){ x = true; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](){ y = true; return 0;}), "Help y");
+
+ args.add('z', "--long-z",
+ std::function([&](std::optional<int> i)
+ {
+ uASSERT(!i.has_value());
+ z = 1; return 0;
+ }), "Help z");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT(x);
+ uASSERT(y);
+ uASSERT_EQUAL(1, z);
+ }
+ }
+
+ void test_nullprogram()
+ {
+ using namespace std::string_literals;
+ // Inspired by https://nullprogram.com/blog/2020/08/01/
+
+ //
+ // Short options
+ //
+ {
+ const char* argv[] = { "program", "-a", "-b", "-c" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<char> r;
+ args.add('a', {}, std::function([&](){ r.push_back('a'); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back('b'); return 0;}), {});
+ args.add('c', {}, std::function([&](){ r.push_back('c'); return 0;}), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(3u, r.size());
+ uASSERT_EQUAL('a', r[0]);
+ uASSERT_EQUAL('b', r[1]);
+ uASSERT_EQUAL('c', r[2]);
+ }
+
+ {
+ const char* argv[] = { "program", "-abc" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<char> r;
+ args.add('a', {}, std::function([&](){ r.push_back('a'); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back('b'); return 0;}), {});
+ args.add('c', {}, std::function([&](){ r.push_back('c'); return 0;}), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(3u, r.size());
+ uASSERT_EQUAL('a', r[0]);
+ uASSERT_EQUAL('b', r[1]);
+ uASSERT_EQUAL('c', r[2]);
+ }
+
+ {
+ const char* argv[] = { "program", "-acb" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<char> r;
+ args.add('a', {}, std::function([&](){ r.push_back('a'); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back('b'); return 0;}), {});
+ args.add('c', {}, std::function([&](){ r.push_back('c'); return 0;}), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(3u, r.size());
+ uASSERT_EQUAL('a', r[0]);
+ uASSERT_EQUAL('c', r[1]);
+ uASSERT_EQUAL('b', r[2]);
+ }
+
+ {
+ const char* argv[] = { "program", "-i", "input.txt", "-o", "output.txt" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::string> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('i', {},
+ std::function([&](std::string input)
+ {
+ r.push_back(input);
+ return 0;
+ }), {});
+
+ args.add('o', {},
+ std::function([&](std::string input)
+ {
+ r.push_back(input);
+ return 0;
+ }), {});
+
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("input.txt"s, r[0]);
+ uASSERT_EQUAL("output.txt"s, r[1]);
+ }
+
+ {
+ const char* argv[] = { "program", "-iinput.txt", "-ooutput.txt" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::string> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('i', {},
+ std::function([&](std::string input)
+ {
+ r.push_back(input);
+ return 0;
+ }), {});
+ args.add('o', {},
+ std::function([&](std::string input)
+ {
+ r.push_back(input);
+ return 0;
+ }), {});
+
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("input.txt"s, r[0]);
+ uASSERT_EQUAL("output.txt"s, r[1]);
+ }
+
+ {
+ const char* argv[] = { "program", "-abco", "output.txt" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::string> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.add('c', {}, std::function([&](){ r.push_back("c"); return 0;}), {});
+ args.add('o', {},
+ std::function([&](std::string input)
+ {
+ r.push_back(input);
+ return 0;
+ }), {});
+
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(4u, r.size());
+ uASSERT_EQUAL("a"s, r[0]);
+ uASSERT_EQUAL("b"s, r[1]);
+ uASSERT_EQUAL("c"s, r[2]);
+ uASSERT_EQUAL("output.txt"s, r[3]);
+ }
+
+ {
+ const char* argv[] = { "program", "-abcooutput.txt" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::string> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.add('c', {}, std::function([&](){ r.push_back("c"); return 0;}), {});
+ args.add('o', {},
+ std::function([&](std::string input)
+ {
+ r.push_back(input);
+ return 0;
+ }), {});
+
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(4u, r.size());
+ uASSERT_EQUAL("a"s, r[0]);
+ uASSERT_EQUAL("b"s, r[1]);
+ uASSERT_EQUAL("c"s, r[2]);
+ uASSERT_EQUAL("output.txt"s, r[3]);
+ }
+
+ {
+ // Optional omitted
+ const char* argv[] = { "program", "-c" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::optional<std::string>> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('c', {},
+ std::function([&](std::optional<std::string> c)
+ {
+ uASSERT(!c.has_value());
+ r.push_back("c");
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(1u, r.size());
+ uASSERT_EQUAL("c"s, r[0]);
+ }
+
+ {
+ // Optional provided
+ const char* argv[] = { "program", "-cblue" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::optional<std::string>> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('c', {},
+ std::function([&](std::optional<std::string> c)
+ {
+ uASSERT(c.has_value());
+ r.push_back(*c);
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(1u, r.size());
+ uASSERT_EQUAL("blue"s, r[0]);
+ }
+
+ {
+ // Optional omitted (blue is a new argument)
+ const char* argv[] = { "program", "-c", "blue" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::optional<std::string>> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('c', {},
+ std::function([&](std::optional<std::string> c)
+ {
+ uASSERT(!c.has_value());
+ r.push_back("c");
+ return 0;
+ }), {});
+ args.set_pos_cb(std::function([&](std::string_view pos)
+ {
+ r.push_back(std::string(pos));
+ return 0;
+ }));
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("c"s, r[0]);
+ uASSERT_EQUAL("blue"s, r[1]);
+ }
+
+ {
+ // Two seperate flags
+ const char* argv[] = { "program", "-c", "-x" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::optional<std::string>> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('x', {}, std::function([&](){ r.push_back("x"); return 0;}), {});
+ args.add('c', {},
+ std::function([&](std::optional<std::string> c)
+ {
+ uASSERT(!c.has_value());
+ r.push_back("c");
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("c"s, r[0]);
+ uASSERT_EQUAL("x"s, r[1]);
+ }
+
+ {
+ // -c with argument "-x"
+ const char* argv[] = { "program", "-c-x" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::optional<std::string>> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('x', {}, std::function([&](){ r.push_back("x"); return 0;}), {});
+ args.add('c', {},
+ std::function([&](std::optional<std::string> c)
+ {
+ uASSERT(c.has_value());
+ r.push_back(*c);
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(1u, r.size());
+ uASSERT_EQUAL("-x"s, r[0]);
+ }
+
+ {
+ const char* argv[] = { "program", "-a", "-b", "foo", "bar" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.set_pos_cb(std::function([&](std::string_view pos)
+ {
+ r.push_back(std::string(pos));
+ return 0;
+ }));
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(4u, r.size());
+ uASSERT_EQUAL("a"s, r[0]);
+ uASSERT_EQUAL("b"s, r[1]);
+ uASSERT_EQUAL("foo"s, r[2]);
+ uASSERT_EQUAL("bar"s, r[3]);
+ }
+
+ {
+ const char* argv[] = { "program", "-b", "-a", "foo", "bar" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.set_pos_cb(std::function([&](std::string_view pos)
+ {
+ r.push_back(std::string(pos));
+ return 0;
+ }));
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(4u, r.size());
+ uASSERT_EQUAL("b"s, r[0]);
+ uASSERT_EQUAL("a"s, r[1]);
+ uASSERT_EQUAL("foo"s, r[2]);
+ uASSERT_EQUAL("bar"s, r[3]);
+ }
+
+ {
+ const char* argv[] = { "program", "-a", "foo", "-b", "bar" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.set_pos_cb(std::function([&](std::string_view pos)
+ {
+ r.push_back(std::string(pos));
+ return 0;
+ }));
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(4u, r.size());
+ uASSERT_EQUAL("a"s, r[0]);
+ uASSERT_EQUAL("foo"s, r[1]);
+ uASSERT_EQUAL("b"s, r[2]);
+ uASSERT_EQUAL("bar"s, r[3]);
+ }
+
+ {
+ const char* argv[] = { "program", "foo", "-a", "-b", "bar" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.set_pos_cb(std::function([&](std::string_view pos)
+ {
+ r.push_back(std::string(pos));
+ return 0;
+ }));
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(4u, r.size());
+ uASSERT_EQUAL("foo"s, r[0]);
+ uASSERT_EQUAL("a"s, r[1]);
+ uASSERT_EQUAL("b"s, r[2]);
+ uASSERT_EQUAL("bar"s, r[3]);
+ }
+
+ {
+ const char* argv[] = { "program", "foo", "bar", "-a", "-b" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.set_pos_cb(std::function([&](std::string_view pos)
+ {
+ r.push_back(std::string(pos));
+ return 0;
+ }));
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(4u, r.size());
+ uASSERT_EQUAL("foo"s, r[0]);
+ uASSERT_EQUAL("bar"s, r[1]);
+ uASSERT_EQUAL("a"s, r[2]);
+ uASSERT_EQUAL("b"s, r[3]);
+ }
+
+ {
+ const char* argv[] = { "program", "-a", "-b", "--", "-x", "foo", "bar" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.set_pos_cb(std::function([&](std::string_view pos)
+ {
+ r.push_back(std::string(pos));
+ return 0;
+ }));
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(5u, r.size());
+ uASSERT_EQUAL("a"s, r[0]);
+ uASSERT_EQUAL("b"s, r[1]);
+ uASSERT_EQUAL("-x"s, r[2]);
+ uASSERT_EQUAL("foo"s, r[3]);
+ uASSERT_EQUAL("bar"s, r[4]);
+ }
+
+ //
+ // Long options
+ //
+ {
+ const char* argv[] = { "program", "--sort" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add({}, "--sort",
+ std::function([&](){ r.push_back("sort"); return 0;}), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(1u, r.size());
+ uASSERT_EQUAL("sort"s, r[0]);
+ }
+
+ {
+ const char* argv[] = { "program", "--no-sort" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add({}, "--no-sort",
+ std::function([&](){ r.push_back("no-sort"); return 0;}), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(1u, r.size());
+ uASSERT_EQUAL("no-sort"s, r[0]);
+ }
+
+ {
+ const char* argv[] =
+ { "program", "--output", "output.txt", "--block-size", "1024" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::string, int> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add({}, "--output",
+ std::function([&](std::string output)
+ {
+ r.push_back(output);
+ return 0;
+ }), {});
+ args.add({}, "--block-size",
+ std::function([&](int block_size)
+ {
+ r.push_back("["s + std::to_string(block_size) + "]"s);
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("output.txt"s, r[0]);
+ uASSERT_EQUAL("[1024]"s, r[1]);
+ }
+
+ {
+ const char* argv[] =
+ { "program", "--output=output.txt", "--block-size=1024" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::string, int> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add({}, "--output",
+ std::function([&](std::string output)
+ {
+ r.push_back(output);
+ return 0;
+ }), {});
+ args.add({}, "--block-size",
+ std::function([&](int block_size)
+ {
+ r.push_back("["s + std::to_string(block_size) + "]"s);
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("output.txt"s, r[0]);
+ uASSERT_EQUAL("[1024]"s, r[1]);
+ }
+
+ {
+ const char* argv[] =
+ { "program", "--color", "--reverse" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::optional<std::string>> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add({}, "--color",
+ std::function([&](std::optional<std::string> color)
+ {
+ uASSERT(!color.has_value());
+ r.push_back("color");
+ return 0;
+ }), {});
+ args.add({}, "--reverse",
+ std::function([&]()
+ {
+ r.push_back("reverse");
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("color"s, r[0]);
+ uASSERT_EQUAL("reverse"s, r[1]);
+ }
+
+ {
+ const char* argv[] =
+ { "program", "--color=never", "--reverse" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::optional<std::string>> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add({}, "--color",
+ std::function([&](std::optional<std::string> color)
+ {
+ uASSERT(color.has_value());
+ r.push_back(*color);
+ return 0;
+ }), {});
+ args.add({}, "--reverse",
+ std::function([&]()
+ {
+ r.push_back("reverse");
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("never"s, r[0]);
+ uASSERT_EQUAL("reverse"s, r[1]);
+ }
+
+ }
+};
+
+// Registers the fixture into the 'registry'
+static ArgParserTest test;
diff --git a/test/argsplit_test.cc b/test/argsplit_test.cc
new file mode 100644
index 0000000..7dce561
--- /dev/null
+++ b/test/argsplit_test.cc
@@ -0,0 +1,203 @@
+#include <uunit.h>
+
+#include <util.h>
+
+class ArgSplitTest
+ : public uUnit
+{
+public:
+ using fs = std::filesystem::path;
+
+ ArgSplitTest()
+ {
+ uTEST(ArgSplitTest::plain_test);
+ uTEST(ArgSplitTest::quot_test);
+ uTEST(ArgSplitTest::apotrophe_test);
+ uTEST(ArgSplitTest::mixed_test);
+ uTEST(ArgSplitTest::escape_test);
+ }
+
+ void plain_test()
+ {
+ using namespace std::string_literals;
+
+ { // zero
+ auto res = argsplit({});
+ uASSERT_EQUAL(0u, res.size());
+ }
+
+ { // one
+ auto res = argsplit("hello"s);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("hello"s, res[0]);
+ }
+
+ { // many
+ auto res = argsplit("hello world"s);
+ uASSERT_EQUAL(2u, res.size());
+ uASSERT_EQUAL("hello"s, res[0]);
+ uASSERT_EQUAL("world"s, res[1]);
+ }
+ }
+
+ void quot_test()
+ {
+ using namespace std::string_literals;
+
+ { // zero
+ auto res = argsplit("\"\"");
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("\"\""s, res[0]);
+ }
+
+ { // one
+ auto res = argsplit("\"hello\""s);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("\"hello\""s, res[0]);
+ }
+
+ { // one with space
+ auto res = argsplit("\"hel lo\""s);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("\"hel lo\""s, res[0]);
+ }
+
+ { // many
+ auto res = argsplit("\"hello\" \"world\""s);
+ uASSERT_EQUAL(2u, res.size());
+ uASSERT_EQUAL("\"hello\""s, res[0]);
+ uASSERT_EQUAL("\"world\""s, res[1]);
+ }
+
+ { // many with spaces
+ auto res = argsplit("\"hel lo\" \"wor ld\""s);
+ uASSERT_EQUAL(2u, res.size());
+ uASSERT_EQUAL("\"hel lo\""s, res[0]);
+ uASSERT_EQUAL("\"wor ld\""s, res[1]);
+ }
+ }
+
+ void apotrophe_test()
+ {
+ using namespace std::string_literals;
+
+ { // zero
+ auto res = argsplit("\'\'");
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("\'\'"s, res[0]);
+ }
+
+ { // one
+ auto res = argsplit("\'hello\'"s);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("\'hello\'"s, res[0]);
+ }
+
+ { // one with space
+ auto res = argsplit("\'hel lo\'"s);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("\'hel lo\'"s, res[0]);
+ }
+
+ { // many
+ auto res = argsplit("\'hello\' \'world\'"s);
+ uASSERT_EQUAL(2u, res.size());
+ uASSERT_EQUAL("\'hello\'"s, res[0]);
+ uASSERT_EQUAL("\'world\'"s, res[1]);
+ }
+
+ { // many with spaces
+ auto res = argsplit("\'hel lo\' \'wor ld\'"s);
+ uASSERT_EQUAL(2u, res.size());
+ uASSERT_EQUAL("\'hel lo\'"s, res[0]);
+ uASSERT_EQUAL("\'wor ld\'"s, res[1]);
+ }
+ }
+
+ void mixed_test()
+ {
+ using namespace std::string_literals;
+
+ { // zero
+ auto res = argsplit("\'\'");
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("\'\'"s, res[0]);
+ }
+
+ { // one
+ auto res = argsplit("\'he\"llo\'"s);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("\'he\"llo\'"s, res[0]);
+ }
+
+ { // one
+ auto res = argsplit("\"he\'llo\""s);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("\"he\'llo\""s, res[0]);
+ }
+
+ { // one with space
+ auto res = argsplit("\'he\"l lo\'\""s);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("\'he\"l lo\'\""s, res[0]);
+ }
+
+ { // one with space
+ auto res = argsplit("\"he\'l lo\"\'"s);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("\"he\'l lo\"\'"s, res[0]);
+ }
+
+ { // many
+ auto res = argsplit("\"he\'llo\" \"wor\'ld\""s);
+ uASSERT_EQUAL(2u, res.size());
+ uASSERT_EQUAL("\"he\'llo\""s, res[0]);
+ uASSERT_EQUAL("\"wor\'ld\""s, res[1]);
+ }
+
+ { // many with spaces
+ auto res = argsplit("\'hel\"lo\' \'wor\"ld\'"s);
+ uASSERT_EQUAL(2u, res.size());
+ uASSERT_EQUAL("\'hel\"lo\'"s, res[0]);
+ uASSERT_EQUAL("\'wor\"ld\'"s, res[1]);
+ }
+ }
+
+ void escape_test()
+ {
+ using namespace std::string_literals;
+
+ { // zero
+ auto res = argsplit("\\");
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("\\"s, res[0]);
+ }
+
+ { // one
+ auto res = argsplit("\\\'"s);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("\\\'"s, res[0]);
+ }
+
+ { // one
+ auto res = argsplit("\\\""s);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("\\\""s, res[0]);
+ }
+
+ { // one
+ auto res = argsplit("\\\\"s);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("\\\\"s, res[0]);
+ }
+
+ { // one
+ auto res = argsplit("a\\ b"s);
+ uASSERT_EQUAL(1u, res.size());
+ uASSERT_EQUAL("a\\ b"s, res[0]);
+ }
+ }
+};
+
+// Registers the fixture into the 'registry'
+static ArgSplitTest test;
diff --git a/test/ctor.cc b/test/ctor.cc
index ccf1c31..0d77a3e 100644
--- a/test/ctor.cc
+++ b/test/ctor.cc
@@ -12,6 +12,75 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
{
.type = ctor::target_type::unit_test,
.system = ctor::output_system::build,
+ .target = "argparser_test",
+ .sources = {
+ "argparser_test.cc",
+ "testmain.cc",
+ },
+ .flags = {
+ .cxxflags = {
+ "-std=c++20", "-O3", "-Wall", "-Werror",
+ "-I../src", "-Iuunit",
+ "-DOUTPUT=\"argparser\"",
+ },
+ },
+ },
+ {
+ .type = ctor::target_type::unit_test,
+ .system = ctor::output_system::build,
+ .target = "argsplit_test",
+ .sources = {
+ "argsplit_test.cc",
+ "testmain.cc",
+ "../src/util.cc",
+ },
+ .flags = {
+ .cxxflags = {
+ "-std=c++20", "-O3", "-Wall", "-Werror",
+ "-I../src", "-Iuunit",
+ "-DOUTPUT=\"argsplit\"",
+ },
+ },
+ },
+ {
+ .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",
@@ -30,6 +99,7 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
"execute_test.cc",
"testmain.cc",
"../src/execute.cc",
+ "../src/pointerlist.cc",
"../src/util.cc",
},
.depends = { "testprog", },
@@ -63,6 +133,24 @@ 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",
@@ -104,7 +192,9 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
.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 d5d40c9..11b067f 100644
--- a/test/execute_test.cc
+++ b/test/execute_test.cc
@@ -23,26 +23,37 @@ public:
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());
- uASSERT_EQUAL(0, execute(cmd, {"retval", "0"}, {}, false));
- uASSERT_EQUAL(1, execute(cmd, {"retval", "1"}, {}, false));
- uASSERT_EQUAL(1, execute("no-such-binary", {}, {}, false));
+ 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 env()
{
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;
+ TmpFile tmp;
std::map<std::string, std::string> env;
@@ -53,7 +64,8 @@ public:
// Overwrite the exiting LANG var
env["LANG"] = "foo";
- uASSERT_EQUAL(0, execute(cmd, {"envdump", tmp.get()}, env, false));
+ auto value = execute(s, cmd, {"envdump", tmp.get()}, env, false);
+ uASSERT_EQUAL(0, value);
std::vector<std::string> vars;
{
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/suite/test.sh b/test/suite/test.sh
index c112351..c54137a 100755
--- a/test/suite/test.sh
+++ b/test/suite/test.sh
@@ -17,6 +17,12 @@ function ctor
./ctor $*
}
+STAT_FORMAT="-c %Y"
+if [[ "$OSTYPE" == "darwin"* ]]; then
+ # Mac OSX
+ STAT_FORMAT="-f %B"
+fi
+
# Wipe the board
rm -Rf ${BUILDDIR}
rm -f configuration.cc
@@ -26,7 +32,7 @@ echo "** ctor_files/ctor.cc.base"
cp ctor_files/ctor.cc.base ctor.cc
# Compile bootstrap binary
-$CXX -pthread -std=c++20 -L${CTORDIR} -lctor -I../../src ctor.cc -o ctor || fail ${LINENO}
+$CXX -pthread $LDFLAGS $CXXFLAGS -std=c++20 -L${CTORDIR} -lctor -I../../src ctor.cc -o ctor || fail ${LINENO}
# No build files should have been created yet
[ -d ${BUILDDIR} ] && fail ${LINENO}
@@ -55,7 +61,7 @@ ctor -v
# ctor should not have been rebuilt, so md5 sum should be the same
(echo $MD5 | md5sum --status -c) || fail ${LINENO}
-MOD1=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o`
+MOD1=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
touch hello.cc
sleep 1.1
@@ -63,7 +69,7 @@ sleep 1.1
ctor -v
# Object file should have been recompiled
-MOD2=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o`
+MOD2=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
[[ $MOD1 == $MOD2 ]] && fail ${LINENO}
# Replacve -DFOO with -DBAR in foo external.cxxflags
@@ -72,13 +78,13 @@ cp ctor_files/ctor.cc.bar ctor.cc
MD5C=`md5sum configuration.cc`
MD5=`md5sum ctor`
-MOD1=`stat -c %Y build/hello-hello_cc.o`
+MOD1=`stat $STAT_FORMAT build/hello-hello_cc.o`
sleep 1.1
# Run normally to reconfigure, rebuild ctor and rebuild hello.cc
ctor -v
-MOD2=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o`
+MOD2=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
[[ $MOD1 == $MOD2 ]] && fail ${LINENO}
(echo $MD5C | md5sum --status -c) && fail ${LINENO}
(echo $MD5 | md5sum --status -c) && fail ${LINENO}
@@ -88,13 +94,13 @@ cp ctor_files/ctor.cc.multi ctor.cc
MD5C=`md5sum configuration.cc`
MD5=`md5sum ctor`
-MOD1=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o`
+MOD1=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
sleep 1.1
# Run normally to reconfigure, rebuild ctor and rebuild hello.cc
ctor -v
-MOD2=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o`
+MOD2=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
[[ $MOD1 == $MOD2 ]] && fail ${LINENO}
(echo $MD5C | md5sum --status -c) && fail ${LINENO}
(echo $MD5 | md5sum --status -c) && fail ${LINENO}
@@ -102,13 +108,13 @@ MOD2=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o`
# now touching foobar.h, should retrigger re-configuration
touch foobar.h
-MOD1=`stat -c %Y ctor`
+MOD1=`stat $STAT_FORMAT ctor`
sleep 1.1
# Run normally to reconfigure, rebuild ctor and rebuild hello.cc
ctor -v
-MOD2=`stat -c %Y ctor`
+MOD2=`stat $STAT_FORMAT ctor`
[[ $MOD1 == $MOD2 ]] && fail ${LINENO}
exit 0
diff --git a/test/tasks_test.cc b/test/tasks_test.cc
index 3de6982..cbd0864 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::vector<std::shared_ptr<Task>>& tasks,
- const std::string& name)
+ 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,26 +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 doesn't matter
- 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));
+ 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"});
@@ -149,11 +170,12 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(nullptr, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(nullptr, getNextTask({}, allTasks, dirtyTasks));
}
{ // 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::vector<std::shared_ptr<Task>> allTasks;
allTasks.push_back(task1);
@@ -165,11 +187,12 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(nullptr, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(nullptr, getNextTask({}, allTasks, dirtyTasks));
}
{ // 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::vector<std::shared_ptr<Task>> allTasks;
allTasks.push_back(task1);
@@ -182,13 +205,14 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(task1, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(task1, getNextTask({}, allTasks, dirtyTasks));
uASSERT_EQUAL(0u, dirtyTasks.size());
}
{ // 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::vector<std::shared_ptr<Task>> allTasks;
allTasks.push_back(task1);
@@ -202,15 +226,17 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(task2, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(task2, getNextTask({}, allTasks, dirtyTasks));
uASSERT_EQUAL(0u, dirtyTasks.size());
}
{ // 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::vector<std::shared_ptr<Task>> allTasks;
allTasks.push_back(task1);
@@ -224,15 +250,17 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(task2, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(task2, getNextTask({}, allTasks, dirtyTasks));
uASSERT_EQUAL(0u, dirtyTasks.size());
}
{ // 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::vector<std::shared_ptr<Task>> allTasks;
allTasks.push_back(task2);
@@ -247,10 +275,54 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(task1, getNextTask(allTasks, dirtyTasks));
+ 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
index dbfb665..93edc3f 100644
--- a/test/testprog.cc
+++ b/test/testprog.cc
@@ -1,8 +1,9 @@
#include <iostream>
#include <fstream>
#include <string>
+#include <csignal>
-extern const char **environ; // see 'man environ'
+extern char **environ;
int main(int argc, const char* argv[])
{
@@ -40,6 +41,11 @@ int main(int argc, const char* argv[])
abort();
}
+ if(cmd == "segfault")
+ {
+ raise(SIGSEGV);
+ }
+
if(cmd == "throw")
{
throw "ouch";
diff --git a/test/tmpfile.h b/test/tmpfile.h
index 5d114d0..0f83a20 100644
--- a/test/tmpfile.h
+++ b/test/tmpfile.h
@@ -3,39 +3,29 @@
// See accompanying file LICENSE for details.
#pragma once
-#include <cstdlib>
-#include <unistd.h>
+#include <cstdio>
-#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
+class TmpFile
{
- tmp_file(const std::string& data = {})
+public:
+ TmpFile(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);
+ auto tmp_dir = std::filesystem::temp_directory_path();
+ auto tmp_file_template = tmp_dir / "ctor_tmp_file-";
+ std::FILE* fp{nullptr};
+ int counter{};
+ while(!fp)
+ {
+ filename = tmp_file_template.string() + std::to_string(counter++);
+ fp = std::fopen(filename.data(), "wx");
+ }
+ std::fwrite(data.data(), data.size(), 1, fp);
+ std::fclose(fp);
}
- ~tmp_file()
+ ~TmpFile()
{
- unlink(filename.data());
+ std::filesystem::remove(filename);
}
const std::string& get() const
diff --git a/test/tools_test.cc b/test/tools_test.cc
index a428ea1..5ae04c3 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
@@ -526,10 +520,6 @@ public:
act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::output, "foo");
uASSERT_EQUAL(exp, act);
- exp = { "-s" };
- act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::strip);
- uASSERT_EQUAL(exp, act);
-
exp = { "-Wall" };
act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::warn_all);
uASSERT_EQUAL(exp, act);
@@ -577,10 +567,6 @@ public:
act = ld_option(ctor::toolchain::clang, ctor::ld_opt::output, "foo");
uASSERT_EQUAL(exp, act);
- exp = { "-s" };
- act = ld_option(ctor::toolchain::clang, ctor::ld_opt::strip);
- uASSERT_EQUAL(exp, act);
-
exp = { "-Wall" };
act = ld_option(ctor::toolchain::clang, ctor::ld_opt::warn_all);
uASSERT_EQUAL(exp, act);
@@ -628,10 +614,6 @@ public:
act = ld_option(ctor::toolchain::any, ctor::ld_opt::output, "foo");
uASSERT_EQUAL(exp, act);
- exp = { "{ctor::ld_opt::strip}" };
- act = ld_option(ctor::toolchain::any, ctor::ld_opt::strip);
- uASSERT_EQUAL(exp, act);
-
exp = { "{ctor::ld_opt::warn_all}" };
act = ld_option(ctor::toolchain::any, ctor::ld_opt::warn_all);
uASSERT_EQUAL(exp, act);
@@ -914,7 +896,7 @@ public:
// Mismatching toolchain (required vs actual) results in no output
// otherwise to_strings is just a proxy for ld_option
- act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::ld_opt::strip});
+ act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::ld_opt::threads});
uASSERT_EQUAL(exp, act);
}
diff --git a/test/uunit b/test/uunit
-Subproject bc078da645412c6b36ef59e635d6c35d11088c9
+Subproject 0f371777e02dd068f9675a05a29230221d5d6a7