diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/argparser_test.cc | 1019 | ||||
| -rw-r--r-- | test/argsplit_test.cc | 203 | ||||
| -rw-r--r-- | test/ctor.cc | 149 | ||||
| -rw-r--r-- | test/cycle_test.cc | 87 | ||||
| -rw-r--r-- | test/deps_test.cc | 97 | ||||
| -rw-r--r-- | test/deps_test_data/empty.d | 0 | ||||
| -rw-r--r-- | test/deps_test_data/missing_colon.d | 1 | ||||
| -rw-r--r-- | test/deps_test_data/multiline.d | 4 | ||||
| -rw-r--r-- | test/deps_test_data/no_deps.d | 1 | ||||
| -rw-r--r-- | test/deps_test_data/no_newline.d | 1 | ||||
| -rw-r--r-- | test/deps_test_data/spaces.d | 1 | ||||
| -rw-r--r-- | test/deps_test_data/trivial.d | 1 | ||||
| -rw-r--r-- | test/execute_test.cc | 83 | ||||
| -rw-r--r-- | test/pointerlist_test.cc | 320 | ||||
| -rw-r--r-- | test/source_type_test.cc | 41 | ||||
| -rw-r--r-- | test/suite/ctor_files/ctor.cc.bar | 18 | ||||
| -rw-r--r-- | test/suite/ctor_files/ctor.cc.base | 19 | ||||
| -rw-r--r-- | test/suite/ctor_files/ctor.cc.multi | 18 | ||||
| -rwxr-xr-x | test/suite/test.sh | 44 | ||||
| -rw-r--r-- | test/tasks_test.cc | 197 | ||||
| -rw-r--r-- | test/testprog.cc | 55 | ||||
| -rw-r--r-- | test/tmpfile.h | 38 | ||||
| -rw-r--r-- | test/tools_test.cc | 927 | ||||
| m--------- | test/uunit | 0 | 
24 files changed, 3189 insertions, 135 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 c24ded7..0d77a3e 100644 --- a/test/ctor.cc +++ b/test/ctor.cc @@ -1,25 +1,111 @@  // -*- c++ -*-  // Distributed under the BSD 2-Clause License.  // See accompanying file LICENSE for details. -#include <libctor.h> +#include <ctor.h>  namespace  { -BuildConfigurations ctorTestConfigs(const Settings& settings) +ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)  {  	return  	{  		{ -			.type = TargetType::UnitTest, +			.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", +			}, +			.flags = { +				.cxxflags = { +					"-std=c++20", "-O3", "-Wall", "-Werror", +				}, +			}, +		}, +		{ +			.type = ctor::target_type::unit_test, +			.system = ctor::output_system::build,  			.target = "execute_test",  			.sources = {  				"execute_test.cc",  				"testmain.cc",  				"../src/execute.cc", +				"../src/pointerlist.cc", +				"../src/util.cc",  			}, +			.depends = { "testprog", },  			.flags = {  				.cxxflags = { -					"-std=c++20", "-O3", "-s", "-Wall", "-Werror", +					"-std=c++20", "-O3", "-Wall", "-Werror",  					"-I../src", "-Iuunit",  					"-DOUTPUT=\"execute\"",  				}, @@ -27,7 +113,8 @@ BuildConfigurations ctorTestConfigs(const Settings& settings)  			},  		},  		{ -			.type = TargetType::UnitTest, +			.type = ctor::target_type::unit_test, +			.system = ctor::output_system::build,  			.target = "tasks_test",  			.sources = {  				"tasks_test.cc", @@ -36,7 +123,7 @@ BuildConfigurations ctorTestConfigs(const Settings& settings)  			.depends = { "libctor_nomain.a" },  			.flags = {  				.cxxflags = { -					"-std=c++20", "-O3", "-s", "-Wall", "-Werror", +					"-std=c++20", "-O3", "-Wall", "-Werror",  					"-I../src", "-Iuunit",  					"-DOUTPUT=\"tasks\"",  				}, @@ -44,7 +131,26 @@ BuildConfigurations ctorTestConfigs(const Settings& settings)  			},  		},  		{ -			.type = TargetType::UnitTest, +			.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", @@ -53,7 +159,7 @@ BuildConfigurations ctorTestConfigs(const Settings& settings)  			.depends = { "libctor_nomain.a" },  			.flags = {  				.cxxflags = { -					"-std=c++20", "-O3", "-s", "-Wall", "-Werror", +					"-std=c++20", "-O3", "-Wall", "-Werror",  					"-I../src", "-Iuunit",  					"-DOUTPUT=\"source_type\"",  				}, @@ -61,12 +167,34 @@ BuildConfigurations ctorTestConfigs(const Settings& settings)  			},  		},  		{ -			.type = TargetType::UnitTestLib, +			.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", +					"-I../src", "-Iuunit", +					"-DOUTPUT=\"tools\"", +				}, +			}, +		}, +		{ +			.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", @@ -75,12 +203,13 @@ BuildConfigurations ctorTestConfigs(const Settings& settings)  				"../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", "-s", "-Wall", "-Werror", +					"-std=c++20", "-O3", "-Wall", "-Werror",  					"-I../src",  				},  				.ldflags = { "-pthread" }, diff --git a/test/cycle_test.cc b/test/cycle_test.cc new file mode 100644 index 0000000..3b45632 --- /dev/null +++ b/test/cycle_test.cc @@ -0,0 +1,87 @@ +#include <uunit.h> + +#include <ctor.h> +#include <build.h> + +namespace +{ +ctor::build_configurations ctorTestConfigsCyclic(const ctor::settings&) +{ +	return +	{ +		// No dependency +		{ +			.target = "target0", +		}, + +		// Direct (self-depends) +		{ +			.target = "target1", +			.depends = { "target1" }, +		}, + +		// Indirect cyclic depends +		{ +			.target = "target2", +			.depends = { "target3" }, +		}, +		{ +			.target = "target3", +			.depends = { "target2" }, +		}, +	}; +} +} + +REG(ctorTestConfigsCyclic); + +class CycleTest +	: public uUnit +{ +public: +	CycleTest() +	{ +		uTEST(CycleTest::getTargets_test); +	} + +	void getTargets_test() +	{ +		using namespace std::string_literals; +		ctor::settings settings{}; +		const auto& tasks = getTasks(settings); +		uASSERT_EQUAL(4u, tasks.size()); + +		uASSERT_EQUAL("target0"s, tasks[0]->target()); +		uASSERT_EQUAL("target1"s, tasks[1]->target()); +		uASSERT_EQUAL("target2"s, tasks[2]->target()); +		uASSERT_EQUAL("target3"s, tasks[3]->target()); + +		for(auto task : tasks) +		{ +			if(task->registerDepTasks(tasks)) +			{ +				uASSERT(false); +			} +		} + +		bool exc; +		exc = false; +		try { getDepTasks(tasks[0]); } catch(...) { exc = true; } +		uASSERT(!exc); + +		exc = false; +		try { getDepTasks(tasks[1]); } catch(...) { exc = true; } +		uASSERT(exc); + +		exc = false; +		try { getDepTasks(tasks[2]); } catch(...) { exc = true; } +		uASSERT(exc); + +		exc = false; +		try { getDepTasks(tasks[3]); } catch(...) { exc = true; } +		uASSERT(exc); +	} +}; + +// Registers the fixture into the 'registry' +static CycleTest test; diff --git a/test/deps_test.cc b/test/deps_test.cc new file mode 100644 index 0000000..762b0e5 --- /dev/null +++ b/test/deps_test.cc @@ -0,0 +1,97 @@ +#include <uunit.h> + +#include <fstream> +#include <map> +#include <vector> + +#include <deps.h> +#include <algorithm> + +#include "paths.h" +#include "tmpfile.h" + +class DepsTest +	: public uUnit +{ +public: +	DepsTest() +	{ +		uTEST(DepsTest::parser_test); +	} + +	void parser_test() +	{ +		using namespace std::string_literals; + +		auto test_data = paths::top_srcdir / "test" / "deps_test_data"; + +		auto empty =  test_data / "empty.d"; +		auto trivial = test_data / "trivial.d"; +		auto no_newline = test_data / "no_newline.d"; +		auto no_deps = test_data / "no_deps.d"; +		auto trailing_whitespace = test_data / "trailing_whitespace.d"; +		auto spaces = test_data / "spaces.d"; +		auto multiline = test_data / "multiline.d"; +		auto no_such_file = test_data / "no_such_file.d"; // doesn't exist +		auto missing_colon = test_data / "missing_colon.d"; + +		{ +			auto res = readDeps(empty.string(), ctor::toolchain::gcc); +			uASSERT(res.empty()); +		} + +		{ +			auto res = readDeps(trivial.string(), ctor::toolchain::gcc); +			uASSERT_EQUAL(1u, res.size()); +			uASSERT_EQUAL("x.cc"s, res[0]); +		} + +		{ +			auto res = readDeps(no_newline.string(), ctor::toolchain::gcc); +			uASSERT_EQUAL(1u, res.size()); +			uASSERT_EQUAL("x.cc"s, res[0]); +		} + +		{ +			auto res = readDeps(no_deps.string(), ctor::toolchain::gcc); +			uASSERT_EQUAL(0u, res.size()); +		} + +		{ +			auto res = readDeps(spaces.string(), ctor::toolchain::gcc); +			uASSERT_EQUAL(2u, res.size()); +			uASSERT_EQUAL("x y.cc"s, res[0]); +			uASSERT_EQUAL("x y.h"s, res[1]); +		} + +		{ +			auto res = readDeps(multiline.string(), ctor::toolchain::gcc); +			uASSERT_EQUAL(12u, res.size()); +			uASSERT_EQUAL("src/configure.cc"s, res[0]); +			uASSERT_EQUAL("src/configure.h"s, res[1]); +			uASSERT_EQUAL("src/getoptpp/getoptpp.hpp"s, res[2]); +			uASSERT_EQUAL("src/execute.h"s, res[3]); +			uASSERT_EQUAL("src/ctor.h"s, res[4]); +			uASSERT_EQUAL("src/tasks.h"s, res[5]); +			uASSERT_EQUAL("src/task.h"s, res[6]); +			uASSERT_EQUAL("src/rebuild.h"s, res[7]); +			uASSERT_EQUAL("src/externals.h"s, res[8]); +			uASSERT_EQUAL("src/externals_manual.h"s, res[9]); +			uASSERT_EQUAL("src/tools.h"s, res[10]); +			uASSERT_EQUAL("src/util.h"s, res[11]); +		} + +		{ +			auto res = readDeps(no_such_file.string(), ctor::toolchain::gcc); +			uASSERT(res.empty()); +		} + +		{ +			auto res = readDeps(missing_colon.string(), ctor::toolchain::gcc); +			uASSERT(res.empty()); +		} +	} +}; + +// Registers the fixture into the 'registry' +static DepsTest test; diff --git a/test/deps_test_data/empty.d b/test/deps_test_data/empty.d new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/deps_test_data/empty.d diff --git a/test/deps_test_data/missing_colon.d b/test/deps_test_data/missing_colon.d new file mode 100644 index 0000000..e46996c --- /dev/null +++ b/test/deps_test_data/missing_colon.d @@ -0,0 +1 @@ +x.cc x.h diff --git a/test/deps_test_data/multiline.d b/test/deps_test_data/multiline.d new file mode 100644 index 0000000..8862ab0 --- /dev/null +++ b/test/deps_test_data/multiline.d @@ -0,0 +1,4 @@ +build/src/libctor_a-configure_cc.o: src/configure.cc src/configure.h \ + src/getoptpp/getoptpp.hpp src/execute.h src/ctor.h src/tasks.h \ + src/task.h src/rebuild.h src/externals.h src/externals_manual.h \ + src/tools.h src/util.h diff --git a/test/deps_test_data/no_deps.d b/test/deps_test_data/no_deps.d new file mode 100644 index 0000000..e7cccf6 --- /dev/null +++ b/test/deps_test_data/no_deps.d @@ -0,0 +1 @@ +x.o:  diff --git a/test/deps_test_data/no_newline.d b/test/deps_test_data/no_newline.d new file mode 100644 index 0000000..88829ea --- /dev/null +++ b/test/deps_test_data/no_newline.d @@ -0,0 +1 @@ +x.o: x.cc
\ No newline at end of file diff --git a/test/deps_test_data/spaces.d b/test/deps_test_data/spaces.d new file mode 100644 index 0000000..c53fd64 --- /dev/null +++ b/test/deps_test_data/spaces.d @@ -0,0 +1 @@ +x\ y.o: x\ y.cc x\ y.h diff --git a/test/deps_test_data/trivial.d b/test/deps_test_data/trivial.d new file mode 100644 index 0000000..15a0c29 --- /dev/null +++ b/test/deps_test_data/trivial.d @@ -0,0 +1 @@ +x.o: x.cc diff --git a/test/execute_test.cc b/test/execute_test.cc index 9da18dc..11b067f 100644 --- a/test/execute_test.cc +++ b/test/execute_test.cc @@ -1,6 +1,15 @@  #include <uunit.h> +#include <fstream> +#include <map> +#include <vector> +  #include <execute.h> +#include <util.h> +#include <algorithm> + +#include "paths.h" +#include "tmpfile.h"  class ExecuteTest  	: public uUnit @@ -8,14 +17,78 @@ class ExecuteTest  public:  	ExecuteTest()  	{ -		uTEST(ExecuteTest::runit); +		uTEST(ExecuteTest::return_value); +		uTEST(ExecuteTest::env); +	} + +	void return_value() +	{ +		ctor::settings s; +		auto cur_path = std::filesystem::path(paths::argv_0).parent_path(); +		std::vector<std::string> paths{{cur_path.string()}}; +		auto cmd = locate("testprog", paths); +		uASSERT(!cmd.empty()); + +		auto value = execute(s, cmd, {"retval", "0"}, {}, false); +		uASSERT_EQUAL(0, value); +		value = execute(s, cmd, {"retval", "1"}, {}, false); +		uASSERT_EQUAL(1, value); +		value = execute(s, "no-such-binary", {}, {}, false); +		uASSERT_EQUAL(1, value); +		value = execute(s, cmd, {"segfault"}, {}, false); +		uASSERT_EQUAL(11, value); +		value = execute(s, cmd, {"throw"}, {}, false); +		uASSERT_EQUAL(6, value); +		value = execute(s, cmd, {"abort"}, {}, false); +		uASSERT_EQUAL(6, value);  	} -	void runit() +	void env()  	{ -		uASSERT_EQUAL(0, execute("/bin/true", {}, false)); -		uASSERT_EQUAL(1, execute("/bin/false", {}, false)); -		uASSERT_EQUAL(1, execute("no-such-binary", {}, false)); +		using namespace std::string_literals; + +		ctor::settings s; +		auto cur_path = std::filesystem::path(paths::argv_0).parent_path(); +		std::vector<std::string> paths{{cur_path.string()}}; +		auto cmd = locate("testprog", paths); +		uASSERT(!cmd.empty()); + +		TmpFile tmp; + +		std::map<std::string, std::string> env; + +		// New env vars +		env["foo"] = "bar"; +		env["bar"] = "42"; + +		// Overwrite the exiting LANG var +		env["LANG"] = "foo"; + +		auto value = execute(s, cmd, {"envdump", tmp.get()}, env, false); +		uASSERT_EQUAL(0, value); + +		std::vector<std::string> vars; +		{ +			std::ifstream infile(tmp.get()); +			std::string line; +			while (std::getline(infile, line)) +			{ +				vars.push_back(line); +			} +		} + +		// Check the two explicitly set vars +		auto chk = std::find(vars.begin(), vars.end(), "foo=bar"s); +		uASSERT(chk != vars.end()); +		chk = std::find(vars.begin(), vars.end(), "bar=42"s); +		uASSERT(chk != vars.end()); + +		// Check the one that should have overwritten the existing one (probably LANG=en_US.UTF-8 or something) +		chk = std::find(vars.begin(), vars.end(), "LANG=foo"s); +		uASSERT(chk != vars.end()); + +		// Check that other vars are also there (ie. the env wasn't cleared on entry) +		uASSERT(vars.size() > 3);  	}  }; diff --git a/test/pointerlist_test.cc b/test/pointerlist_test.cc new file mode 100644 index 0000000..4274473 --- /dev/null +++ b/test/pointerlist_test.cc @@ -0,0 +1,320 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#include <uunit.h> + +#include <set> +#include <algorithm> + +#include "pointerlist.h" + +class PointerListTest +	: public uUnit +{ +public: +	PointerListTest() +	{ +		uTEST(PointerListTest::test_zom_pointerlist_push); +		uTEST(PointerListTest::test_zom_pointerlist_from_args); +		uTEST(PointerListTest::test_zom_envmap_insert); +		uTEST(PointerListTest::test_zom_envmap_from_env); +		uTEST(PointerListTest::test_exceptional_env); +	} + +	void test_zom_pointerlist_push() +	{ +		using namespace std::string_literals; + +		{ // Zero +			PointerList args; +			uASSERT_EQUAL(0u, args.size()); +			auto [argc, argv] = args.get(); +			uASSERT_EQUAL(0, argc); +			uASSERT(nullptr != argv); +			uASSERT_EQUAL(nullptr, argv[0]); +		} + +		{ // One +			PointerList args; +			args.push_back("hello"); +			uASSERT_EQUAL(1u, args.size()); +			auto [argc, argv] = args.get(); +			uASSERT_EQUAL(1, argc); +			uASSERT_EQUAL("hello"s, std::string(argv[0])); +		} + +		{ // Many +			PointerList args; +			args.push_back("hello"); +			args.push_back("dear"); +			args.push_back("world"); +			uASSERT_EQUAL(3u, args.size()); +			auto [argc, argv] = args.get(); +			uASSERT_EQUAL(3, argc); +			uASSERT_EQUAL("hello"s, std::string(argv[0])); +			uASSERT_EQUAL("dear"s, std::string(argv[1])); +			uASSERT_EQUAL("world"s, std::string(argv[2])); +		} +	} + +	void test_zom_pointerlist_from_args() +	{ +		using namespace std::string_literals; + +		{ // Zero +			PointerList args(0, nullptr); +			uASSERT_EQUAL(0u, args.size()); +			auto [argc, argv] = args.get(); +			uASSERT_EQUAL(0, argc); +			uASSERT(nullptr != argv); +			uASSERT_EQUAL(nullptr, argv[0]); +		} + +		{ // One +			int _argc{1}; +			const char* _argv[] = { "hello" }; +			PointerList args(_argc, _argv); +			uASSERT_EQUAL(1u, args.size()); +			auto [argc, argv] = args.get(); +			uASSERT_EQUAL(1, argc); +			uASSERT_EQUAL("hello"s, std::string(argv[0])); +		} + +		{ // Many +			int _argc{3}; +			const char* _argv[] = { "hello", "dear", "world" }; +			PointerList args(_argc, _argv); +			uASSERT_EQUAL(3u, args.size()); +			auto [argc, argv] = args.get(); +			uASSERT_EQUAL(3, argc); +			// order must be preserved +			uASSERT_EQUAL("hello"s, std::string(argv[0])); +			uASSERT_EQUAL("dear"s, std::string(argv[1])); +			uASSERT_EQUAL("world"s, std::string(argv[2])); +		} +	} + +	void test_zom_envmap_insert() +	{ +		using namespace std::string_literals; + +		{ // Zero +			EnvMap env; +			uASSERT_EQUAL(0u, env.size()); + +			auto [argc, argv] = env.get(); +			uASSERT_EQUAL(0, argc); +			uASSERT(nullptr != argv); +			uASSERT_EQUAL(nullptr, argv[0]); + +			auto str = env.stringify(); +			uASSERT_EQUAL(1u, str.size()); +			uASSERT_EQUAL('\0', str[0]); +		} + +		{ // One (key only) +			EnvMap env; +			env.insert("hello"); +			uASSERT_EQUAL(1u, env.size()); +			auto [argc, argv] = env.get(); +			uASSERT_EQUAL(1, argc); +			uASSERT_EQUAL("hello="s, std::string(argv[0])); +			uASSERT_EQUAL(""s, env["hello"]); +		} +		{ // One (with value) +			EnvMap env; +			env.insert("hello=world"); +			uASSERT_EQUAL(1u, env.size()); +			auto [argc, argv] = env.get(); +			uASSERT_EQUAL(1, argc); +			uASSERT_EQUAL("hello=world"s, std::string(argv[0])); +			uASSERT_EQUAL("world"s, env["hello"]); + +			uASSERT_EQUAL("hello=world\0\0"s, env.stringify()); +		} + +		{ // Overwrite one +			EnvMap env; +			env.insert("hello=world"); +			uASSERT_EQUAL(1u, env.size()); +			uASSERT_EQUAL("world"s, env["hello"s]); + +			env.insert("hello=foo"); +			uASSERT_EQUAL(1u, env.size()); +			uASSERT_EQUAL("foo"s, env["hello"s]); +		} + +		{ // Many +			EnvMap env; +			env.insert("hello=world"); +			env.insert("world=leader"); +			env.insert("dear=boar"); +			uASSERT_EQUAL(3u, env.size()); + +			uASSERT_EQUAL("boar"s, env["dear"s]); +			uASSERT_EQUAL("world"s, env["hello"s]); +			uASSERT_EQUAL("leader"s, env["world"s]); + +			auto [argc, argv] = env.get(); +			uASSERT_EQUAL(3, argc); +			// store and sort to verify unordered +			std::vector<std::string> vals{argv[0], argv[1], argv[2]}; +			std::ranges::sort(vals); +			uASSERT_EQUAL("dear=boar"s, vals[0]); +			uASSERT_EQUAL("hello=world"s, vals[1]); +			uASSERT_EQUAL("world=leader"s, vals[2]); + +			// test all combinations since ordering is not a requirement +			// exactly one of the must be true (boolean XOR) +			auto str = env.stringify(); +			uASSERT(((((("dear=boar\0hello=world\0world=leader\0\0"s == str) != +			            ("dear=boar\0world=leader\0hello=world\0\0"s == str)) != +			           ("hello=world\0dear=boar\0world=leader\0\0"s == str)) != +			          ("hello=world\0world=leader\0dear=boar\0\0"s == str)) != +			         ("world=leader\0dear=boar\0hello=world\0\0"s == str)) != +			        ("world=leader\0hello=world\0dear=boar\0\0"s == str)); +		} +	} + +	void test_zom_envmap_from_env() +	{ +		using namespace std::string_literals; + +		{ // Zero +			const char* penv = nullptr; +			EnvMap env(penv); +			uASSERT_EQUAL(0u, env.size()); +			auto [argc, argv] = env.get(); +			uASSERT_EQUAL(0, argc); +			uASSERT(nullptr != argv); +			uASSERT_EQUAL(nullptr, argv[0]); +		} + +		{ // Zero +			const char** ppenv = nullptr; +			EnvMap env(ppenv); +			uASSERT_EQUAL(0u, env.size()); +			auto [argc, argv] = env.get(); +			uASSERT_EQUAL(0, argc); +			uASSERT(nullptr != argv); +			uASSERT_EQUAL(nullptr, argv[0]); +		} + +		{ // One (key only) +			const char* ptr = "hello\0\0"; +			EnvMap env(ptr); +			uASSERT_EQUAL(1u, env.size()); +			auto [argc, argv] = env.get(); +			uASSERT_EQUAL(1, argc); +			uASSERT_EQUAL("hello="s, std::string(argv[0])); +			uASSERT_EQUAL(""s, env["hello"]); +		} + +		{ // One (key only) +			const char* ptr[] = {"hello", nullptr}; +			EnvMap env(ptr); +			uASSERT_EQUAL(1u, env.size()); +			auto [argc, argv] = env.get(); +			uASSERT_EQUAL(1, argc); +			uASSERT_EQUAL("hello="s, std::string(argv[0])); +			uASSERT_EQUAL(""s, env["hello"]); +		} + +		{ // One (with value) +			const char* ptr = "hello=world\0\0"; +			EnvMap env(ptr); +			uASSERT_EQUAL(1u, env.size()); +			auto [argc, argv] = env.get(); +			uASSERT_EQUAL(1, argc); +			uASSERT_EQUAL("hello=world"s, std::string(argv[0])); +			uASSERT_EQUAL("world"s, env["hello"]); + +			uASSERT_EQUAL("hello=world\0\0"s, env.stringify()); +		} + +		{ // One (with value) +			const char* ptr[] = {"hello=world\0", nullptr}; +			EnvMap env(ptr); +			uASSERT_EQUAL(1u, env.size()); +			auto [argc, argv] = env.get(); +			uASSERT_EQUAL(1, argc); +			uASSERT_EQUAL("hello=world"s, std::string(argv[0])); +			uASSERT_EQUAL("world"s, env["hello"]); + +			uASSERT_EQUAL("hello=world\0\0"s, env.stringify()); +		} + +		{ // Many +			const char* ptr = "hello=world\0world=leader\0dear=boar\0\0"; +			EnvMap env(ptr); +			uASSERT_EQUAL(3u, env.size()); + +			uASSERT_EQUAL("boar"s, env["dear"s]); +			uASSERT_EQUAL("world"s, env["hello"s]); +			uASSERT_EQUAL("leader"s, env["world"s]); + +			auto [argc, argv] = env.get(); +			uASSERT_EQUAL(3, argc); +			// store and sort to verify unordered +			std::vector<std::string> vals{argv[0], argv[1], argv[2]}; +			std::ranges::sort(vals); +			uASSERT_EQUAL("dear=boar"s, vals[0]); +			uASSERT_EQUAL("hello=world"s, vals[1]); +			uASSERT_EQUAL("world=leader"s, vals[2]); + +			// test all combinations since ordering is not a requirement +			// exactly one of the must be true (boolean XOR) +			auto str = env.stringify(); +			uASSERT(((((("dear=boar\0hello=world\0world=leader\0\0"s == str) != +			            ("dear=boar\0world=leader\0hello=world\0\0"s == str)) != +			           ("hello=world\0dear=boar\0world=leader\0\0"s == str)) != +			          ("hello=world\0world=leader\0dear=boar\0\0"s == str)) != +			         ("world=leader\0dear=boar\0hello=world\0\0"s == str)) != +			        ("world=leader\0hello=world\0dear=boar\0\0"s == str)); +		} + +		{ // Many +			const char* ptr[] = +				{"hello=world\0", "world=leader\0", "dear=boar\0", nullptr}; +			EnvMap env(ptr); +			uASSERT_EQUAL(3u, env.size()); + +			uASSERT_EQUAL("boar"s, env["dear"s]); +			uASSERT_EQUAL("world"s, env["hello"s]); +			uASSERT_EQUAL("leader"s, env["world"s]); + +			auto [argc, argv] = env.get(); +			uASSERT_EQUAL(3, argc); +			// store and sort to verify unordered +			std::vector<std::string> vals{argv[0], argv[1], argv[2]}; +			std::ranges::sort(vals); +			uASSERT_EQUAL("dear=boar"s, vals[0]); +			uASSERT_EQUAL("hello=world"s, vals[1]); +			uASSERT_EQUAL("world=leader"s, vals[2]); + +			// test all combinations since ordering is not a requirement +			// exactly one of the must be true (boolean XOR) +			auto str = env.stringify(); +			uASSERT(((((("dear=boar\0hello=world\0world=leader\0\0"s == str) != +			            ("dear=boar\0world=leader\0hello=world\0\0"s == str)) != +			           ("hello=world\0dear=boar\0world=leader\0\0"s == str)) != +			          ("hello=world\0world=leader\0dear=boar\0\0"s == str)) != +			         ("world=leader\0dear=boar\0hello=world\0\0"s == str)) != +			        ("world=leader\0hello=world\0dear=boar\0\0"s == str)); +		} +	} + +	void test_exceptional_env() +	{ +		using namespace std::string_literals; + +		{ // Zero +			EnvMap env; +			uASSERT_EQUAL(0u, env.size()); +			uASSERT_EQUAL(""s, env["foo"]); // lookup of non-existing key +		} +	} +}; + +// Registers the fixture into the 'registry' +static PointerListTest test; diff --git a/test/source_type_test.cc b/test/source_type_test.cc index ed7e783..288f1e5 100644 --- a/test/source_type_test.cc +++ b/test/source_type_test.cc @@ -1,37 +1,40 @@ -#include <uunit.h> - -#include <libctor.h> +#include <ctor.h>  #include <task_cc.h> -std::ostream& operator<<(std::ostream& stream, const Language& lang) +std::ostream& operator<<(std::ostream& stream, const ctor::language& lang); + +#include <uunit.h> + +std::ostream& operator<<(std::ostream& stream, const ctor::language& lang)  {  	switch(lang)  	{ -	case Language::Auto: -		stream << "Language::Auto"; +	case ctor::language::automatic: +		stream << "ctor::language::automatic";  		break; -	case Language::C: -		stream << "Language::C"; +	case ctor::language::c: +		stream << "ctor::language::c";  		break; -	case Language::Cpp: -		stream << "Language::Cpp"; +	case ctor::language::cpp: +		stream << "ctor::language::cpp";  		break; -	case Language::Asm: -		stream << "Language::Asm"; +	case ctor::language::assembler: +		stream << "ctor::language::assembler";  		break;  	}  	return stream;  } +  class TestableTaskCC  	: public TaskCC  {  public: -	TestableTaskCC(const Source& source) +	TestableTaskCC(const ctor::source& source)  		: TaskCC({}, {}, "build", source)  	{} -	Language language() const +	ctor::language language() const  	{  		return source_language;  	} @@ -50,22 +53,22 @@ public:  	{  		{ // c++  			TestableTaskCC task("hello.cc"); -			uASSERT_EQUAL(Language::Cpp, task.language()); +			uASSERT_EQUAL(ctor::language::cpp, task.language());  		}  		{ // c  			TestableTaskCC task("hello.c"); -			uASSERT_EQUAL(Language::C, task.language()); +			uASSERT_EQUAL(ctor::language::c, task.language());  		}  		{ // asm  			TestableTaskCC task("hello.s"); -			uASSERT_EQUAL(Language::Asm, task.language()); +			uASSERT_EQUAL(ctor::language::assembler, task.language());  		}  		{ // custom/explicit language -			TestableTaskCC task( {"hello.foo", Language::Asm} ); -			uASSERT_EQUAL(Language::Asm, task.language()); +			TestableTaskCC task( {"hello.foo", ctor::language::assembler} ); +			uASSERT_EQUAL(ctor::language::assembler, task.language());  		}  		// Note: Failure state will result in exit(1) so cannot be tested diff --git a/test/suite/ctor_files/ctor.cc.bar b/test/suite/ctor_files/ctor.cc.bar index 92456cb..218f9cc 100644 --- a/test/suite/ctor_files/ctor.cc.bar +++ b/test/suite/ctor_files/ctor.cc.bar @@ -1,12 +1,12 @@  // -*- c++ -*-  // Distributed under the BSD 2-Clause License.  // See accompanying file LICENSE for details. -#include <libctor.h> +#include <ctor.h>  //#include "config.h"  namespace  { -BuildConfigurations ctorConfigs() +ctor::build_configurations ctorConfigs(const ctor::settings& settings)  {  	return  	{ @@ -30,17 +30,19 @@ BuildConfigurations ctorConfigs()  	};  } -ExternalConfigurations ctorExtConfigs() +ctor::external_configurations ctorExtConfigs(const ctor::settings& settings)  {  	return  	{  		{  			.name = "bar", -			.flags = { -				.cxxflags = { "-D_A_", "-DBAR"}, -				.cflags = { "-D_B_" }, -				.ldflags = { "-D_C_" }, -				.asmflags = { "-D_D_" }, +			.external = ctor::external_manual{ +				.flags = { +					.cflags = { "-D_B_" }, +					.cxxflags = { "-D_A_", "-DBAR"}, +					.ldflags = { "-D_C_" }, +					.asmflags = { "-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 6c60513..eab39c4 100644 --- a/test/suite/ctor_files/ctor.cc.base +++ b/test/suite/ctor_files/ctor.cc.base @@ -1,12 +1,12 @@  // -*- c++ -*-  // Distributed under the BSD 2-Clause License.  // See accompanying file LICENSE for details. -#include <libctor.h> +#include <ctor.h>  //#include "config.h"  namespace  { -BuildConfigurations ctorConfigs() +ctor::build_configurations ctorConfigs(const ctor::settings& settings)  {  	return  	{ @@ -30,17 +30,20 @@ BuildConfigurations ctorConfigs()  	};  } -ExternalConfigurations ctorExtConfigs() +ctor::external_configurations ctorExtConfigs(const ctor::settings& settings)  {  	return  	{  		{  			.name = "bar", -			.flags = { -				.cxxflags = { "-D_A_", "-DFOO"}, -				.cflags = { "-D_B_" }, -				.ldflags = { "-D_C_" }, -				.asmflags = { "-D_D_" }, +			.external = ctor::external_manual +			{ +				.flags = { +					.cflags = { "-D_B_" }, +					.cxxflags = { "-D_A_", "-DFOO"}, +					.ldflags = { "-D_C_" }, +					.asmflags = { "-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.multi b/test/suite/ctor_files/ctor.cc.multi index 9db2517..2b88afe 100644 --- a/test/suite/ctor_files/ctor.cc.multi +++ b/test/suite/ctor_files/ctor.cc.multi @@ -1,14 +1,14 @@  // -*- c++ -*-  // Distributed under the BSD 2-Clause License.  // See accompanying file LICENSE for details. -#include <libctor.h> +#include <ctor.h>  //#include "config.h"  #include "foobar.h"  namespace  { -BuildConfigurations ctorConfigs() +ctor::build_configurations ctorConfigs(const ctor::settings& settings)  {  	return  	{ @@ -32,17 +32,19 @@ BuildConfigurations ctorConfigs()  	};  } -ExternalConfigurations ctorExtConfigs() +ctor::external_configurations ctorExtConfigs(const ctor::settings& settings)  {  	return  	{  		{  			.name = "bar", -			.flags = { -				.cxxflags = { "-D_A_", "-DFOO"}, -				.cflags = { "-D_B_" }, -				.ldflags = { "-D_C_" }, -				.asmflags = { "-D_D_" }, +			.external = ctor::external_manual{ +				.flags = { +					.cflags = { "-D_B_" }, +					.cxxflags = { "-D_A_", "-DFOO"}, +					.ldflags = { "-D_C_" }, +					.asmflags = { "-D_D_" }, +				},  			},  			// Creates --with-foo-prefix arg to configure which will be used for  			// -L and -I flags. diff --git a/test/suite/test.sh b/test/suite/test.sh index c980154..c54137a 100755 --- a/test/suite/test.sh +++ b/test/suite/test.sh @@ -1,4 +1,9 @@  #!/bin/bash +: ${CXX:=g++} +: ${CTORDIR:=../../build} +: ${BUILDDIR:=build} + +CXX=$(which $CXX)  function fail  { @@ -12,22 +17,29 @@ function ctor  		./ctor $*  } +STAT_FORMAT="-c %Y" +if [[ "$OSTYPE" == "darwin"* ]]; then +    # Mac OSX +		STAT_FORMAT="-f %B" +fi +  # Wipe the board -rm -Rf build +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 -g++ -pthread -std=c++20 -L../../build -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 build ] && fail ${LINENO} +[ -d ${BUILDDIR} ] && fail ${LINENO}  # capture md5 sum of ctor binary before configure is called  MD5=`md5sum ctor` -ctor configure --ctor-includedir ../../src --ctor-libdir ../../build +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} @@ -36,7 +48,7 @@ ctor configure --ctor-includedir ../../src --ctor-libdir ../../build  [ ! -f configuration.cc ] && fail ${LINENO}  # Shouldn't compile anything yet - only configure -[ -f build/hello-hello_cc.o ] && fail ${LINENO} +[ -f ${BUILDDIR}/hello-hello_cc.o ] && fail ${LINENO}  MD5=`md5sum ctor` @@ -44,12 +56,12 @@ MD5=`md5sum ctor`  ctor -v  # Compiled object should now exist -[ ! -f build/hello-hello_cc.o ] && fail ${LINENO} +[ ! -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 -c %Y build/hello-hello_cc.o` +MOD1=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`  touch hello.cc  sleep 1.1 @@ -57,36 +69,38 @@ sleep 1.1  ctor -v  # Object file should have been recompiled -MOD2=`stat -c %Y build/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 +echo "** ctor_files/ctor.cc.bar"  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 build/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} +echo "** ctor_files/ctor.cc.multi"  cp ctor_files/ctor.cc.multi ctor.cc  MD5C=`md5sum configuration.cc`  MD5=`md5sum ctor` -MOD1=`stat -c %Y build/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 build/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} @@ -94,11 +108,13 @@ MOD2=`stat -c %Y build/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 2e0ffc7..cbd0864 100644 --- a/test/tasks_test.cc +++ b/test/tasks_test.cc @@ -1,15 +1,16 @@  #include <uunit.h> -#include <libctor.h> +#include <ctor.h>  #include <tasks.h>  namespace  { -BuildConfigurations ctorTestConfigs1(const Settings&) +ctor::build_configurations ctorTestConfigs1(const ctor::settings&)  {  	return  	{  		{ +			.name = "Target1",  			.target = "target1",  			.sources = {"foo.cc", "bar.c"},  		}, @@ -19,7 +20,7 @@ BuildConfigurations ctorTestConfigs1(const Settings&)  	};  } -BuildConfigurations ctorTestConfigs2(const Settings&) +ctor::build_configurations ctorTestConfigs2(const ctor::settings&)  {  	return  	{ @@ -33,16 +34,29 @@ BuildConfigurations ctorTestConfigs2(const Settings&)  }  } +namespace test_global { +ctor::toolchain toolchain{}; +ctor::arch arch{}; +} +const ctor::configuration& ctor::get_configuration() +{ +	static ctor::configuration cfg{}; +	cfg.build_toolchain = test_global::toolchain; +	cfg.build_arch = test_global::arch; +	return cfg; +} + +  REG(ctorTestConfigs1);  REG(ctorTestConfigs2); -std::size_t count(const std::set<std::shared_ptr<Task>>& tasks, -                  const std::string& name) +std::size_t count(const std::vector<std::shared_ptr<Task>>& tasks, +                  const std::filesystem::path& name)  {  	auto cnt{0u};  	for(const auto& task : tasks)  	{ -		if(task->target() == name) +		if(task->target() == name.string())  		{  			cnt++;  		} @@ -54,16 +68,17 @@ class TestTask  	: public Task  {  public: -	TestTask(const std::string& name, bool dirty, +	TestTask(const ctor::build_configuration& config, +	         const ctor::settings& settings, +	         const std::string& name, bool dirty,  	         const std::vector<std::string>& deps = {}) -		: Task({}, {}, {}) +		: Task(config, settings, {})  		, task_name(name)  		, task_dirty(dirty)  		, task_deps(deps)  	{  	} -	std::string name() const override { return task_name; }  	int clean() override { return 0; }  	std::vector<std::string> depends() const override { return task_deps; }  	std::string target() const override { return task_name; } @@ -81,17 +96,20 @@ 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()  	{  		using namespace std::string_literals; -		Settings settings{}; +		ctor::settings settings{};  		const auto& targets = getTargets(settings);  		uASSERT_EQUAL(4u, targets.size()); @@ -108,27 +126,29 @@ public:  	void getTasks_test()  	{ +		test_global::toolchain = ctor::toolchain::gcc; +		test_global::arch = ctor::arch::unix; +  		using namespace std::string_literals; -		Settings settings{ .builddir = "foo" }; +		ctor::settings settings{ .builddir = "foo" };  		{  			auto tasks = getTasks(settings);  			uASSERT_EQUAL(6u, tasks.size()); -			// Note: count() is used here because the order of -			// std::set<std::shared_ptr<T>> is not deterministic. -			uASSERT_EQUAL(1u, count(tasks, "target1"s)); -			uASSERT_EQUAL(1u, count(tasks, "target2"s)); -			uASSERT_EQUAL(1u, count(tasks, "target3"s)); -			uASSERT_EQUAL(1u, count(tasks, "target4"s)); -			uASSERT_EQUAL(1u, count(tasks, "test/target1-foo_cc.o"s)); -			uASSERT_EQUAL(1u, count(tasks, "test/target1-bar_c.o"s)); +			// Note: count() is used here because the order doesn't matter +			uASSERT_EQUAL(1u, count(tasks, fs("target1"))); +			uASSERT_EQUAL(1u, count(tasks, fs("target2"))); +			uASSERT_EQUAL(1u, count(tasks, fs("target3"))); +			uASSERT_EQUAL(1u, count(tasks, fs("target4"))); +			uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-foo_cc.o")); +			uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-bar_c.o"));  		}  		{  			auto tasks = getTasks(settings, {"target1", "target3"});  			uASSERT_EQUAL(4u, tasks.size()); -			uASSERT_EQUAL(1u, count(tasks, "target1"s)); -			uASSERT_EQUAL(1u, count(tasks, "target3"s)); -			uASSERT_EQUAL(1u, count(tasks, "test/target1-foo_cc.o"s)); -			uASSERT_EQUAL(1u, count(tasks, "test/target1-bar_c.o"s)); +			uASSERT_EQUAL(1u, count(tasks, fs("target1"))); +			uASSERT_EQUAL(1u, count(tasks, fs("target3"))); +			uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-foo_cc.o")); +			uASSERT_EQUAL(1u, count(tasks, fs("test")/"target1-bar_c.o"));  		}  		{  			auto tasks = getTasks(settings, {"no-such-target"}); @@ -139,119 +159,170 @@ public:  	void getNextTask_test()  	{  		using namespace std::string_literals; -		Settings settings{}; +		ctor::settings settings{};  		{ // Zero (Empty) -			std::set<std::shared_ptr<Task>> allTasks; -			std::set<std::shared_ptr<Task>> dirtyTasks; +			std::vector<std::shared_ptr<Task>> allTasks; +			std::vector<std::shared_ptr<Task>> dirtyTasks;  			for(auto& task : dirtyTasks)  			{  				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::set<std::shared_ptr<Task>> allTasks; -			allTasks.insert(task1); +			std::vector<std::shared_ptr<Task>> allTasks; +			allTasks.push_back(task1); -			std::set<std::shared_ptr<Task>> dirtyTasks; +			std::vector<std::shared_ptr<Task>> dirtyTasks;  			for(auto& task : dirtyTasks)  			{  				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::set<std::shared_ptr<Task>> allTasks; -			allTasks.insert(task1); +			std::vector<std::shared_ptr<Task>> allTasks; +			allTasks.push_back(task1); -			std::set<std::shared_ptr<Task>> dirtyTasks; -			dirtyTasks.insert(task1); +			std::vector<std::shared_ptr<Task>> dirtyTasks; +			dirtyTasks.push_back(task1);  			for(auto& task : dirtyTasks)  			{  				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::set<std::shared_ptr<Task>> allTasks; -			allTasks.insert(task1); -			allTasks.insert(task2); +			std::vector<std::shared_ptr<Task>> allTasks; +			allTasks.push_back(task1); +			allTasks.push_back(task2); -			std::set<std::shared_ptr<Task>> dirtyTasks; -			dirtyTasks.insert(task2); +			std::vector<std::shared_ptr<Task>> dirtyTasks; +			dirtyTasks.push_back(task2);  			for(auto& task : dirtyTasks)  			{  				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::set<std::shared_ptr<Task>> allTasks; -			allTasks.insert(task1); -			allTasks.insert(task2); +			std::vector<std::shared_ptr<Task>> allTasks; +			allTasks.push_back(task1); +			allTasks.push_back(task2); -			std::set<std::shared_ptr<Task>> dirtyTasks; -			dirtyTasks.insert(task2); +			std::vector<std::shared_ptr<Task>> dirtyTasks; +			dirtyTasks.push_back(task2);  			for(auto& task : dirtyTasks)  			{  				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::set<std::shared_ptr<Task>> allTasks; -			allTasks.insert(task2); -			allTasks.insert(task1); +			std::vector<std::shared_ptr<Task>> allTasks; +			allTasks.push_back(task2); +			allTasks.push_back(task1); -			std::set<std::shared_ptr<Task>> dirtyTasks; -			dirtyTasks.insert(task2); -			dirtyTasks.insert(task1); +			std::vector<std::shared_ptr<Task>> dirtyTasks; +			dirtyTasks.push_back(task2); +			dirtyTasks.push_back(task1);  			for(auto& task : dirtyTasks)  			{  				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 new file mode 100644 index 0000000..93edc3f --- /dev/null +++ b/test/testprog.cc @@ -0,0 +1,55 @@ +#include <iostream> +#include <fstream> +#include <string> +#include <csignal> + +extern char **environ; + +int main(int argc, const char* argv[]) +{ +	if(argc < 2) +	{ +		return 0; +	} + +	std::string cmd = argv[1]; + +	if(cmd == "envdump") +	{ +		if(argc < 3) +		{ +			return 0; +		} +		std::ofstream ostrm(argv[2], std::ios::binary); +		for(auto current = environ; *current; ++current) +		{ +			ostrm << (*current) << "\n"; +		} +	} + +	if(cmd == "retval") +	{ +		if(argc < 3) +		{ +			return 0; +		} +		return std::stoi(argv[2]); +	} + +	if(cmd == "abort") +	{ +		abort(); +	} + +	if(cmd == "segfault") +	{ +		raise(SIGSEGV); +	} + +	if(cmd == "throw") +	{ +		throw "ouch"; +	} + +	return 0; +} diff --git a/test/tmpfile.h b/test/tmpfile.h new file mode 100644 index 0000000..0f83a20 --- /dev/null +++ b/test/tmpfile.h @@ -0,0 +1,38 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#pragma once + +#include <cstdio> + +class TmpFile +{ +public: +	TmpFile(const std::string& data = {}) +	{ +		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); +	} + +	~TmpFile() +	{ +		std::filesystem::remove(filename); +	} + +	const std::string& get() const +	{ +		return filename; +	} + +private: +	std::string filename; +}; diff --git a/test/tools_test.cc b/test/tools_test.cc new file mode 100644 index 0000000..5ae04c3 --- /dev/null +++ b/test/tools_test.cc @@ -0,0 +1,927 @@ +#include <vector> +#include <string> +#include <ostream> +#include <initializer_list> +#include <cassert> + +#include <tools.h> + +std::ostream& operator<<(std::ostream& stream, const ctor::toolchain& toolchain) +{ +	switch(toolchain) +	{ +	case ctor::toolchain::none: +		stream << "ctor::toolchain::none"; +		break; +	case ctor::toolchain::any: +		stream << "ctor::toolchain::any"; +		break; +	case ctor::toolchain::gcc: +		stream << "ctor::toolchain::gcc"; +		break; +	case ctor::toolchain::clang: +		stream << "ctor::toolchain::clang"; +		break; +	} +	return stream; +} + +std::ostream& operator<<(std::ostream& stream, const std::vector<std::string>& vs) +{ +	bool first{true}; +	stream << "{ "; +	for(const auto& v : vs) +	{ +		if(!first) +		{ +			stream << ", "; +		} +		stream << "'" << v << "'"; +		first = false; +	} +	stream << " }"; + +	return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::c_flag& flag) +{ +	stream << "{" << flag.opt << ", \"" << flag.arg << "\", " << flag.toolchain << "}"; +	return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::cxx_flag& flag) +{ +	stream << "{" << flag.opt << ", \"" << flag.arg << "\", " << flag.toolchain << "}"; +	return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::ld_flag& flag) +{ +	stream << "{" << flag.opt << ", \"" << flag.arg << "\", " << flag.toolchain << "}"; +	return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::ar_flag& flag) +{ +	stream << "{" << flag.opt << ", \"" << flag.arg << "\", " << flag.toolchain << "}"; +	return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::asm_flag& flag) +{ +	stream << "{" << flag.opt << ", \"" << flag.arg << "\", " << flag.toolchain << "}"; +	return stream; +} + +bool operator!=(const ctor::c_flag& a, const ctor::c_flag& b) +{ +	return +		a.opt != b.opt || +		a.arg != b.arg || +		a.toolchain != b.toolchain; +} + +bool operator!=(const ctor::cxx_flag& a, const ctor::cxx_flag& b) +{ +	return +		a.opt != b.opt || +		a.arg != b.arg || +		a.toolchain != b.toolchain; +} +bool operator!=(const ctor::ld_flag& a, const ctor::ld_flag& b) +{ +	return +		a.opt != b.opt || +		a.arg != b.arg || +		a.toolchain != b.toolchain; +} + +bool operator!=(const ctor::ar_flag& a, const ctor::ar_flag& b) +{ +	return +		a.opt != b.opt || +		a.arg != b.arg || +		a.toolchain != b.toolchain; +} +bool operator!=(const ctor::asm_flag& a, const ctor::asm_flag& b) +{ +	return +		a.opt != b.opt || +		a.arg != b.arg || +		a.toolchain != b.toolchain; +} + +#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 +{ +public: +	ToolsTest() +	{ +		uTEST(ToolsTest::getToolChain_test); + +		uTEST(ToolsTest::getOption_toolchain_c_test); +		uTEST(ToolsTest::getOption_toolchain_cxx_test); +		uTEST(ToolsTest::getOption_toolchain_ld_test); +		uTEST(ToolsTest::getOption_toolchain_ar_test); +		uTEST(ToolsTest::getOption_toolchain_asm_test); + +		uTEST(ToolsTest::getOption_str_c_test); +		uTEST(ToolsTest::getOption_str_cxx_test); +		uTEST(ToolsTest::getOption_str_ld_test); +		uTEST(ToolsTest::getOption_str_ar_test); +		uTEST(ToolsTest::getOption_str_asm_test); + +		uTEST(ToolsTest::to_strings_c_test); +		uTEST(ToolsTest::to_strings_cxx_test); +		uTEST(ToolsTest::to_strings_ld_test); +		uTEST(ToolsTest::to_strings_ar_test); +		uTEST(ToolsTest::to_strings_asm_test); +	} + +	void getToolChain_test() +	{ +		// +		// gcc +		// +		uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/gcc")); +		uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/gcc-10")); +		uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/x86_64-pc-linux-gnu-g++-9.3.0")); + +		uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/g++")); +		uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/g++-10")); +		uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/x86_64-pc-linux-gnu-g++-9.3.0")); + +		// +		// clang +		// +		uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang")); +		uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang-16")); +		uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/lib/llvm/16/bin/i686-pc-linux-gnu-clang-16")); + +		uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang++")); +		uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang++-16")); +		uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/lib/llvm/16/bin/i686-pc-linux-gnu-clang++-16")); +	} + + +	void getOption_toolchain_c_test() +	{ +		std::vector<std::string> exp; +		std::vector<std::string> act; + +		// +		// gcc +		// +		exp = { "-o", "foo" }; +		act = c_option(ctor::toolchain::gcc, ctor::c_opt::output, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-g" }; +		act = c_option(ctor::toolchain::gcc, ctor::c_opt::debug); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Wall" }; +		act = c_option(ctor::toolchain::gcc, ctor::c_opt::warn_all); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Werror" }; +		act = c_option(ctor::toolchain::gcc, ctor::c_opt::warnings_as_errors); +		uASSERT_EQUAL(exp, act); + +		exp = { "-MMD" }; +		act = c_option(ctor::toolchain::gcc, ctor::c_opt::generate_dep_tree); +		uASSERT_EQUAL(exp, act); + +		exp = { "-c" }; +		act = c_option(ctor::toolchain::gcc, ctor::c_opt::no_link); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Ifoo" }; +		act = c_option(ctor::toolchain::gcc, ctor::c_opt::include_path, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-std=foo" }; +		act = c_option(ctor::toolchain::gcc, ctor::c_opt::c_std, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Ofoo" }; +		act = c_option(ctor::toolchain::gcc, ctor::c_opt::optimization, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-fPIC" }; +		act = c_option(ctor::toolchain::gcc, ctor::c_opt::position_independent_code); +		uASSERT_EQUAL(exp, act); + +		exp = { "-fPIE" }; +		act = c_option(ctor::toolchain::gcc, ctor::c_opt::position_independent_executable); +		uASSERT_EQUAL(exp, act); + +		exp = { "-foo" }; +		act = c_option(ctor::toolchain::gcc, ctor::c_opt::custom, "-foo"); +		uASSERT_EQUAL(exp, act); + +		// +		// clang +		// +		exp = { "-o", "foo" }; +		act = c_option(ctor::toolchain::clang, ctor::c_opt::output, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-g" }; +		act = c_option(ctor::toolchain::clang, ctor::c_opt::debug); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Wall" }; +		act = c_option(ctor::toolchain::clang, ctor::c_opt::warn_all); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Werror" }; +		act = c_option(ctor::toolchain::clang, ctor::c_opt::warnings_as_errors); +		uASSERT_EQUAL(exp, act); + +		exp = { "-MMD" }; +		act = c_option(ctor::toolchain::clang, ctor::c_opt::generate_dep_tree); +		uASSERT_EQUAL(exp, act); + +		exp = { "-c" }; +		act = c_option(ctor::toolchain::clang, ctor::c_opt::no_link); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Ifoo" }; +		act = c_option(ctor::toolchain::clang, ctor::c_opt::include_path, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-std=foo" }; +		act = c_option(ctor::toolchain::clang, ctor::c_opt::c_std, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Ofoo" }; +		act = c_option(ctor::toolchain::clang, ctor::c_opt::optimization, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-fPIC" }; +		act = c_option(ctor::toolchain::clang, ctor::c_opt::position_independent_code); +		uASSERT_EQUAL(exp, act); + +		exp = { "-fPIE" }; +		act = c_option(ctor::toolchain::clang, ctor::c_opt::position_independent_executable); +		uASSERT_EQUAL(exp, act); + +		exp = { "-foo" }; +		act = c_option(ctor::toolchain::clang, ctor::c_opt::custom, "-foo"); +		uASSERT_EQUAL(exp, act); + +		// +		// any +		// +		exp = { "{ctor::c_opt::output, \"foo\"}" }; +		act = c_option(ctor::toolchain::any, ctor::c_opt::output, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::c_opt::debug}" }; +		act = c_option(ctor::toolchain::any, ctor::c_opt::debug); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::c_opt::warn_all}" }; +		act = c_option(ctor::toolchain::any, ctor::c_opt::warn_all); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::c_opt::warnings_as_errors}" }; +		act = c_option(ctor::toolchain::any, ctor::c_opt::warnings_as_errors); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::c_opt::generate_dep_tree}" }; +		act = c_option(ctor::toolchain::any, ctor::c_opt::generate_dep_tree); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::c_opt::no_link}" }; +		act = c_option(ctor::toolchain::any, ctor::c_opt::no_link); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::c_opt::include_path, \"foo\"}" }; +		act = c_option(ctor::toolchain::any, ctor::c_opt::include_path, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::c_opt::c_std, \"foo\"}" }; +		act = c_option(ctor::toolchain::any, ctor::c_opt::c_std, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::c_opt::optimization, \"foo\"}" }; +		act = c_option(ctor::toolchain::any, ctor::c_opt::optimization, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::c_opt::position_independent_code}" }; +		act = c_option(ctor::toolchain::any, ctor::c_opt::position_independent_code); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::c_opt::position_independent_executable}" }; +		act = c_option(ctor::toolchain::any, ctor::c_opt::position_independent_executable); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::c_opt::custom, \"-foo\"}" }; +		act = c_option(ctor::toolchain::any, ctor::c_opt::custom, "-foo"); +		uASSERT_EQUAL(exp, act); +	} + +	void getOption_toolchain_cxx_test() +	{ +		std::vector<std::string> exp; +		std::vector<std::string> act; + +		// +		// gcc +		// +		exp = { "-o", "foo" }; +		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::output, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-g" }; +		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::debug); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Wall" }; +		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::warn_all); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Werror" }; +		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::warnings_as_errors); +		uASSERT_EQUAL(exp, act); + +		exp = { "-MMD" }; +		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::generate_dep_tree); +		uASSERT_EQUAL(exp, act); + +		exp = { "-c" }; +		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::no_link); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Ifoo" }; +		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::include_path, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-std=foo" }; +		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::cpp_std, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Ofoo" }; +		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::optimization, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-fPIC" }; +		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::position_independent_code); +		uASSERT_EQUAL(exp, act); + +		exp = { "-fPIE" }; +		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::position_independent_executable); +		uASSERT_EQUAL(exp, act); + +		exp = { "-foo" }; +		act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::custom, "-foo"); +		uASSERT_EQUAL(exp, act); + +		// +		// clang +		// +		exp = { "-o", "foo" }; +		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::output, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-g" }; +		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::debug); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Wall" }; +		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::warn_all); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Werror" }; +		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::warnings_as_errors); +		uASSERT_EQUAL(exp, act); + +		exp = { "-MMD" }; +		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::generate_dep_tree); +		uASSERT_EQUAL(exp, act); + +		exp = { "-c" }; +		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::no_link); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Ifoo" }; +		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::include_path, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-std=foo" }; +		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::cpp_std, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Ofoo" }; +		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::optimization, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-fPIC" }; +		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::position_independent_code); +		uASSERT_EQUAL(exp, act); + +		exp = { "-fPIE" }; +		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::position_independent_executable); +		uASSERT_EQUAL(exp, act); + +		exp = { "-foo" }; +		act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::custom, "-foo"); +		uASSERT_EQUAL(exp, act); + +		// +		// any +		// +		exp = { "{ctor::cxx_opt::output, \"foo\"}" }; +		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::output, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::cxx_opt::debug}" }; +		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::debug); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::cxx_opt::warn_all}" }; +		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::warn_all); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::cxx_opt::warnings_as_errors}" }; +		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::warnings_as_errors); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::cxx_opt::generate_dep_tree}" }; +		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::generate_dep_tree); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::cxx_opt::no_link}" }; +		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::no_link); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::cxx_opt::include_path, \"foo\"}" }; +		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::include_path, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::cxx_opt::cpp_std, \"foo\"}" }; +		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::cpp_std, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::cxx_opt::optimization, \"foo\"}" }; +		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::optimization, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::cxx_opt::position_independent_code}" }; +		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::position_independent_code); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::cxx_opt::position_independent_executable}" }; +		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::position_independent_executable); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::cxx_opt::custom, \"-foo\"}" }; +		act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::custom, "-foo"); +		uASSERT_EQUAL(exp, act); +	} + +	void getOption_toolchain_ld_test() +	{ +		std::vector<std::string> exp; +		std::vector<std::string> act; + +		// +		// gcc +		// +		exp = { "-o", "foo" }; +		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::output, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Wall" }; +		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::warn_all); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Werror" }; +		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::warnings_as_errors); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Lfoo" }; +		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::library_path, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-lfoo" }; +		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::link, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-std=foo" }; +		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::cpp_std, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-shared" }; +		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::build_shared); +		uASSERT_EQUAL(exp, act); + +		exp = { "-pthread" }; +		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::threads); +		uASSERT_EQUAL(exp, act); + +		exp = { "-fPIC" }; +		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::position_independent_code); +		uASSERT_EQUAL(exp, act); + +		exp = { "-fPIE" }; +		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::position_independent_executable); +		uASSERT_EQUAL(exp, act); + +		exp = { "-foo" }; +		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::custom, "-foo"); +		uASSERT_EQUAL(exp, act); + +		// +		// clang +		// +		exp = { "-o", "foo" }; +		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::output, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Wall" }; +		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::warn_all); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Werror" }; +		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::warnings_as_errors); +		uASSERT_EQUAL(exp, act); + +		exp = { "-Lfoo" }; +		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::library_path, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-lfoo" }; +		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::link, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-std=foo" }; +		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::cpp_std, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-shared" }; +		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::build_shared); +		uASSERT_EQUAL(exp, act); + +		exp = { "-pthread" }; +		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::threads); +		uASSERT_EQUAL(exp, act); + +		exp = { "-fPIC" }; +		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::position_independent_code); +		uASSERT_EQUAL(exp, act); + +		exp = { "-fPIE" }; +		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::position_independent_executable); +		uASSERT_EQUAL(exp, act); + +		exp = { "-foo" }; +		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::custom, "-foo"); +		uASSERT_EQUAL(exp, act); + +		// +		// any +		// +		exp = { "{ctor::ld_opt::output, \"foo\"}" }; +		act = ld_option(ctor::toolchain::any, ctor::ld_opt::output, "foo"); +		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); + +		exp = { "{ctor::ld_opt::warnings_as_errors}" }; +		act = ld_option(ctor::toolchain::any, ctor::ld_opt::warnings_as_errors); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::ld_opt::library_path, \"foo\"}" }; +		act = ld_option(ctor::toolchain::any, ctor::ld_opt::library_path, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::ld_opt::link, \"foo\"}" }; +		act = ld_option(ctor::toolchain::any, ctor::ld_opt::link, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::ld_opt::cpp_std, \"foo\"}" }; +		act = ld_option(ctor::toolchain::any, ctor::ld_opt::cpp_std, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::ld_opt::build_shared}" }; +		act = ld_option(ctor::toolchain::any, ctor::ld_opt::build_shared); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::ld_opt::threads}" }; +		act = ld_option(ctor::toolchain::any, ctor::ld_opt::threads); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::ld_opt::position_independent_code}" }; +		act = ld_option(ctor::toolchain::any, ctor::ld_opt::position_independent_code); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::ld_opt::position_independent_executable}" }; +		act = ld_option(ctor::toolchain::any, ctor::ld_opt::position_independent_executable); +		uASSERT_EQUAL(exp, act); + +		exp = { "{ctor::ld_opt::custom, \"-foo\"}" }; +		act = ld_option(ctor::toolchain::any, ctor::ld_opt::custom, "-foo"); +		uASSERT_EQUAL(exp, act); +	} + +	void getOption_toolchain_ar_test() +	{ +		std::vector<std::string> exp; +		std::vector<std::string> act; + +		// +		// gcc +		// +		exp = { "-r" }; +		act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::replace); +		uASSERT_EQUAL(exp, act); + +		exp = { "-s" }; +		act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::add_index); +		uASSERT_EQUAL(exp, act); + +		exp = { "-c" }; +		act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::create); +		uASSERT_EQUAL(exp, act); + +		exp = { "foo" }; +		act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::output, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-foo" }; +		act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::custom, "-foo"); +		uASSERT_EQUAL(exp, act); + +		// +		// clang +		// +		exp = { "-r" }; +		act = ar_option(ctor::toolchain::clang, ctor::ar_opt::replace); +		uASSERT_EQUAL(exp, act); + +		exp = { "-s" }; +		act = ar_option(ctor::toolchain::clang, ctor::ar_opt::add_index); +		uASSERT_EQUAL(exp, act); + +		exp = { "-c" }; +		act = ar_option(ctor::toolchain::clang, ctor::ar_opt::create); +		uASSERT_EQUAL(exp, act); + +		exp = { "foo" }; +		act = ar_option(ctor::toolchain::clang, ctor::ar_opt::output, "foo"); +		uASSERT_EQUAL(exp, act); + +		exp = { "-foo" }; +		act = ar_option(ctor::toolchain::clang, ctor::ar_opt::custom, "-foo"); +		uASSERT_EQUAL(exp, act); + +		// +		// any +		// +		exp = { "{ctor::ar_opt::custom, \"-foo\"}" }; +		act = ar_option(ctor::toolchain::any, ctor::ar_opt::custom, "-foo"); +		uASSERT_EQUAL(exp, act); +} + +	void getOption_toolchain_asm_test() +	{ +		std::vector<std::string> exp; +		std::vector<std::string> act; + +		// +		// gcc +		// +		exp = { "-foo" }; +		act = asm_option(ctor::toolchain::gcc, ctor::asm_opt::custom, "-foo"); +		uASSERT_EQUAL(exp, act); + +		// +		// clang +		// +		exp = { "-foo" }; +		act = asm_option(ctor::toolchain::clang, ctor::asm_opt::custom, "-foo"); +		uASSERT_EQUAL(exp, act); + +		// +		// any +		// +		exp = { "{ctor::asm_opt::custom, \"-foo\"}" }; +		act = asm_option(ctor::toolchain::any, ctor::asm_opt::custom, "-foo"); +		uASSERT_EQUAL(exp, act); +	} + + +	void getOption_str_c_test() +	{ +		ctor::c_flag exp(""); +		ctor::c_flag act(""); + +		// +		// gcc +		// +		exp = { ctor::c_opt::include_path, "foo" }; +		act = c_option("-Ifoo", ctor::toolchain::gcc); +		uASSERT_EQUAL(exp, act); + +		exp = { ctor::c_opt::custom, "foo" }; +		act = c_option("foo", ctor::toolchain::gcc); +		uASSERT_EQUAL(exp, act); + +		// +		// clang +		// +		exp = { ctor::c_opt::include_path, "foo" }; +		act = c_option("-Ifoo", ctor::toolchain::clang); +		uASSERT_EQUAL(exp, act); + +		exp = { ctor::c_opt::custom, "foo" }; +		act = c_option("foo", ctor::toolchain::clang); +		uASSERT_EQUAL(exp, act); +	} + +	void getOption_str_cxx_test() +	{ +		ctor::cxx_flag exp(""); +		ctor::cxx_flag act(""); + +		// +		// gcc +		// +		exp = { ctor::cxx_opt::include_path, "foo" }; +		act = cxx_option("-Ifoo", ctor::toolchain::gcc); +		uASSERT_EQUAL(exp, act); + +		exp = { ctor::cxx_opt::custom, "foo" }; +		act = cxx_option("foo", ctor::toolchain::gcc); +		uASSERT_EQUAL(exp, act); + +		// +		// clang +		// +		exp = { ctor::cxx_opt::include_path, "foo" }; +		act = cxx_option("-Ifoo", ctor::toolchain::clang); +		uASSERT_EQUAL(exp, act); + +		exp = { ctor::cxx_opt::custom, "foo" }; +		act = cxx_option("foo", ctor::toolchain::clang); +		uASSERT_EQUAL(exp, act); +	} + +	void getOption_str_ld_test() +	{ +		ctor::ld_flag exp(""); +		ctor::ld_flag act(""); + +		// +		// gcc +		// +		exp = { ctor::ld_opt::library_path, "foo" }; +		act = ld_option("-Lfoo", ctor::toolchain::gcc); +		uASSERT_EQUAL(exp, act); + +		exp = { ctor::ld_opt::custom, "foo" }; +		act = ld_option("foo", ctor::toolchain::gcc); +		uASSERT_EQUAL(exp, act); + +		// +		// clang +		// +		exp = { ctor::ld_opt::library_path, "foo" }; +		act = ld_option("-Lfoo", ctor::toolchain::clang); +		uASSERT_EQUAL(exp, act); + +		exp = { ctor::ld_opt::custom, "foo" }; +		act = ld_option("foo", ctor::toolchain::clang); +		uASSERT_EQUAL(exp, act); +	} + +	void getOption_str_ar_test() +	{ +		ctor::ar_flag exp(""); +		ctor::ar_flag act(""); + +		// +		// gcc +		// +		exp = { ctor::ar_opt::custom, "foo" }; +		act = ar_option("foo", ctor::toolchain::gcc); +		uASSERT_EQUAL(exp, act); + +		// +		// clang +		// +		exp = { ctor::ar_opt::custom, "foo" }; +		act = ar_option("foo", ctor::toolchain::clang); +		uASSERT_EQUAL(exp, act); +	} + +	void getOption_str_asm_test() +	{ +		ctor::asm_flag exp(""); +		ctor::asm_flag act(""); + +		// +		// gcc +		// +		exp = { ctor::asm_opt::custom, "foo" }; +		act = asm_option("foo", ctor::toolchain::gcc); +		uASSERT_EQUAL(exp, act); + +		// +		// clang +		// +		exp = { ctor::asm_opt::custom, "foo" }; +		act = asm_option("foo", ctor::toolchain::clang); +		uASSERT_EQUAL(exp, act); +	} + + +	void to_strings_c_test() +	{ +		std::vector<std::string> exp; +		std::vector<std::string> act; + +		// Mismatching toolchain (required vs actual) results in no output +		// otherwise to_strings is just a proxy for c_option +		act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::c_opt::no_link}); +		uASSERT_EQUAL(exp, act); +	} + +	void to_strings_cxx_test() +	{ +		std::vector<std::string> exp; +		std::vector<std::string> act; + +		// Mismatching toolchain (required vs actual) results in no output +		// otherwise to_strings is just a proxy for cxx_option +		act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::cxx_opt::no_link}); +		uASSERT_EQUAL(exp, act); +	} + +	void to_strings_ld_test() +	{ +		std::vector<std::string> exp; +		std::vector<std::string> act; + +		// 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::threads}); +		uASSERT_EQUAL(exp, act); +	} + +	void to_strings_ar_test() +	{ +		std::vector<std::string> exp; +		std::vector<std::string> act; + +		// Mismatching toolchain (required vs actual) results in no output +		// otherwise to_strings is just a proxy for ar_option +		act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::ar_opt::custom, "foo"}); +		uASSERT_EQUAL(exp, act); +	} + +	void to_strings_asm_test() +	{ +		std::vector<std::string> exp; +		std::vector<std::string> act; + +		// Mismatching toolchain (required vs actual) results in no output +		// otherwise to_strings is just a proxy for asm_option +		act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::asm_opt::custom, "foo"}); +		uASSERT_EQUAL(exp, act); +	} +}; + +// Registers the fixture into the 'registry' +static ToolsTest test; diff --git a/test/uunit b/test/uunit -Subproject bc078da645412c6b36ef59e635d6c35d11088c9 +Subproject 0f371777e02dd068f9675a05a29230221d5d6a7  | 
