#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 = { "-s" };
		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::strip);
		uASSERT_EQUAL(exp, act);

		exp = { "-Wall" };
		act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::warn_all);
		uASSERT_EQUAL(exp, act);

		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 = { "-s" };
		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::strip);
		uASSERT_EQUAL(exp, act);

		exp = { "-Wall" };
		act = ld_option(ctor::toolchain::clang, ctor::ld_opt::warn_all);
		uASSERT_EQUAL(exp, act);

		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::strip}" };
		act = ld_option(ctor::toolchain::any, ctor::ld_opt::strip);
		uASSERT_EQUAL(exp, act);

		exp = { "{ctor::ld_opt::warn_all}" };
		act = ld_option(ctor::toolchain::any, ctor::ld_opt::warn_all);
		uASSERT_EQUAL(exp, act);

		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::strip});
		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;