elna/tests/tester.cpp

264 lines
8.8 KiB
C++
Raw Normal View History

2024-03-14 08:52:45 +01:00
#include "elna/tester.hpp"
2024-02-18 11:26:57 +01:00
2024-02-22 21:29:25 +01:00
#include <iostream>
#include <fstream>
#include <algorithm>
#include <boost/process/env.hpp>
2024-03-14 08:52:45 +01:00
2024-02-22 21:29:25 +01:00
namespace elna
2024-02-18 11:26:57 +01:00
{
2024-02-22 21:29:25 +01:00
std::uint32_t test_results::total() const noexcept
2024-02-18 11:26:57 +01:00
{
return m_total;
}
2024-02-22 21:29:25 +01:00
std::uint32_t test_results::passed() const noexcept
2024-02-18 11:26:57 +01:00
{
return m_passed;
}
2024-02-22 21:29:25 +01:00
std::uint32_t test_results::failed() const noexcept
2024-02-18 11:26:57 +01:00
{
return m_total - m_passed;
}
2024-02-22 21:29:25 +01:00
int test_results::exit_code() const noexcept
2024-02-18 11:26:57 +01:00
{
return m_total == m_passed ? EXIT_SUCCESS : EXIT_FAILURE;
}
2024-03-14 08:52:45 +01:00
void test_results::add_exit_code(const test_status status) noexcept
2024-02-18 11:26:57 +01:00
{
++m_total;
2024-03-14 08:52:45 +01:00
if (status == test_status::successful)
2024-02-18 11:26:57 +01:00
{
++m_passed;
}
}
2024-03-03 13:11:39 +01:00
static std::filesystem::path in_build_directory()
{
return "build/riscv";
}
2024-02-25 15:16:19 +01:00
static std::string in_build_directory(const std::filesystem::path& path)
{
2024-03-03 13:11:39 +01:00
return in_build_directory() / path;
2024-02-25 15:16:19 +01:00
}
static std::filesystem::path in_actual_directory()
{
return "build/tests";
}
2024-03-28 00:55:13 +01:00
boost::process::v2::process_stdio get_output_streams(const std::uint8_t stream_number,
boost::asio::readable_pipe& read_pipe)
{
if (stream_number == 1)
{
return boost::process::v2::process_stdio{ nullptr, read_pipe };
}
if (stream_number == 2)
{
return boost::process::v2::process_stdio{ nullptr, {}, read_pipe };
}
return boost::process::v2::process_stdio{ nullptr, read_pipe, read_pipe };
}
int run_for_output(boost::asio::io_context& context, const std::filesystem::path& log_path,
2024-03-28 00:55:13 +01:00
const std::filesystem::path& binary, std::initializer_list<boost::string_view> arguments)
2024-03-23 14:53:26 +01:00
{
boost::asio::readable_pipe read_pipe{ context };
std::string output;
boost::asio::dynamic_string_buffer buffer = boost::asio::dynamic_buffer(output);
boost::system::error_code ec;
2024-03-28 00:55:13 +01:00
boost::process::v2::process_stdio process_stdio;
boost::process::v2::process elna_child(context, binary, arguments,
boost::process::v2::process_stdio{ nullptr, read_pipe, 1 });
std::ofstream log_file{ log_path };
2024-03-23 14:53:26 +01:00
do
{
std::size_t transferred = read_pipe.read_some(buffer.prepare(512), ec);
if (transferred == 0)
{
break;
}
buffer.commit(transferred);
log_file.write(boost::asio::buffer_cast<const char *>(buffer.data()), buffer.size());
2024-03-23 14:53:26 +01:00
buffer.consume(transferred);
}
while (ec == boost::system::errc::success);
return elna_child.wait();
2024-03-23 14:53:26 +01:00
}
std::string find_object(const std::vector<std::filesystem::path>& environment, const std::string& object)
{
auto variable = std::find(environment.cbegin(), environment.cend(), object);
for (const auto& variable : environment)
{
auto full_path = variable / object;
if (std::filesystem::exists(full_path))
{
return full_path.native();
}
}
return object;
}
test_status build_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry)
2024-02-25 15:16:19 +01:00
{
const std::filesystem::path test_filename = test_entry.path().filename();
std::filesystem::path test_binary = in_build_directory(test_filename);
std::filesystem::path test_object = test_binary;
std::filesystem::path test_log = test_binary;
2024-02-25 15:16:19 +01:00
test_binary.replace_extension();
test_object.replace_extension(".o");
test_log.replace_extension(".log");
2024-02-25 15:16:19 +01:00
std::filesystem::remove(test_binary);
std::filesystem::remove(test_object);
auto result_code = run_for_output(context, test_log, "./build/bin/elna",
2024-03-23 14:53:26 +01:00
{ "-o", test_object.string(), test_entry.path().string() });
if (result_code != 0)
2024-02-25 15:16:19 +01:00
{
return test_status::compile_failed;
2024-02-25 15:16:19 +01:00
}
test_status result = result_code == 0 ? test_status::successful : test_status::compile_failed;
std::vector<std::filesystem::path> environment;
std::vector<std::string> linker_arguments = { "-o", test_binary.string() };
for (const auto segment : boost::this_process::environment()["LIBRARY_PATH"].to_vector())
{
linker_arguments.push_back("-L" + segment);
environment.push_back(std::filesystem::path(segment));
}
linker_arguments.push_back(find_object(environment, "crt0.o"));
linker_arguments.push_back(find_object(environment, "crtbegin.o"));
linker_arguments.push_back(test_object.string());
linker_arguments.insert(linker_arguments.cend(),
{ "--start-group", "-lgcc", "-lc", "-lgloss", "--end-group" });
linker_arguments.push_back(find_object(environment, "crtend.o"));
boost::process::v2::execute(boost::process::v2::process(context, boost::process::search_path("ld"),
linker_arguments
2024-03-23 14:53:26 +01:00
));
return result;
2024-02-25 15:16:19 +01:00
}
static test_status check_expectation(const std::filesystem::path& expectation_path,
const std::filesystem::path& actual_directory)
2024-02-22 21:29:25 +01:00
{
std::filesystem::path log_filename = expectation_path.filename();
log_filename.replace_extension(".log");
2024-02-22 21:29:25 +01:00
std::filesystem::path test_log = actual_directory / log_filename;
2024-02-22 21:29:25 +01:00
test_log.replace_extension(".log");
2024-02-22 21:29:25 +01:00
boost::process::child diff(
2024-03-14 08:52:45 +01:00
boost::process::search_path("diff"), "-Nur", "--color",
expectation_path, test_log
2024-02-22 21:29:25 +01:00
);
diff.wait();
2024-03-14 08:52:45 +01:00
if (diff.exit_code() == 0)
{
return test_status::successful;
2024-03-14 08:52:45 +01:00
}
return test_status::expectation_failed;
2024-03-14 08:52:45 +01:00
}
void run_test(boost::asio::io_context& context, const std::filesystem::path& test_binary)
2024-03-14 08:52:45 +01:00
{
std::filesystem::path test_log = test_binary;
test_log.replace_extension(".log");
2024-03-14 08:52:45 +01:00
run_for_output(context, test_log, boost::process::search_path("qemu-riscv32"),
2024-03-23 14:53:26 +01:00
{ test_binary.string() });
2024-02-22 21:29:25 +01:00
}
2024-02-18 11:26:57 +01:00
2024-02-22 21:29:25 +01:00
static test_results run_in_path(const std::filesystem::path test_directory)
2024-02-18 11:26:57 +01:00
{
2024-02-22 21:29:25 +01:00
test_results results;
2024-03-23 14:53:26 +01:00
boost::asio::io_context context;
2024-02-22 21:29:25 +01:00
for (const auto& test_entry : std::filesystem::directory_iterator(test_directory))
2024-02-18 11:26:57 +01:00
{
2024-02-22 21:29:25 +01:00
if (test_entry.path().extension() != ".eln" || !test_entry.is_regular_file())
{
continue;
}
test_status result;
2024-02-25 15:16:19 +01:00
2024-03-14 08:52:45 +01:00
std::cout << "Running " << test_entry << std::endl;
2024-02-25 15:16:19 +01:00
try
{
build_test(context, test_entry);
2024-02-25 15:16:19 +01:00
}
catch (const boost::process::process_error& exception)
{
test_status::build_failed;
std::cout << exception.what() << std::endl;
}
}
for (const auto& executable_test : std::filesystem::directory_iterator(in_build_directory()))
{
if (executable_test.path().has_extension())
{
continue;
2024-02-25 15:16:19 +01:00
}
run_test(context, executable_test.path());
}
for (const auto& expectation_entry : std::filesystem::directory_iterator(test_directory / "failures"))
{
auto test_entry = test_directory / expectation_entry.path().filename();
test_entry.replace_extension(".eln");
auto result = check_expectation(expectation_entry.path(), in_build_directory());
print_result(test_entry, result);
results.add_exit_code(result);
}
for (const auto& expectation_entry : std::filesystem::directory_iterator(test_directory / "expectations"))
{
auto test_entry = test_directory / expectation_entry.path().filename();
test_entry.replace_extension(".eln");
auto result = check_expectation(expectation_entry.path(), in_actual_directory());
2024-03-14 08:52:45 +01:00
print_result(test_entry, result);
results.add_exit_code(result);
2024-02-18 11:26:57 +01:00
}
2024-02-22 21:29:25 +01:00
return results;
2024-02-18 11:26:57 +01:00
}
2024-03-14 08:52:45 +01:00
void print_result(const std::filesystem::path& test_entry, const test_status& result)
2024-03-14 08:52:45 +01:00
{
if (result != test_status::successful)
2024-03-14 08:52:45 +01:00
{
std::cout << test_entry << " failed." << std::endl;
}
}
2024-02-22 21:29:25 +01:00
};
2024-02-18 11:26:57 +01:00
int main()
{
2024-03-03 13:11:39 +01:00
std::filesystem::create_directory(elna::in_build_directory());
std::filesystem::create_directory(elna::in_actual_directory());
2024-02-18 11:26:57 +01:00
std::cout << "Run all tests and check the results" << std::endl;
std::filesystem::path test_directory{ "tests" };
2024-02-22 21:29:25 +01:00
const auto results = elna::run_in_path(test_directory);
2024-02-18 11:26:57 +01:00
std::cout << std::endl;
std::cout << results.total() << " tests run, "
<< results.passed() << " passed, "
<< results.failed() << " failed." << std::endl;
return results.exit_code();
}