Add missing virtual distructors
This commit is contained in:
		@@ -9,9 +9,6 @@ find_package(Boost COMPONENTS program_options REQUIRED)
 | 
			
		||||
find_package(FLEX)
 | 
			
		||||
include_directories(${Boost_INCLUDE_DIR})
 | 
			
		||||
 | 
			
		||||
add_executable(tester tests/tester.cpp include/elna/tester.hpp)
 | 
			
		||||
target_include_directories(tester PRIVATE include)
 | 
			
		||||
 | 
			
		||||
FLEX_TARGET(scanner source/scanner.l ${CMAKE_CURRENT_BINARY_DIR}/scanner.cpp)
 | 
			
		||||
 | 
			
		||||
add_executable(elna cli/main.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -153,6 +153,7 @@ namespace elna::source
 | 
			
		||||
        explicit node(const position position);
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~node() noexcept = default;
 | 
			
		||||
        virtual void accept(parser_visitor *) = 0;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,8 @@ namespace elna::source
 | 
			
		||||
        error(const std::filesystem::path& path, const position position);
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~error() noexcept = default;
 | 
			
		||||
 | 
			
		||||
        /// Error text.
 | 
			
		||||
        virtual std::string what() const = 0;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,44 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
 | 
			
		||||
#define BOOST_PROCESS_USE_STD_FS
 | 
			
		||||
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include <boost/process.hpp>
 | 
			
		||||
#include <boost/process/v2.hpp>
 | 
			
		||||
 | 
			
		||||
namespace elna
 | 
			
		||||
{
 | 
			
		||||
    enum class test_status
 | 
			
		||||
    {
 | 
			
		||||
        successful,
 | 
			
		||||
        compile_failed,
 | 
			
		||||
        expectation_failed,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class test_results final
 | 
			
		||||
    {
 | 
			
		||||
        std::uint32_t m_total{ 0 };
 | 
			
		||||
        std::uint32_t m_passed{ 0 };
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        test_results() = default;
 | 
			
		||||
 | 
			
		||||
        std::uint32_t total() const noexcept;
 | 
			
		||||
        std::uint32_t passed() const noexcept;
 | 
			
		||||
        std::uint32_t failed() const noexcept;
 | 
			
		||||
 | 
			
		||||
        int exit_code() const noexcept;
 | 
			
		||||
        void add_exit_code(const test_status result) noexcept;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    boost::process::v2::process_stdio get_output_streams(const std::uint8_t stream_number,
 | 
			
		||||
            boost::asio::readable_pipe& read_pipe);
 | 
			
		||||
    int run_for_output(boost::asio::io_context& context, const std::uint8_t stream_number,
 | 
			
		||||
            const std::filesystem::path& binary, std::initializer_list<boost::string_view> arguments);
 | 
			
		||||
    test_status build_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry);
 | 
			
		||||
    void run_test(boost::asio::io_context& context, const std::filesystem::path& test_entry);
 | 
			
		||||
    void print_result(const std::filesystem::path& test_entry, const test_status& result);
 | 
			
		||||
}
 | 
			
		||||
@@ -43,8 +43,8 @@ namespace elna::source
 | 
			
		||||
    void intermediate_code::clear()
 | 
			
		||||
    {
 | 
			
		||||
        this->instructions.clear();
 | 
			
		||||
        std::uint32_t variable_counter = 1;
 | 
			
		||||
        std::uint32_t label_counter = 0;
 | 
			
		||||
        this->m_variable_counter = 1;
 | 
			
		||||
        this->m_label_counter = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<quadruple>::iterator intermediate_code::begin()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										319
									
								
								tests/tester.cpp
									
									
									
									
									
								
							
							
						
						
									
										319
									
								
								tests/tester.cpp
									
									
									
									
									
								
							@@ -1,319 +0,0 @@
 | 
			
		||||
#include "elna/tester.hpp"
 | 
			
		||||
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <boost/process/env.hpp>
 | 
			
		||||
 | 
			
		||||
namespace elna
 | 
			
		||||
{
 | 
			
		||||
    std::uint32_t test_results::total() const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        return m_total;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::uint32_t test_results::passed() const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        return m_passed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::uint32_t test_results::failed() const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        return m_total - m_passed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int test_results::exit_code() const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        return m_total == m_passed ? EXIT_SUCCESS : EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void test_results::add_exit_code(const test_status status) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        ++m_total;
 | 
			
		||||
        if (status == test_status::successful)
 | 
			
		||||
        {
 | 
			
		||||
            ++m_passed;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static std::filesystem::path in_build_directory()
 | 
			
		||||
    {
 | 
			
		||||
        return "build/riscv";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static std::string in_build_directory(const std::filesystem::path& path)
 | 
			
		||||
    {
 | 
			
		||||
        return in_build_directory() / path;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static std::filesystem::path in_actual_directory()
 | 
			
		||||
    {
 | 
			
		||||
        return "build/tests";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static std::filesystem::path in_root_directory()
 | 
			
		||||
    {
 | 
			
		||||
        return "build/root";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static std::filesystem::path in_root_directory(const std::filesystem::path& path)
 | 
			
		||||
    {
 | 
			
		||||
        return in_root_directory() / path;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
            const std::filesystem::path& binary, std::initializer_list<boost::string_view> arguments)
 | 
			
		||||
    {
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
        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 };
 | 
			
		||||
        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());
 | 
			
		||||
            buffer.consume(transferred);
 | 
			
		||||
        }
 | 
			
		||||
        while (ec == boost::system::errc::success);
 | 
			
		||||
 | 
			
		||||
        return elna_child.wait();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    test_status build_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry)
 | 
			
		||||
    {
 | 
			
		||||
        const std::filesystem::path test_filename = test_entry.path().filename();
 | 
			
		||||
 | 
			
		||||
        std::filesystem::path test_binary = in_root_directory("tests" / test_filename);
 | 
			
		||||
        std::filesystem::path test_object = in_build_directory(test_filename);
 | 
			
		||||
        std::filesystem::path test_log = in_build_directory(test_filename);
 | 
			
		||||
        test_binary.replace_extension();
 | 
			
		||||
        test_object.replace_extension(".o");
 | 
			
		||||
        test_log.replace_extension(".log");
 | 
			
		||||
 | 
			
		||||
        std::filesystem::remove(test_binary);
 | 
			
		||||
        std::filesystem::remove(test_object);
 | 
			
		||||
 | 
			
		||||
        auto result_code = run_for_output(context, test_log, "./build/bin/elna",
 | 
			
		||||
                { "-o", test_object.string(), test_entry.path().string() });
 | 
			
		||||
        if (result_code != 0)
 | 
			
		||||
        {
 | 
			
		||||
            return test_status::compile_failed;
 | 
			
		||||
        }
 | 
			
		||||
        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() };
 | 
			
		||||
 | 
			
		||||
        linker_arguments.push_back(test_object.string());
 | 
			
		||||
 | 
			
		||||
        boost::process::v2::execute(boost::process::v2::process(context, boost::process::search_path("ld"),
 | 
			
		||||
                    linker_arguments
 | 
			
		||||
        ));
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static test_status check_expectation(const std::filesystem::path& expectation_path,
 | 
			
		||||
            const std::filesystem::path& actual_directory)
 | 
			
		||||
    {
 | 
			
		||||
        std::filesystem::path log_filename = expectation_path.filename();
 | 
			
		||||
        log_filename.replace_extension(".log");
 | 
			
		||||
 | 
			
		||||
        std::filesystem::path test_log = actual_directory / log_filename;
 | 
			
		||||
 | 
			
		||||
        test_log.replace_extension(".log");
 | 
			
		||||
        boost::process::child diff(
 | 
			
		||||
                boost::process::search_path("diff"), "-Nur", "--color",
 | 
			
		||||
                expectation_path, test_log
 | 
			
		||||
        );
 | 
			
		||||
        diff.wait();
 | 
			
		||||
 | 
			
		||||
        if (diff.exit_code() == 0)
 | 
			
		||||
        {
 | 
			
		||||
            return test_status::successful;
 | 
			
		||||
        }
 | 
			
		||||
        return test_status::expectation_failed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void run_test(boost::asio::io_context& context, const std::filesystem::path& test_binary)
 | 
			
		||||
    {
 | 
			
		||||
        std::filesystem::path test_log = test_binary;
 | 
			
		||||
        test_log.replace_extension(".log");
 | 
			
		||||
 | 
			
		||||
        run_for_output(context, test_log, boost::process::search_path("qemu-riscv32"),
 | 
			
		||||
                { test_binary.string() });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void cpio_archive(boost::asio::io_context& context)
 | 
			
		||||
    {
 | 
			
		||||
        boost::asio::readable_pipe read_pipe{ context };
 | 
			
		||||
        boost::asio::writable_pipe write_pipe{ context };
 | 
			
		||||
        std::string output;
 | 
			
		||||
        boost::asio::dynamic_string_buffer buffer = boost::asio::dynamic_buffer(output);
 | 
			
		||||
        boost::system::error_code ec;
 | 
			
		||||
        std::ofstream archive_output{ "build/root.cpio", std::ios::binary };
 | 
			
		||||
        boost::process::v2::process_stdio process_stdio;
 | 
			
		||||
        auto current_path = std::filesystem::current_path();
 | 
			
		||||
 | 
			
		||||
        std::filesystem::current_path(in_root_directory());
 | 
			
		||||
        boost::process::v2::process cpio_child(context, boost::process::search_path("cpio"),
 | 
			
		||||
                { "-o", "--format=newc" },
 | 
			
		||||
                boost::process::v2::process_stdio{ write_pipe, read_pipe, nullptr });
 | 
			
		||||
 | 
			
		||||
        for (const auto& entry : std::filesystem::recursive_directory_iterator("."))
 | 
			
		||||
        {
 | 
			
		||||
            auto entry_path = entry.path().string() + "\n";
 | 
			
		||||
            auto entry_iterator = std::cbegin(entry_path);
 | 
			
		||||
            std::size_t written{ 0 };
 | 
			
		||||
 | 
			
		||||
            while (entry_iterator != std::cend(entry_path))
 | 
			
		||||
            {
 | 
			
		||||
                std::size_t written = write_pipe.write_some(boost::asio::buffer(entry_iterator.base(),
 | 
			
		||||
                            std::distance(entry_iterator, std::cend(entry_path))));
 | 
			
		||||
                std::advance(entry_iterator, written);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        write_pipe.close();
 | 
			
		||||
        do
 | 
			
		||||
        {
 | 
			
		||||
            std::size_t transferred = read_pipe.read_some(buffer.prepare(512), ec);
 | 
			
		||||
 | 
			
		||||
            if (transferred == 0)
 | 
			
		||||
            {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            buffer.commit(transferred);
 | 
			
		||||
            archive_output.write(boost::asio::buffer_cast<const char *>(buffer.data()), buffer.size());
 | 
			
		||||
            buffer.consume(transferred);
 | 
			
		||||
        }
 | 
			
		||||
        while (ec == boost::system::errc::success);
 | 
			
		||||
 | 
			
		||||
        std::filesystem::current_path(current_path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void run_vm(boost::asio::io_context& context)
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<std::string> arguments = {
 | 
			
		||||
            "-nographic",
 | 
			
		||||
            "-M", "virt",
 | 
			
		||||
            "-bios", "default",
 | 
			
		||||
            "-kernel", "build/tools/linux-5.15.159/arch/riscv/boot/Image",
 | 
			
		||||
            "-append", "quiet",
 | 
			
		||||
            "-initrd", "build/root.cpio"
 | 
			
		||||
        };
 | 
			
		||||
        auto process = boost::process::v2::process(context,
 | 
			
		||||
                boost::process::search_path("qemu-system-riscv32"), arguments);
 | 
			
		||||
        boost::process::v2::execute(std::move(process));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void create_init()
 | 
			
		||||
    {
 | 
			
		||||
        std::filesystem::copy("build/tools/init", in_root_directory());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static test_results run_in_path(const std::filesystem::path test_directory)
 | 
			
		||||
    {
 | 
			
		||||
        test_results results;
 | 
			
		||||
        boost::asio::io_context context;
 | 
			
		||||
 | 
			
		||||
        for (const auto& test_entry : std::filesystem::directory_iterator(test_directory))
 | 
			
		||||
        {
 | 
			
		||||
            if (test_entry.path().extension() != ".eln" || !test_entry.is_regular_file())
 | 
			
		||||
            {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            test_status result;
 | 
			
		||||
 | 
			
		||||
            std::cout << "Compiling " << test_entry << std::endl;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                build_test(context, test_entry);
 | 
			
		||||
            }
 | 
			
		||||
            catch (const boost::process::process_error& exception)
 | 
			
		||||
            {
 | 
			
		||||
                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;
 | 
			
		||||
            }
 | 
			
		||||
            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);
 | 
			
		||||
        }
 | 
			
		||||
        std::filesystem::copy(test_directory / "expectations", in_root_directory("expectations"),
 | 
			
		||||
                std::filesystem::copy_options::recursive);
 | 
			
		||||
        create_init();
 | 
			
		||||
        cpio_archive(context);
 | 
			
		||||
        run_vm(context);
 | 
			
		||||
 | 
			
		||||
        return results;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void print_result(const std::filesystem::path& test_entry, const test_status& result)
 | 
			
		||||
    {
 | 
			
		||||
        if (result != test_status::successful)
 | 
			
		||||
        {
 | 
			
		||||
            std::cout << test_entry << " failed." << std::endl;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main()
 | 
			
		||||
{
 | 
			
		||||
    std::filesystem::create_directory(elna::in_build_directory());
 | 
			
		||||
    std::filesystem::create_directory(elna::in_actual_directory());
 | 
			
		||||
 | 
			
		||||
    std::filesystem::remove_all(elna::in_root_directory());
 | 
			
		||||
    std::filesystem::create_directory(elna::in_root_directory());
 | 
			
		||||
    std::filesystem::create_directory(elna::in_root_directory("expectations"));
 | 
			
		||||
    std::filesystem::create_directory(elna::in_root_directory("tests"));
 | 
			
		||||
 | 
			
		||||
    auto current_environment = boost::this_process::environment();
 | 
			
		||||
    current_environment["PATH"] += "./build/tools/sysroot/bin";
 | 
			
		||||
 | 
			
		||||
    std::cout << "Run all tests and check the results" << std::endl;
 | 
			
		||||
    std::filesystem::path test_directory{ "tests" };
 | 
			
		||||
    const auto results = elna::run_in_path(test_directory);
 | 
			
		||||
 | 
			
		||||
    std::cout << std::endl;
 | 
			
		||||
    std::cout << results.total() << " tests run, "
 | 
			
		||||
        << results.passed() << " passed, "
 | 
			
		||||
        << results.failed() << " failed." << std::endl;
 | 
			
		||||
 | 
			
		||||
    return results.exit_code();
 | 
			
		||||
}
 | 
			
		||||
@@ -96,16 +96,15 @@ async function runVM(cpioImage) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function runInDirectory (directoryPath) {
 | 
			
		||||
  const testSources = await glob(path.join(directoryPath, '*.eln'))
 | 
			
		||||
  let failed = 0
 | 
			
		||||
 | 
			
		||||
  for (const testEntry of testSources) {
 | 
			
		||||
    console.log(`Compiling ${path.basename(testEntry)}.`)
 | 
			
		||||
 | 
			
		||||
  for (const testEntry of await glob(path.join(directoryPath, '*.eln'))) {
 | 
			
		||||
    const parsedPath = path.parse(testEntry)
 | 
			
		||||
 | 
			
		||||
    console.log(`Compiling ${parsedPath.base}.`)
 | 
			
		||||
    const buildResult = await compileTest(parsedPath)
 | 
			
		||||
 | 
			
		||||
    if (buildResult.output !== '') {
 | 
			
		||||
    if (buildResult.code !== 0) {
 | 
			
		||||
      if (!(await checkFailure(parsedPath, buildResult))) {
 | 
			
		||||
        ++failed
 | 
			
		||||
      }
 | 
			
		||||
@@ -120,7 +119,7 @@ async function runInDirectory (directoryPath) {
 | 
			
		||||
  await runVM(cpioImage)
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    total: testSources.length,
 | 
			
		||||
    total: (await glob(path.join(directoryPath, 'failures/*'))).length,
 | 
			
		||||
    failed,
 | 
			
		||||
 | 
			
		||||
    passed () {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user