summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/argparser_test.cc1019
-rw-r--r--test/ctor.cc337
-rw-r--r--test/cycle_test.cc14
-rw-r--r--test/execute_test.cc21
-rw-r--r--test/generated_sources_test.cc135
-rw-r--r--test/source_type_test.cc11
-rw-r--r--test/suite/ctor_files/ctor.cc.bar37
-rw-r--r--test/suite/ctor_files/ctor.cc.base37
-rw-r--r--test/suite/ctor_files/ctor.cc.generated104
-rw-r--r--test/suite/ctor_files/ctor.cc.generated287
-rw-r--r--test/suite/ctor_files/ctor.cc.multi37
-rw-r--r--test/suite/test.cc370
-rwxr-xr-xtest/suite/test.sh120
-rw-r--r--test/tasks_test.cc30
-rw-r--r--test/tmpfile.h44
-rw-r--r--test/tools_test.cc23
16 files changed, 2011 insertions, 415 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/ctor.cc b/test/ctor.cc
index b7bcc6d..d1c0188 100644
--- a/test/ctor.cc
+++ b/test/ctor.cc
@@ -8,198 +8,221 @@ namespace
ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
{
return
- {
{
- .type = ctor::target_type::unit_test,
- .system = ctor::output_system::build,
- .target = "argsplit_test",
- .sources = {
- "argsplit_test.cc",
- "testmain.cc",
- "../src/util.cc",
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("argparser_test"),
+ ctor::sources{
+ "argparser_test.cc",
+ "testmain.cc",
+ },
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
+ "-I../src", "-Iuunit",
+ "-DOUTPUT=\"argparser\"",
+ "-fexceptions",
+ },
},
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("generated_sources_test"),
+ ctor::sources{
+ "generated_sources_test.cc",
+ "testmain.cc",
+ "../configuration.cc",
+ },
+ ctor::depends({"libctor_nomain.a"}),
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
- "-DOUTPUT=\"argsplit\"",
+ "-DOUTPUT=\"generated_sources\"",
+ "-fexceptions",
},
},
- },
- {
- .type = ctor::target_type::unit_test,
- .system = ctor::output_system::build,
- .target = "pointerlist_test",
- .sources = {
- "pointerlist_test.cc",
- "testmain.cc",
- "../src/pointerlist.cc",
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("argsplit_test"),
+ ctor::sources{
+ "argsplit_test.cc",
+ "testmain.cc",
+ "../src/util.cc",
+ },
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
+ "-I../src", "-Iuunit",
+ "-DOUTPUT=\"argsplit\"",
+ "-fexceptions",
+ },
},
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("pointerlist_test"),
+ ctor::sources{
+ "pointerlist_test.cc",
+ "testmain.cc",
+ "../src/pointerlist.cc",
+ },
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
"-DOUTPUT=\"pointerlist\"",
+ "-fexceptions",
},
},
- },
- {
- .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",
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("deps_test"),
+ ctor::sources{
+ "deps_test.cc",
+ "testmain.cc",
+ "../src/deps.cc",
+ "../src/util.cc",
+ },
+ ctor::depends({"testprog"}),
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
"-DOUTPUT=\"deps\"",
+ "-fexceptions",
},
},
- },
- {
- .type = ctor::target_type::unit_test,
- .system = ctor::output_system::build,
- .target = "testprog",
- .sources = {
- "testprog.cc",
- },
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("testprog"),
+ ctor::sources{
+ "testprog.cc",
+ },
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
+ "-fexceptions",
},
},
- },
- {
- .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",
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("execute_test"),
+ ctor::sources{
+ "execute_test.cc",
+ "testmain.cc",
+ "../src/execute.cc",
+ "../src/pointerlist.cc",
+ "../src/util.cc",
+ },
+ ctor::depends({"testprog"}),
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
"-DOUTPUT=\"execute\"",
+ "-fexceptions",
},
- .ldflags = { "-pthread" },
- },
- },
- {
- .type = ctor::target_type::unit_test,
- .system = ctor::output_system::build,
- .target = "tasks_test",
- .sources = {
- "tasks_test.cc",
- "testmain.cc",
- },
- .depends = { "libctor_nomain.a" },
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ ctor::ld_flags{ "-pthread" },
+ },
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("tasks_test"),
+ ctor::sources{
+ "tasks_test.cc",
+ "testmain.cc",
+ },
+ ctor::depends({"libctor_nomain.a"}),
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
"-DOUTPUT=\"tasks\"",
+ "-fexceptions",
},
- .ldflags = { "-pthread" },
- },
- },
- {
- .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",
+ ctor::ld_flags{ "-pthread" },
+ },
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("cycle_test"),
+ ctor::sources{
+ "cycle_test.cc",
+ "testmain.cc",
+ "../configuration.cc",
+ },
+ ctor::depends({"libctor_nomain.a"}),
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
"-DOUTPUT=\"cycle\"",
+ "-fexceptions",
},
- .ldflags = { "-pthread" },
- },
- },
- {
- .type = ctor::target_type::unit_test,
- .system = ctor::output_system::build,
- .target = "source_type_test",
- .sources = {
- "source_type_test.cc",
- "testmain.cc",
- },
- .depends = { "libctor_nomain.a" },
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ ctor::ld_flags{ "-pthread" },
+ },
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("source_type_test"),
+ ctor::sources{
+ "source_type_test.cc",
+ "testmain.cc",
+ "../configuration.cc",
+ },
+ ctor::depends({"libctor_nomain.a"}),
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
"-DOUTPUT=\"source_type\"",
+ "-fexceptions",
},
- .ldflags = { "-pthread" },
- },
- },
- {
- .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" },
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ ctor::ld_flags{ "-pthread" },
+ },
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("tools_test"),
+ ctor::sources{
+ "tools_test.cc",
+ "testmain.cc",
+ "../configuration.cc",
+ },
+ ctor::depends({"libctor_nomain.a"}),
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
"-DOUTPUT=\"tools\"",
+ "-fexceptions",
},
},
- },
- {
- .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",
- "../src/task_ar.cc",
- "../src/task_cc.cc",
- "../src/task_fn.cc",
- "../src/task_ld.cc",
- "../src/task_so.cc",
- "../src/tools.cc",
- "../src/util.cc",
- "../src/externals_manual.cc",
- },
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ {
+ ctor::target_type::unit_test_library,
+ ctor::output_system::build,
+ ctor::target("libctor_nomain.a"),
+ ctor::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",
+ "../src/task_ar.cc",
+ "../src/task_cc.cc",
+ "../src/task_fn.cc",
+ "../src/task_ld.cc",
+ "../src/task_so.cc",
+ "../src/tools.cc",
+ "../src/util.cc",
+ "../src/externals_manual.cc",
+ },
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src",
+ "-fexceptions",
},
- .ldflags = { "-pthread" },
+ ctor::ld_flags{ "-pthread" },
},
- },
- };
+ };
}
}
diff --git a/test/cycle_test.cc b/test/cycle_test.cc
index 3b45632..8f4c296 100644
--- a/test/cycle_test.cc
+++ b/test/cycle_test.cc
@@ -11,23 +11,23 @@ ctor::build_configurations ctorTestConfigsCyclic(const ctor::settings&)
{
// No dependency
{
- .target = "target0",
+ ctor::target("target0"),
},
// Direct (self-depends)
{
- .target = "target1",
- .depends = { "target1" },
+ ctor::target("target1"),
+ ctor::depends({ "target1" }),
},
// Indirect cyclic depends
{
- .target = "target2",
- .depends = { "target3" },
+ ctor::target("target2"),
+ ctor::depends({ "target3" }),
},
{
- .target = "target3",
- .depends = { "target2" },
+ ctor::target("target3"),
+ ctor::depends({ "target2" }),
},
};
}
diff --git a/test/execute_test.cc b/test/execute_test.cc
index 722b6ea..f8f902f 100644
--- a/test/execute_test.cc
+++ b/test/execute_test.cc
@@ -23,6 +23,8 @@ public:
void return_value()
{
+ constexpr int segfault_return_value = 11;
+ constexpr int exception_return_value = 6;
ctor::settings s;
auto cur_path = std::filesystem::path(paths::argv_0).parent_path();
std::vector<std::string> paths{{cur_path.string()}};
@@ -31,16 +33,21 @@ public:
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);
+ uASSERT_EQUAL(segfault_return_value, value);
+
value = execute(s, cmd, {"throw"}, {}, false);
- uASSERT_EQUAL(6, value);
+ uASSERT_EQUAL(exception_return_value, value);
+
value = execute(s, cmd, {"abort"}, {}, false);
- uASSERT_EQUAL(6, value);
+ uASSERT_EQUAL(exception_return_value, value);
}
void env()
@@ -53,7 +60,7 @@ public:
auto cmd = locate("testprog", paths);
uASSERT(!cmd.empty());
- tmp_file tmp;
+ TmpFile tmp;
std::map<std::string, std::string> env;
@@ -83,11 +90,13 @@ public:
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)
+ // 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)
+ // Check that other vars are also there (ie. the env wasn't cleared on
+ // entry)
uASSERT(vars.size() > 3);
}
};
diff --git a/test/generated_sources_test.cc b/test/generated_sources_test.cc
new file mode 100644
index 0000000..29a48fc
--- /dev/null
+++ b/test/generated_sources_test.cc
@@ -0,0 +1,135 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include <uunit.h>
+
+#include <ctor.h>
+#include <tasks.h>
+#include <rebuild.h>
+
+class GeneratedSourcesTest
+ : public uUnit
+{
+public:
+ GeneratedSourcesTest()
+ {
+ uTEST(GeneratedSourcesTest::test_custom_output);
+ uTEST(GeneratedSourcesTest::test_many_to_one_output);
+ }
+
+ void setup()
+ {
+ // Make sure we start from a clean slate
+ getConfigFileList().clear();
+ }
+
+ void test_custom_output()
+ {
+ using namespace std::string_literals;
+
+ ctor::reg(
+ [](const ctor::settings&)
+ {
+ return ctor::build_configurations{
+ {
+ ctor::target("test1"),
+ ctor::sources{
+ {"foo.cc", ctor::source_type::generated}
+ },
+ },
+ {
+ ctor::target("this_is_unused"),
+ ctor::sources{
+ {"bar.x", ctor::output_file{"foo.cc"}},
+ {"bar.y", ctor::output_file{"bar.cc"}},
+ },
+ []([[maybe_unused]]const std::string& input,
+ [[maybe_unused]]const std::string& output,
+ [[maybe_unused]]const ctor::build_configuration& config,
+ [[maybe_unused]]const ctor::settings& settings)
+ {
+ return 0;
+ },
+ },
+ };
+ });
+ ctor::settings settings{};
+ auto tasks = getTasks(settings);
+ for(auto task : tasks)
+ {
+ uASSERT(task->registerDepTasks(tasks) == 0);
+ }
+ uASSERT_EQUAL(4u, tasks.size());
+ bool found{false};
+ for(const auto& task : tasks)
+ {
+ if(task->target() == "test1")
+ {
+ auto deps_test1 = task->getDependsTasks();
+ uASSERT_EQUAL(1u, deps_test1.size());
+ auto deps_foo_o = deps_test1[0]->getDependsTasks();
+ uASSERT_EQUAL(1u, deps_foo_o.size());
+ uASSERT_EQUAL("test/bar.x"s, deps_foo_o[0]->source());
+ found = true;
+ }
+ }
+ uASSERT(found);
+ }
+
+ void test_many_to_one_output()
+ {
+ using namespace std::string_literals;
+
+ ctor::reg(
+ [](const ctor::settings&)
+ {
+ return ctor::build_configurations{
+ {
+ ctor::target("test1"),
+ ctor::sources{
+ {"foo.cc", ctor::source_type::generated}
+ },
+ },
+ {
+ ctor::target("foo.cc"),
+ ctor::sources{
+ "bar.x",
+ "bar.y",
+ },
+ []([[maybe_unused]]const std::vector<std::string>& input,
+ [[maybe_unused]]const std::string& output,
+ [[maybe_unused]]const ctor::build_configuration& config,
+ [[maybe_unused]]const ctor::settings& settings)
+ {
+ return 0;
+ },
+ },
+ };
+ });
+ ctor::settings settings{};
+ auto tasks = getTasks(settings);
+ for(auto task : tasks)
+ {
+ uASSERT(task->registerDepTasks(tasks) == 0);
+ }
+ uASSERT_EQUAL(4u, tasks.size());
+ bool found{false};
+ for(const auto& task : tasks)
+ {
+ if(task->target() == "test1")
+ {
+ auto deps_test1 = task->getDependsTasks();
+ uASSERT_EQUAL(1u, deps_test1.size());
+ auto deps_foo_o = deps_test1[0]->getDependsTasks();
+ uASSERT_EQUAL(1u, deps_foo_o.size());
+ uASSERT_EQUAL("test/bar.x"s, deps_foo_o[0]->source());
+ found = true;
+ }
+ }
+ uASSERT(found);
+ }
+
+};
+
+// Registers the fixture into the 'registry'
+static GeneratedSourcesTest test;
diff --git a/test/source_type_test.cc b/test/source_type_test.cc
index 288f1e5..345c591 100644
--- a/test/source_type_test.cc
+++ b/test/source_type_test.cc
@@ -26,12 +26,21 @@ std::ostream& operator<<(std::ostream& stream, const ctor::language& lang)
return stream;
}
+const ctor::configuration& test_configuration()
+{
+ static ctor::configuration cfg{};
+ cfg.build_toolchain = ctor::toolchain::gcc;
+ cfg.build_arch = ctor::arch::unix;
+ return cfg;
+}
+REG(test_configuration);
+
class TestableTaskCC
: public TaskCC
{
public:
TestableTaskCC(const ctor::source& source)
- : TaskCC({}, {}, "build", source)
+ : TaskCC(ctor::target_type::object, {}, {}, "build", source)
{}
ctor::language language() const
diff --git a/test/suite/ctor_files/ctor.cc.bar b/test/suite/ctor_files/ctor.cc.bar
index 218f9cc..d77eb70 100644
--- a/test/suite/ctor_files/ctor.cc.bar
+++ b/test/suite/ctor_files/ctor.cc.bar
@@ -11,21 +11,20 @@ ctor::build_configurations ctorConfigs(const ctor::settings& settings)
return
{
{
- .name = "hello",
- .target = "hello",
- .sources = {
+ ctor::name("hello"),
+ ctor::target("hello"),
+ ctor::sources{
"hello.cc",
},
- .flags = {
- .cxxflags = {
- "-std=c++20",
- "-O3",
- "-g",
- "-Wall",
- "-Werror",
- },
+ ctor::cxx_flags{
+ "-std=c++20",
+ "-O3",
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-fexceptions",
},
- .externals = {"bar"},
+ ctor::externals({"bar"}),
}
};
}
@@ -35,14 +34,12 @@ ctor::external_configurations ctorExtConfigs(const ctor::settings& settings)
return
{
{
- .name = "bar",
- .external = ctor::external_manual{
- .flags = {
- .cflags = { "-D_B_" },
- .cxxflags = { "-D_A_", "-DBAR"},
- .ldflags = { "-D_C_" },
- .asmflags = { "-D_D_" },
- },
+ ctor::name("bar"),
+ ctor::external_manual{
+ ctor::c_flags{ "-D_B_" },
+ ctor::cxx_flags{ "-D_A_", "-DBAR"},
+ ctor::ld_flags{ "-D_C_" },
+ ctor::asm_flags{ "-D_D_" },
},
// Creates --with-foo-prefix arg to configure which will be used for
// -L and -I flags.
diff --git a/test/suite/ctor_files/ctor.cc.base b/test/suite/ctor_files/ctor.cc.base
index eab39c4..73b5cdb 100644
--- a/test/suite/ctor_files/ctor.cc.base
+++ b/test/suite/ctor_files/ctor.cc.base
@@ -11,21 +11,20 @@ ctor::build_configurations ctorConfigs(const ctor::settings& settings)
return
{
{
- .name = "hello",
- .target = "hello",
- .sources = {
+ ctor::name("hello"),
+ ctor::target("hello"),
+ ctor::sources{
"hello.cc",
},
- .flags = {
- .cxxflags = {
- "-std=c++20",
- "-O3",
- "-g",
- "-Wall",
- "-Werror",
- },
+ ctor::cxx_flags{
+ "-std=c++20",
+ "-O3",
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-fexceptions",
},
- .externals = {"bar"},
+ ctor::externals({"bar"}),
}
};
}
@@ -35,15 +34,13 @@ ctor::external_configurations ctorExtConfigs(const ctor::settings& settings)
return
{
{
- .name = "bar",
- .external = ctor::external_manual
+ ctor::name("bar"),
+ ctor::external_manual
{
- .flags = {
- .cflags = { "-D_B_" },
- .cxxflags = { "-D_A_", "-DFOO"},
- .ldflags = { "-D_C_" },
- .asmflags = { "-D_D_" },
- },
+ ctor::c_flags{ "-D_B_" },
+ ctor::cxx_flags{ "-D_A_", "-DFOO"},
+ ctor::ld_flags{ "-D_C_" },
+ ctor::asm_flags{ "-D_D_" },
},
// Creates --with-foo-prefix arg to configure which will be used for
// -L and -I flags.
diff --git a/test/suite/ctor_files/ctor.cc.generated b/test/suite/ctor_files/ctor.cc.generated
new file mode 100644
index 0000000..96b6fb5
--- /dev/null
+++ b/test/suite/ctor_files/ctor.cc.generated
@@ -0,0 +1,104 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include <ctor.h>
+#include <filesystem>
+#include <iostream>
+#include <fstream>
+
+namespace
+{
+ctor::build_configurations ctorConfigs(const ctor::settings& settings)
+{
+ return
+ {
+ {
+ ctor::target("world"),
+ ctor::sources{
+ { "world.cc", ctor::source_type::generated },
+ },
+ ctor::cxx_flags{
+ "-std=c++20",
+ "-O3",
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-fexceptions",
+ },
+ },
+ {
+ ctor::target("foo"),
+ ctor::sources{
+ { "foo.cc", ctor::source_type::generated },
+ },
+ ctor::cxx_flags{
+ "-std=c++20",
+ "-O3",
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-fexceptions",
+ },
+ },
+ {
+ ctor::target("this_is_unused"),
+ ctor::sources{
+ {"hello.cc", ctor::output_file{"world.cc"}},
+ {"hello.cc", ctor::output_file{"foo.cc"}},
+ },
+ [](const std::string& input,
+ const std::string& output,
+ const ctor::build_configuration& config,
+ const ctor::settings& settings)
+ {
+ namespace fs = std::filesystem;
+ std::cout << "Input: " << input << '\n';
+ std::cout << "Output: " << output << '\n';
+ fs::copy_file(input, output, fs::copy_options::overwrite_existing);
+ return 0;
+ },
+ },
+
+ {
+ ctor::target("many_to_one"),
+ ctor::sources{
+ {"many_to_one.cc", ctor::source_type::generated}
+ },
+ ctor::cxx_flags{"-fexceptions"},
+ },
+ {
+ ctor::target("many_to_one.cc"),
+ ctor::sources{
+ {"foo.cc", ctor::source_type::generated},
+ {"hello.cc"},
+ },
+ [](const std::vector<std::string>& input,
+ const std::string& output,
+ const ctor::build_configuration& config,
+ const ctor::settings& settings)
+ {
+ std::cout << "Output: " << output << '\n';
+ std::ofstream ofs(output);
+ bool comment{true};
+ for(const auto& input_file : input)
+ {
+ std::cout << "Input: " << input_file << '\n';
+ std::ifstream ifs(input_file);
+ std::string line;
+ while(std::getline(ifs, line))
+ {
+ ofs << line << '\n';
+ }
+ if(comment) ofs << "/*\n";
+ comment = false;
+ }
+ ofs << "*/\n";
+ return 0;
+ }
+ },
+
+ };
+}
+}
+
+REG(ctorConfigs);
diff --git a/test/suite/ctor_files/ctor.cc.generated2 b/test/suite/ctor_files/ctor.cc.generated2
new file mode 100644
index 0000000..2e36c0e
--- /dev/null
+++ b/test/suite/ctor_files/ctor.cc.generated2
@@ -0,0 +1,87 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include <ctor.h>
+#include <filesystem>
+#include <iostream>
+#include <fstream>
+
+namespace
+{
+ctor::build_configurations ctorConfigs(const ctor::settings& settings)
+{
+ return
+ {
+ {
+ ctor::target("world"),
+ ctor::sources{
+ { "world.cc", ctor::source_type::generated },
+ },
+ },
+ {
+ ctor::target("foo"),
+ ctor::sources{
+ { "foo.cc", ctor::source_type::generated },
+ },
+ },
+ {
+ ctor::target("this_is_unused"),
+ ctor::sources{
+ {"hello.cc", ctor::output_file{"world.cc"}},
+ {"hello.cc", ctor::output_file{"foo.cc"}},
+ },
+ [](const std::string& input,
+ const std::string& output,
+ const ctor::build_configuration& config,
+ const ctor::settings& settings)
+ {
+ namespace fs = std::filesystem;
+ std::cout << "Input: " << input << '\n';
+ std::cout << "Output: " << output << '\n';
+ fs::copy_file(input, output, fs::copy_options::overwrite_existing);
+ return 0;
+ }
+ },
+
+ {
+ ctor::target("many_to_one"),
+ ctor::sources{
+ {"many_to_one.cc", ctor::source_type::generated}
+ },
+ ctor::cxx_flags{ "-fexceptions", },
+ },
+ {
+ ctor::target("many_to_one.cc"),
+ ctor::sources{
+ {"hello.cc"},
+ },
+ [](const std::vector<std::string>& input,
+ const std::string& output,
+ const ctor::build_configuration& config,
+ const ctor::settings& settings)
+ {
+ std::cout << "Output: " << output << '\n';
+ std::ofstream ofs(output);
+ bool comment{true};
+ for(const auto& input_file : input)
+ {
+ std::cout << "Input: " << input_file << '\n';
+ std::ifstream ifs(input_file);
+ std::string line;
+ while(std::getline(ifs, line))
+ {
+ ofs << line << '\n';
+ }
+ if(comment) ofs << "/*\n";
+ comment = false;
+ }
+ ofs << "*/\n";
+ return 0;
+ }
+ },
+
+ };
+}
+}
+
+REG(ctorConfigs);
diff --git a/test/suite/ctor_files/ctor.cc.multi b/test/suite/ctor_files/ctor.cc.multi
index 2b88afe..fc4c7a4 100644
--- a/test/suite/ctor_files/ctor.cc.multi
+++ b/test/suite/ctor_files/ctor.cc.multi
@@ -13,21 +13,20 @@ ctor::build_configurations ctorConfigs(const ctor::settings& settings)
return
{
{
- .name = "hello",
- .target = "hello",
- .sources = {
+ ctor::name("hello"),
+ ctor::target("hello"),
+ ctor::sources{
"hello.cc",
},
- .flags = {
- .cxxflags = {
- "-std=c++20",
- "-O3",
- "-g",
- "-Wall",
- "-Werror",
- },
+ ctor::cxx_flags{
+ "-std=c++20",
+ "-O3",
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-fexceptions",
},
- .externals = {"bar"},
+ ctor::externals({"bar"}),
}
};
}
@@ -37,14 +36,12 @@ ctor::external_configurations ctorExtConfigs(const ctor::settings& settings)
return
{
{
- .name = "bar",
- .external = ctor::external_manual{
- .flags = {
- .cflags = { "-D_B_" },
- .cxxflags = { "-D_A_", "-DFOO"},
- .ldflags = { "-D_C_" },
- .asmflags = { "-D_D_" },
- },
+ ctor::name("bar"),
+ ctor::external_manual{
+ ctor::c_flags{ "-D_B_" },
+ ctor::cxx_flags{ "-D_A_", "-DFOO"},
+ ctor::ld_flags{ "-D_C_" },
+ ctor::asm_flags{ "-D_D_" },
},
// Creates --with-foo-prefix arg to configure which will be used for
// -L and -I flags.
diff --git a/test/suite/test.cc b/test/suite/test.cc
new file mode 100644
index 0000000..b096a8b
--- /dev/null
+++ b/test/suite/test.cc
@@ -0,0 +1,370 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include "../../src/util.cc"
+#include "../../src/pointerlist.cc"
+#include "../../src/execute.cc"
+
+#include <iostream>
+#include <string>
+#include <filesystem>
+#include <thread>
+#include <chrono>
+#include <sstream>
+
+using namespace std::chrono_literals;
+
+int fail(int value = 1,
+ const std::source_location location = std::source_location::current())
+{
+ std::cout << "*** Failure at line " << location.line() << '\n';
+ exit(value);
+}
+
+const std::string ctor_exe{"./ctor"};
+const std::string obj_ext{".o"};
+
+void run_ctor(const std::vector<std::string>& args,
+ const std::source_location location = std::source_location::current())
+{
+ ctor::settings settings{.verbose = 2};
+ auto ret = execute(settings, ctor_exe, args);
+ if(ret != 0)
+ {
+ fail(ret, location);
+ }
+}
+
+void assert_not_exists(const std::string& path,
+ const std::source_location location = std::source_location::current())
+{
+ if(std::filesystem::exists(path))
+ {
+ fail(1, location);
+ }
+}
+
+void assert_exists(const std::string& path,
+ const std::source_location location = std::source_location::current())
+{
+ if(!std::filesystem::exists(path))
+ {
+ fail(1, location);
+ }
+}
+
+void copy_config(std::string cfg)
+{
+ std::cout << "** ctor_files/ctor.cc." + cfg + "\n";
+ std::filesystem::copy("ctor_files/ctor.cc." + cfg, "ctor.cc",
+ std::filesystem::copy_options::overwrite_existing);
+ if(std::filesystem::exists(ctor_exe))
+ {
+ auto ctor_exe_time = std::filesystem::last_write_time(ctor_exe);
+ std::filesystem::last_write_time("ctor.cc", ctor_exe_time + 1s);
+ }
+}
+
+class Tracker
+{
+public:
+ Tracker(const std::string& file_) : file(file_)
+ {
+ capture(); // capture initial value
+ }
+
+ void capture()
+ {
+ changed();
+ }
+
+ bool changed()
+ {
+ auto tmp = readFile(file);
+ auto content_changed = tmp != content;
+ content = tmp;
+ return content_changed;
+ }
+
+private:
+ std::string file;
+ std::string content;
+};
+
+void assert_not_changed(Tracker& tracker,
+ const std::source_location location = std::source_location::current())
+{
+ if(tracker.changed())
+ {
+ fail(1, location);
+ }
+}
+
+void assert_changed(Tracker& tracker,
+ const std::source_location location = std::source_location::current())
+{
+ if(!tracker.changed())
+ {
+ fail(1, location);
+ }
+}
+
+int main()
+{
+ ctor::settings settings{};
+ settings.verbose = 2;
+ auto paths = get_paths();
+
+ std::string CXX = "g++";
+ get_env("CXX", CXX);
+ std::string CTORDIR = "../../build";
+ get_env("CTORDIR", CTORDIR);
+ std::string BUILDDIR = "build";
+ get_env("BUILDDIR", BUILDDIR);
+ std::string CXXFLAGS;
+ get_env("CXXFLAGS", CXXFLAGS);
+ std::string LDFLAGS;
+ get_env("LDFLAGS", LDFLAGS);
+
+ // Wipe the board
+ std::filesystem::remove_all(BUILDDIR);
+ std::filesystem::remove("configuration.cc");
+ std::filesystem::remove("config.h");
+ std::filesystem::remove(ctor_exe);
+
+ //////////////////////////////////////////////////////////////////////////////
+ // bootstrap
+ {
+ auto cxx_prog = locate(CXX, paths);
+
+ std::vector<std::string> args =
+ {"-pthread", "-std=c++20", "-L", CTORDIR, "-lctor", "-I", "../../src",
+ "ctor.cc", "-o", ctor_exe};
+ if(!CXXFLAGS.empty())
+ {
+ auto tokens = argsplit(CXXFLAGS);
+ for(const auto& token : tokens)
+ {
+ args.push_back(token);
+ }
+ }
+ if(!LDFLAGS.empty())
+ {
+ auto tokens = argsplit(LDFLAGS);
+ for(const auto& token : tokens)
+ {
+ args.push_back(token);
+ }
+ }
+
+ // Compile bootstrap binary
+ copy_config("base");
+ auto ret = execute(settings, cxx_prog, args);
+ if(ret != 0)
+ {
+ fail(ret);
+ }
+ }
+ //////////////////////////////////////////////////////////////////////////////
+ // check if source file changes are tracked
+ {
+ // No build files should have been created yet
+ assert_not_exists(BUILDDIR);
+
+ // capture ctor binary before configure is called
+ Tracker ctor_bin(ctor_exe);
+ run_ctor(
+ {"configure", "--ctor-includedir", "../../src",
+ "--ctor-libdir=" + CTORDIR, "--build-dir=" + BUILDDIR});
+
+ // ctor should be rebuilt at this point, so binary should have changed
+ assert_changed(ctor_bin);
+
+ // configuration.cc should have been generated now
+ assert_exists("configuration.cc");
+ assert_exists("config.h");
+
+ // Shouldn't compile anything yet - only configure
+ assert_not_exists(BUILDDIR + "/hello-hello_cc" + obj_ext);
+
+ ctor_bin.capture();
+
+ // Run normally to build project
+ run_ctor({"-v"});
+
+ // Compiled object should now exist
+ assert_exists(BUILDDIR + "/hello-hello_cc" + obj_ext);
+
+ // ctor should not have been rebuilt, so binary should be the same
+ assert_not_changed(ctor_bin);
+
+ std::this_thread::sleep_for(1100ms);
+
+ auto time =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ std::filesystem::last_write_time("hello.cc", time + 1s);
+ std::this_thread::sleep_for(1100ms);
+
+ // Run normally to rebuild hello.cc
+ run_ctor({"-v"});
+
+ // Object file should have been recompiled
+ auto time2 =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ if(time == time2)
+ {
+ fail();
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // check if flags change trigger rebuild
+ {
+ // Replace -DFOO with -DBAR in foo external.cxxflags
+ copy_config("bar");
+ Tracker configuration_cc_bin("configuration.cc");
+ Tracker ctor_bin(ctor_exe);
+ auto time =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ std::this_thread::sleep_for(1100ms);
+
+ // Run normally to reconfigure, rebuild ctor and rebuild hello.cc
+ run_ctor({"-v"});
+
+ auto time2 =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ if(time == time2)
+ {
+ fail();
+ }
+
+ assert_changed(configuration_cc_bin);
+ assert_changed(ctor_bin);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // check if included files in ctor.cc is tracked for changes
+ {
+ copy_config("multi");
+ Tracker configuration_cc_bin("configuration.cc");
+ Tracker ctor_bin(ctor_exe);
+ auto time =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ std::this_thread::sleep_for(1100ms);
+
+ // Run normally to reconfigure, rebuild ctor and rebuild hello.cc
+ run_ctor({"-v"});
+
+ auto time2 =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ if(time == time2)
+ {
+ fail();
+ }
+
+ assert_changed(configuration_cc_bin);
+ assert_changed(ctor_bin);
+
+ // now touching foobar.h, should retrigger re-configuration
+ time = std::filesystem::last_write_time(ctor_exe);
+ std::filesystem::last_write_time("foobar.h", time + 1s);
+ std::this_thread::sleep_for(1100ms);
+
+ // Run normally to reconfigure, rebuild ctor and rebuild hello.cc
+ run_ctor({"-v"});
+
+ time2 = std::filesystem::last_write_time(ctor_exe);
+ if(time == time2)
+ {
+ fail();
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // generated part1: one-to-one generation
+ {
+ copy_config("generated");
+ std::filesystem::remove(BUILDDIR + "/world.cc");
+ std::filesystem::remove(BUILDDIR + "/foo.cc");
+
+ Tracker configuration_cc_bin("configuration.cc");
+ Tracker ctor_bin(ctor_exe);
+ std::this_thread::sleep_for(1100ms);
+
+ // Run normally to reconfigure, rebuild ctor and build world.cc
+ run_ctor({"-v", "world"});
+
+ assert_changed(configuration_cc_bin);
+ assert_changed(ctor_bin);
+
+ // foo.cc should not be generated at this point
+ assert_not_exists(BUILDDIR+"/foo.cc");
+
+ auto time_w = std::filesystem::last_write_time(BUILDDIR + "/world.cc");
+ auto time_wo =
+ std::filesystem::last_write_time(BUILDDIR + "/" + BUILDDIR+
+ "/world-world_cc" + obj_ext);
+ std::this_thread::sleep_for(1100ms);
+
+ // now touching hello.cc, should trigger regeneration of world.cc and
+ // rebuild
+ std::filesystem::last_write_time("hello.cc", time_w + 1s);
+ run_ctor({"-v", "world"});
+
+ auto time_w2 = std::filesystem::last_write_time(BUILDDIR + "/world.cc");
+ auto time_wo2 =
+ std::filesystem::last_write_time(BUILDDIR + "/" + BUILDDIR +
+ "/world-world_cc" + obj_ext);
+ if(time_w == time_w2)
+ {
+ fail();
+ }
+ if(time_wo == time_wo2)
+ {
+ fail();
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // generated part2: many-to-one generation
+ {
+ std::filesystem::remove(BUILDDIR + "/world.cc");
+ std::filesystem::remove(BUILDDIR + "/foo.cc");
+ std::filesystem::remove(BUILDDIR + "/many_to_one.cc");
+
+ run_ctor({"-v", "many_to_one"});
+
+ // world.cc should not be generated at this point
+ assert_not_exists(BUILDDIR+"/world.cc");
+
+ // foo.cc should have been generated at this point
+ assert_exists(BUILDDIR+"/foo.cc");
+
+ // many_to_one.cc should have been generated
+ assert_exists(BUILDDIR+"/many_to_one.cc");
+
+ auto time = std::filesystem::last_write_time(BUILDDIR + "/many_to_one.cc");
+ std::this_thread::sleep_for(1100ms);
+
+ Tracker ctor_bin(ctor_exe);
+
+ // remove "foo.cc" from sources list, so it only contains hello.cc
+ copy_config("generated2");
+
+ // rebuild
+ run_ctor({"-v", "many_to_one"});
+
+ // Verify that the change resulted in a ctor rebuild
+ assert_changed(ctor_bin);
+
+ // Verify that source-list change triggered a new build of many_to_one.cc
+ auto time2 =
+ std::filesystem::last_write_time(BUILDDIR + "/many_to_one.cc");
+ if(time == time2)
+ {
+ fail();
+ }
+ }
+
+ return 0;
+}
diff --git a/test/suite/test.sh b/test/suite/test.sh
index 97d2551..4638c0d 100755
--- a/test/suite/test.sh
+++ b/test/suite/test.sh
@@ -1,122 +1,4 @@
#!/bin/bash
: ${CXX:=g++}
-: ${CTORDIR:=../../build}
-: ${BUILDDIR:=build}
-CXX=$(which $CXX)
-
-function fail
-{
- echo "*** Failure at line $1"
- exit 1
-}
-
-function ctor
-{
- echo "*** Running: ./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
-rm -f ctor
-
-echo "** ctor_files/ctor.cc.base"
-cp ctor_files/ctor.cc.base ctor.cc
-
-# Compile bootstrap binary
-$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}
-
-# capture md5 sum of ctor binary before configure is called
-MD5=`md5sum ctor`
-ctor configure --ctor-includedir ../../src --ctor-libdir=${CTORDIR} --build-dir=${BUILDDIR}
-
-# ctor should be rebuilt at this point, so md5 sum should have changed
-(echo $MD5 | md5sum --status -c) && fail ${LINENO}
-
-# configuration.cc should have been generated now
-[ ! -f configuration.cc ] && fail ${LINENO}
-
-# Shouldn't compile anything yet - only configure
-[ -f ${BUILDDIR}/hello-hello_cc.o ] && fail ${LINENO}
-
-MD5=`md5sum ctor`
-
-# Run normally to build project
-ctor -v
-
-# Compiled object should now exist
-[ ! -f ${BUILDDIR}/hello-hello_cc.o ] && fail ${LINENO}
-
-# ctor should not have been rebuilt, so md5 sum should be the same
-(echo $MD5 | md5sum --status -c) || fail ${LINENO}
-
-MOD1=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
-touch hello.cc
-sleep 1.1
-
-# Run normally to rebuild hello.cc
-ctor -v
-
-# Object file should have been recompiled
-MOD2=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
-echo $MOD1
-echo $MOD2
-[[ $MOD1 == $MOD2 ]] && fail ${LINENO}
-
-# Replacve -DFOO with -DBAR in foo external.cxxflags
-echo "** ctor_files/ctor.cc.bar"
-cp ctor_files/ctor.cc.bar ctor.cc
-
-MD5C=`md5sum configuration.cc`
-MD5=`md5sum ctor`
-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 $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}
-
-echo "** ctor_files/ctor.cc.multi"
-cp ctor_files/ctor.cc.multi ctor.cc
-
-MD5C=`md5sum configuration.cc`
-MD5=`md5sum ctor`
-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 $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}
-
-# now touching foobar.h, should retrigger re-configuration
-touch foobar.h
-
-MOD1=`stat $STAT_FORMAT ctor`
-sleep 1.1
-
-# Run normally to reconfigure, rebuild ctor and rebuild hello.cc
-ctor -v
-
-MOD2=`stat $STAT_FORMAT ctor`
-[[ $MOD1 == $MOD2 ]] && fail ${LINENO}
-
-exit 0
+$CXX $LDFLAGS $CXXFLAGS -std=c++20 -Wall test.cc -o test && ./test
diff --git a/test/tasks_test.cc b/test/tasks_test.cc
index b444bd5..c8e4cf8 100644
--- a/test/tasks_test.cc
+++ b/test/tasks_test.cc
@@ -10,12 +10,12 @@ ctor::build_configurations ctorTestConfigs1(const ctor::settings&)
return
{
{
- .name = "Target1",
- .target = "target1",
- .sources = {"foo.cc", "bar.c"},
+ ctor::name("Target1"),
+ ctor::target("target1"),
+ ctor::sources({"foo.cc", "bar.c"}),
},
{
- .target = "target2",
+ ctor::target("target2"),
},
};
}
@@ -25,10 +25,10 @@ ctor::build_configurations ctorTestConfigs2(const ctor::settings&)
return
{
{
- .target = "target3",
+ ctor::target("target3"),
},
{
- .target = "target4",
+ ctor::target("target4"),
},
};
}
@@ -38,14 +38,14 @@ namespace test_global {
ctor::toolchain toolchain{};
ctor::arch arch{};
}
-const ctor::configuration& ctor::get_configuration()
+const ctor::configuration& test_configuration()
{
static ctor::configuration cfg{};
cfg.build_toolchain = test_global::toolchain;
cfg.build_arch = test_global::arch;
return cfg;
}
-
+REG(test_configuration);
REG(ctorTestConfigs1);
REG(ctorTestConfigs2);
@@ -72,7 +72,7 @@ public:
const ctor::settings& settings,
const std::string& name, bool dirty,
const std::vector<std::string>& deps = {})
- : Task(config, settings, {})
+ : Task(ctor::target_type::executable, config, settings, {})
, task_name(name)
, task_dirty(dirty)
, task_deps(deps)
@@ -170,7 +170,7 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(nullptr, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(nullptr, getNextTask({}, allTasks, dirtyTasks));
}
{ // Zero (One task, no dirty)
@@ -187,7 +187,7 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(nullptr, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(nullptr, getNextTask({}, allTasks, dirtyTasks));
}
{ // One (One task, one dirty)
@@ -205,7 +205,7 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(task1, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(task1, getNextTask({}, allTasks, dirtyTasks));
uASSERT_EQUAL(0u, dirtyTasks.size());
}
@@ -226,7 +226,7 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(task2, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(task2, getNextTask({}, allTasks, dirtyTasks));
uASSERT_EQUAL(0u, dirtyTasks.size());
}
@@ -250,7 +250,7 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(task2, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(task2, getNextTask({}, allTasks, dirtyTasks));
uASSERT_EQUAL(0u, dirtyTasks.size());
}
@@ -275,7 +275,7 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(task1, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(task1, getNextTask({}, allTasks, dirtyTasks));
uASSERT_EQUAL(1u, dirtyTasks.size());
}
}
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 5ae04c3..cfbcbce 100644
--- a/test/tools_test.cc
+++ b/test/tools_test.cc
@@ -114,29 +114,6 @@ bool operator!=(const ctor::asm_flag& a, const ctor::asm_flag& b)
#include <uunit.h>
-const ctor::configuration& ctor::get_configuration()
-{
- static ctor::configuration cfg;
- return cfg;
-}
-
-std::string ctor::configuration::get(const std::string& key, [[maybe_unused]]const std::string& default_value) const
-{
- if(key == ctor::cfg::host_cxx)
- {
- return {};
- }
-
- if(key == ctor::cfg::build_cxx)
- {
- return {};
- }
-
- assert(false); // bad key
-
- return {};
-}
-
class ToolsTest
: public uUnit
{