Support failure tests
This commit is contained in:
		
							
								
								
									
										2
									
								
								TODO
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								TODO
									
									
									
									
									
								
							@@ -14,6 +14,8 @@
 | 
			
		||||
- Support immediates greater than 12 bits.
 | 
			
		||||
- It seems instructions are correctly encoded only if the compiler is running
 | 
			
		||||
  on a little endian architecture.
 | 
			
		||||
- Print filename in the error message.
 | 
			
		||||
- Merge declaration and definition nodes.
 | 
			
		||||
 | 
			
		||||
# Shell
 | 
			
		||||
- Persist the history.
 | 
			
		||||
 
 | 
			
		||||
@@ -43,12 +43,19 @@ namespace elna::cli
 | 
			
		||||
            }
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
        auto ast = source::parser(std::move(lex_result.success())).parse();
 | 
			
		||||
        source::parser parser{ std::move(lex_result.success()) };
 | 
			
		||||
        auto ast = parser.parse();
 | 
			
		||||
        if (ast == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            for (const auto& compile_error : parser.errors())
 | 
			
		||||
            {
 | 
			
		||||
                std::cout << compile_error->line() << ':' << compile_error->column()
 | 
			
		||||
                    << ": " << compile_error->what() << std::endl;
 | 
			
		||||
            }
 | 
			
		||||
            return 2;
 | 
			
		||||
        }
 | 
			
		||||
        source::name_analysis_visitor().visit(ast.get());
 | 
			
		||||
        source::type_analysis_visitor().visit(ast.get());
 | 
			
		||||
        source::allocator_visitor().visit(ast.get());
 | 
			
		||||
        riscv::riscv32_elf(ast.get(), out_file);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -107,6 +107,8 @@ namespace elna::source
 | 
			
		||||
        std::int32_t number() const;
 | 
			
		||||
        const elna::source::position& position() const noexcept;
 | 
			
		||||
 | 
			
		||||
        std::string to_string() const;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        type m_type;
 | 
			
		||||
        value m_value{};
 | 
			
		||||
 
 | 
			
		||||
@@ -279,6 +279,7 @@ namespace elna::source
 | 
			
		||||
        std::unique_ptr<expression> parse_term();
 | 
			
		||||
        std::unique_ptr<expression> parse_expression();
 | 
			
		||||
        std::unique_ptr<constant_definition> parse_constant_definition();
 | 
			
		||||
        std::unique_ptr<procedure_definition> parse_procedure_definition();
 | 
			
		||||
        std::unique_ptr<declaration> parse_declaration();
 | 
			
		||||
        std::unique_ptr<statement> parse_statement();
 | 
			
		||||
        std::unique_ptr<call_statement> parse_call_statement();
 | 
			
		||||
@@ -286,7 +287,8 @@ namespace elna::source
 | 
			
		||||
        std::unique_ptr<assign_statement> parse_assign_statement();
 | 
			
		||||
        std::unique_ptr<if_statement> parse_if_statement();
 | 
			
		||||
        std::unique_ptr<while_statement> parse_while_statement();
 | 
			
		||||
        std::vector<std::unique_ptr<definition>> parse_definitions();
 | 
			
		||||
        std::vector<std::unique_ptr<constant_definition>> parse_constant_definitions();
 | 
			
		||||
        std::vector<std::unique_ptr<procedure_definition>> parse_procedure_definitions();
 | 
			
		||||
        std::vector<std::unique_ptr<declaration>> parse_declarations();
 | 
			
		||||
        std::unique_ptr<block> parse_block();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -70,6 +70,11 @@ namespace elna::source
 | 
			
		||||
            errors().emplace_back(std::make_unique<U>(first));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        explicit result(E&& errors)
 | 
			
		||||
            : payload(std::move(errors))
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool has_errors() const noexcept
 | 
			
		||||
        {
 | 
			
		||||
            return std::holds_alternative<E>(payload);
 | 
			
		||||
 
 | 
			
		||||
@@ -22,4 +22,9 @@ namespace elna::source
 | 
			
		||||
        void visit(declaration *declaration) override;
 | 
			
		||||
        void visit(block *block) override;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class type_analysis_visitor final : public empty_visitor
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,10 @@
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
@@ -14,6 +18,7 @@ namespace elna
 | 
			
		||||
        compile_failed,
 | 
			
		||||
        build_failed,
 | 
			
		||||
        expectation_failed,
 | 
			
		||||
        expectation_not_found
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct test_result final
 | 
			
		||||
@@ -39,5 +44,9 @@ namespace elna
 | 
			
		||||
        void add_exit_code(const test_status result) noexcept;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    test_result run_for_output(boost::asio::io_context& context, const std::filesystem::path& binary,
 | 
			
		||||
            std::initializer_list<boost::string_view> arguments);
 | 
			
		||||
    test_result build_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry);
 | 
			
		||||
    test_result run_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry);
 | 
			
		||||
    void print_result(const std::filesystem::directory_entry& test_entry, const test_result& result);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
#include "elna/source/lexer.hpp"
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <variant>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
@@ -212,6 +213,60 @@ namespace elna::source
 | 
			
		||||
            || of() == type::boolean;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string token::to_string() const
 | 
			
		||||
    {
 | 
			
		||||
        switch (this->m_type)
 | 
			
		||||
        {
 | 
			
		||||
            case type::number:
 | 
			
		||||
                 return "«number»";
 | 
			
		||||
            case type::boolean:
 | 
			
		||||
                 return "«boolean»";
 | 
			
		||||
            case type::term_operator:
 | 
			
		||||
                 return "«term_operator»";
 | 
			
		||||
            case type::let:
 | 
			
		||||
                 return "«const»";
 | 
			
		||||
            case type::identifier:
 | 
			
		||||
                 return "«identifier»";
 | 
			
		||||
            case type::equals:
 | 
			
		||||
                 return "«=»";
 | 
			
		||||
            case type::var:
 | 
			
		||||
                 return "«var»";
 | 
			
		||||
            case type::semicolon:
 | 
			
		||||
                 return "«;»";
 | 
			
		||||
            case type::left_paren:
 | 
			
		||||
                 return "«(»";
 | 
			
		||||
            case type::right_paren:
 | 
			
		||||
                 return "«)»";
 | 
			
		||||
            case type::dot:
 | 
			
		||||
                 return "«)»";
 | 
			
		||||
            case type::comma:
 | 
			
		||||
                 return "«,»";
 | 
			
		||||
            case type::factor_operator:
 | 
			
		||||
                 return "«*»";
 | 
			
		||||
            case type::eof:
 | 
			
		||||
                 return "«EOF»";
 | 
			
		||||
            case type::begin:
 | 
			
		||||
                 return "«begin»";
 | 
			
		||||
            case type::end:
 | 
			
		||||
                 return "«end»";
 | 
			
		||||
            case type::assignment:
 | 
			
		||||
                 return "«:=»";
 | 
			
		||||
            case type::colon:
 | 
			
		||||
                 return "«:»";
 | 
			
		||||
            case type::when:
 | 
			
		||||
                 return "«if»";
 | 
			
		||||
            case type::then:
 | 
			
		||||
                 return "«then»";
 | 
			
		||||
            case type::_while:
 | 
			
		||||
                 return "«while»";
 | 
			
		||||
            case type::_do:
 | 
			
		||||
                 return "«do»";
 | 
			
		||||
            case type::procedure:
 | 
			
		||||
                 return "«proc»";
 | 
			
		||||
        };
 | 
			
		||||
        assert(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unexpected_character::unexpected_character(const std::string& character, const source::position position)
 | 
			
		||||
        : error(position), character(character)
 | 
			
		||||
    {
 | 
			
		||||
@@ -233,7 +288,7 @@ namespace elna::source
 | 
			
		||||
 | 
			
		||||
    std::string unexpected_token::what() const
 | 
			
		||||
    {
 | 
			
		||||
        return "Unexpected token";
 | 
			
		||||
        return "Unexpected token " + m_token.to_string();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lexer::lexer(std::vector<token>&& tokens, const position last_position)
 | 
			
		||||
 
 | 
			
		||||
@@ -49,9 +49,9 @@ namespace elna::source
 | 
			
		||||
 | 
			
		||||
    void empty_visitor::visit(block *block)
 | 
			
		||||
    {
 | 
			
		||||
        for (const auto& block_definition : block->definitions())
 | 
			
		||||
        for (const auto& constant : block->definitions())
 | 
			
		||||
        {
 | 
			
		||||
            block_definition->accept(this);
 | 
			
		||||
            constant->accept(this);
 | 
			
		||||
        }
 | 
			
		||||
        for (const auto& block_declaration : block->declarations())
 | 
			
		||||
        {
 | 
			
		||||
@@ -83,9 +83,6 @@ namespace elna::source
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * AST node.
 | 
			
		||||
     */
 | 
			
		||||
    void node::accept(parser_visitor *)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
@@ -381,14 +378,26 @@ namespace elna::source
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<program> parser::parse()
 | 
			
		||||
    {
 | 
			
		||||
        auto definitions = parse_definitions();
 | 
			
		||||
        auto constants = parse_constant_definitions();
 | 
			
		||||
        auto declarations = parse_declarations();
 | 
			
		||||
        auto procedures = parse_procedure_definitions();
 | 
			
		||||
        auto parsed_statement = parse_statement();
 | 
			
		||||
 | 
			
		||||
        if (parsed_statement == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            return nullptr;
 | 
			
		||||
        }
 | 
			
		||||
        std::vector<std::unique_ptr<definition>> definitions(constants.size() + procedures.size());
 | 
			
		||||
        std::vector<std::unique_ptr<definition>>::iterator definition = definitions.begin();
 | 
			
		||||
 | 
			
		||||
        for (auto& constant : constants)
 | 
			
		||||
        {
 | 
			
		||||
            *definition++ = std::move(constant);
 | 
			
		||||
        }
 | 
			
		||||
        for (auto& procedure : procedures)
 | 
			
		||||
        {
 | 
			
		||||
            *definition++ = std::move(procedure);
 | 
			
		||||
        }
 | 
			
		||||
        return std::make_unique<program>(std::move(definitions),
 | 
			
		||||
                std::move(declarations), std::move(parsed_statement));
 | 
			
		||||
    }
 | 
			
		||||
@@ -492,6 +501,28 @@ namespace elna::source
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<procedure_definition> parser::parse_procedure_definition()
 | 
			
		||||
    {
 | 
			
		||||
        if (!iterator.skip(token::type::procedure))
 | 
			
		||||
        {
 | 
			
		||||
            return nullptr;
 | 
			
		||||
        }
 | 
			
		||||
        auto definition_identifier = iterator.advance(token::type::identifier);
 | 
			
		||||
 | 
			
		||||
        if (!definition_identifier.has_value() || !iterator.skip(token::type::semicolon))
 | 
			
		||||
        {
 | 
			
		||||
            return nullptr;
 | 
			
		||||
        }
 | 
			
		||||
        auto definition_body = parse_block();
 | 
			
		||||
 | 
			
		||||
        if (definition_body == nullptr || !iterator.skip(token::type::semicolon))
 | 
			
		||||
        {
 | 
			
		||||
            return nullptr;
 | 
			
		||||
        }
 | 
			
		||||
        return std::make_unique<procedure_definition>(definition_identifier->get().identifier(),
 | 
			
		||||
                std::move(definition_body));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<declaration> parser::parse_declaration()
 | 
			
		||||
    {
 | 
			
		||||
        auto declaration_identifier = iterator.advance(token::type::identifier);
 | 
			
		||||
@@ -641,9 +672,9 @@ namespace elna::source
 | 
			
		||||
        return std::make_unique<while_statement>(std::move(condition), std::move(body));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<std::unique_ptr<definition>> parser::parse_definitions()
 | 
			
		||||
    std::vector<std::unique_ptr<constant_definition>> parser::parse_constant_definitions()
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<std::unique_ptr<definition>> definitions;
 | 
			
		||||
        std::vector<std::unique_ptr<constant_definition>> definitions;
 | 
			
		||||
 | 
			
		||||
        if (iterator->of() != token::type::let)
 | 
			
		||||
        {
 | 
			
		||||
@@ -651,7 +682,7 @@ namespace elna::source
 | 
			
		||||
        }
 | 
			
		||||
        ++iterator; // Skip const.
 | 
			
		||||
 | 
			
		||||
        std::unique_ptr<definition> parsed_definition;
 | 
			
		||||
        std::unique_ptr<constant_definition> parsed_definition;
 | 
			
		||||
        while ((parsed_definition = parse_constant_definition()) != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            definitions.push_back(std::move(parsed_definition));
 | 
			
		||||
@@ -674,6 +705,23 @@ namespace elna::source
 | 
			
		||||
        return definitions;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<std::unique_ptr<procedure_definition>> parser::parse_procedure_definitions()
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<std::unique_ptr<procedure_definition>> definitions;
 | 
			
		||||
 | 
			
		||||
        while (iterator.current(token::type::procedure))
 | 
			
		||||
        {
 | 
			
		||||
            auto parsed_definition = parse_procedure_definition();
 | 
			
		||||
 | 
			
		||||
            if (parsed_definition == nullptr)
 | 
			
		||||
            {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            definitions.push_back(std::move(parsed_definition));
 | 
			
		||||
        }
 | 
			
		||||
        return definitions;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<std::unique_ptr<declaration>> parser::parse_declarations()
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<std::unique_ptr<declaration>> declarations;
 | 
			
		||||
@@ -709,7 +757,7 @@ namespace elna::source
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<block> parser::parse_block()
 | 
			
		||||
    {
 | 
			
		||||
        auto definitions = parse_definitions();
 | 
			
		||||
        auto constants = parse_constant_definitions();
 | 
			
		||||
        auto declarations = parse_declarations();
 | 
			
		||||
        auto parsed_statement = parse_statement();
 | 
			
		||||
 | 
			
		||||
@@ -717,6 +765,13 @@ namespace elna::source
 | 
			
		||||
        {
 | 
			
		||||
            return nullptr;
 | 
			
		||||
        }
 | 
			
		||||
        std::vector<std::unique_ptr<definition>> definitions(constants.size());
 | 
			
		||||
        std::vector<std::unique_ptr<definition>>::iterator definition = definitions.begin();
 | 
			
		||||
 | 
			
		||||
        for (auto& constant : constants)
 | 
			
		||||
        {
 | 
			
		||||
            *definition++ = std::move(constant);
 | 
			
		||||
        }
 | 
			
		||||
        return std::make_unique<block>(std::move(definitions),
 | 
			
		||||
                std::move(declarations), std::move(parsed_statement));
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								tests/expectations/procedure_definition.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/expectations/procedure_definition.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
2
 | 
			
		||||
							
								
								
									
										1
									
								
								tests/failures/single_word_error.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/failures/single_word_error.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
1:1: Unexpected token «identifier»
 | 
			
		||||
							
								
								
									
										6
									
								
								tests/procedure_definition.eln
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								tests/procedure_definition.eln
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
proc f;
 | 
			
		||||
  writei(5);
 | 
			
		||||
 | 
			
		||||
begin
 | 
			
		||||
  writei(2)
 | 
			
		||||
end.
 | 
			
		||||
							
								
								
									
										1
									
								
								tests/single_word_error.eln
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/single_word_error.eln
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
asdf
 | 
			
		||||
@@ -1,11 +1,7 @@
 | 
			
		||||
#include "elna/tester.hpp"
 | 
			
		||||
 | 
			
		||||
#include <future>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
#include <boost/process.hpp>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
 | 
			
		||||
namespace elna
 | 
			
		||||
{
 | 
			
		||||
    std::uint32_t test_results::total() const noexcept
 | 
			
		||||
@@ -47,7 +43,36 @@ namespace elna
 | 
			
		||||
        return in_build_directory() / path;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static test_result build_test(const std::filesystem::directory_entry& test_entry)
 | 
			
		||||
    test_result run_for_output(boost::asio::io_context& context, const std::filesystem::path& binary,
 | 
			
		||||
            std::initializer_list<boost::string_view> arguments)
 | 
			
		||||
    {
 | 
			
		||||
        boost::asio::readable_pipe read_pipe{ context };
 | 
			
		||||
        test_result result{};
 | 
			
		||||
        std::string output;
 | 
			
		||||
        boost::asio::dynamic_string_buffer buffer = boost::asio::dynamic_buffer(output);
 | 
			
		||||
        boost::system::error_code ec;
 | 
			
		||||
        boost::process::v2::process elna_child(context,
 | 
			
		||||
                binary, arguments,
 | 
			
		||||
                boost::process::v2::process_stdio{ nullptr, read_pipe, read_pipe });
 | 
			
		||||
        do
 | 
			
		||||
        {
 | 
			
		||||
            std::size_t transferred = read_pipe.read_some(buffer.prepare(512), ec);
 | 
			
		||||
 | 
			
		||||
            if (transferred == 0)
 | 
			
		||||
            {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            buffer.commit(transferred);
 | 
			
		||||
            result.output.append(boost::asio::buffer_cast<const char *>(buffer.data()), buffer.size());
 | 
			
		||||
            buffer.consume(transferred);
 | 
			
		||||
        }
 | 
			
		||||
        while (ec == boost::system::errc::success);
 | 
			
		||||
        result.code = elna_child.wait();
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    test_result build_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry)
 | 
			
		||||
    {
 | 
			
		||||
        const std::filesystem::path test_filename = test_entry.path().filename();
 | 
			
		||||
 | 
			
		||||
@@ -59,14 +84,17 @@ namespace elna
 | 
			
		||||
        std::filesystem::remove(test_binary);
 | 
			
		||||
        std::filesystem::remove(test_object);
 | 
			
		||||
 | 
			
		||||
        auto status = boost::process::system("./build/bin/elna",
 | 
			
		||||
                "-o", test_object.string(), test_entry.path().string());
 | 
			
		||||
        if (status != 0)
 | 
			
		||||
        test_result result = run_for_output(context, "./build/bin/elna",
 | 
			
		||||
                { "-o", test_object.string(), test_entry.path().string() });
 | 
			
		||||
 | 
			
		||||
        if (result.code != 0)
 | 
			
		||||
        {
 | 
			
		||||
            return test_result{ test_status::compile_failed, status };
 | 
			
		||||
            result.status = test_status::compile_failed;
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
        status = boost::process::system(
 | 
			
		||||
                "/opt/riscv/bin/riscv32-unknown-elf-ld",
 | 
			
		||||
        boost::process::v2::execute(boost::process::v2::process(
 | 
			
		||||
                    context, "/opt/riscv/bin/riscv32-unknown-elf-ld",
 | 
			
		||||
                    {
 | 
			
		||||
                        "-o", test_binary.string(),
 | 
			
		||||
                        "-L/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/",
 | 
			
		||||
                        "-L/opt/riscv/riscv32-unknown-elf/lib",
 | 
			
		||||
@@ -75,21 +103,38 @@ namespace elna
 | 
			
		||||
                       test_object.string(),
 | 
			
		||||
                        "--start-group", "-lgcc", "-lc", "-lgloss", "--end-group",
 | 
			
		||||
                        "/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/crtend.o"
 | 
			
		||||
        );
 | 
			
		||||
                    }
 | 
			
		||||
        ));
 | 
			
		||||
        return test_result{};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static test_result check_expectation(const std::filesystem::directory_entry& test_entry, const test_result& run)
 | 
			
		||||
    {
 | 
			
		||||
        const std::filesystem::path test_filename = test_entry.path().filename();
 | 
			
		||||
        std::filesystem::path test_filename = test_entry.path().filename();
 | 
			
		||||
        test_filename.replace_extension(".txt");
 | 
			
		||||
 | 
			
		||||
        std::filesystem::path expectation_path = test_entry.path().parent_path() / "expectations" / test_filename;
 | 
			
		||||
        expectation_path.replace_extension(".txt");
 | 
			
		||||
        const std::filesystem::path expectation_path =
 | 
			
		||||
            test_entry.path().parent_path() / "expectations" / test_filename;
 | 
			
		||||
        const std::filesystem::path failures_path =
 | 
			
		||||
            test_entry.path().parent_path() / "failures" / test_filename;
 | 
			
		||||
 | 
			
		||||
        std::string expected_result_path;
 | 
			
		||||
        if (std::filesystem::exists(expectation_path))
 | 
			
		||||
        {
 | 
			
		||||
            expected_result_path = expectation_path.string();
 | 
			
		||||
        }
 | 
			
		||||
        else if (std::filesystem::exists(failures_path))
 | 
			
		||||
        {
 | 
			
		||||
            expected_result_path = failures_path.string();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return test_result{ test_status::expectation_not_found, run.code, run.output };
 | 
			
		||||
        }
 | 
			
		||||
        boost::process::opstream pipe_stream;
 | 
			
		||||
        boost::process::child diff(
 | 
			
		||||
                boost::process::search_path("diff"), "-Nur", "--color",
 | 
			
		||||
                expectation_path.string(), "-",
 | 
			
		||||
                expected_result_path, "-",
 | 
			
		||||
                boost::process::std_in < pipe_stream
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@@ -100,35 +145,27 @@ namespace elna
 | 
			
		||||
 | 
			
		||||
        if (diff.exit_code() == 0)
 | 
			
		||||
        {
 | 
			
		||||
            return run;
 | 
			
		||||
            return test_result{ test_status::successful, run.code, run.output };
 | 
			
		||||
        }
 | 
			
		||||
        return test_result{ test_status::expectation_failed, run.code, run.output };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static test_result run_test(const std::filesystem::directory_entry& test_entry)
 | 
			
		||||
    test_result run_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_build_directory(test_filename);
 | 
			
		||||
        test_binary.replace_extension();
 | 
			
		||||
 | 
			
		||||
        boost::asio::io_service io_service;
 | 
			
		||||
        std::future<std::string> buffer;
 | 
			
		||||
        boost::process::child qemu(
 | 
			
		||||
        return run_for_output(context,
 | 
			
		||||
                boost::process::search_path("qemu-riscv32"),
 | 
			
		||||
            test_binary.string(),
 | 
			
		||||
            boost::process::std_out > buffer,
 | 
			
		||||
            boost::process::std_err > buffer,
 | 
			
		||||
            io_service
 | 
			
		||||
        );
 | 
			
		||||
        io_service.run();
 | 
			
		||||
 | 
			
		||||
        return test_result{ test_status::successful, qemu.exit_code(), buffer.get() };
 | 
			
		||||
                { test_binary.string() });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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))
 | 
			
		||||
        {
 | 
			
		||||
@@ -141,10 +178,10 @@ namespace elna
 | 
			
		||||
            std::cout << "Running " << test_entry << std::endl;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                result = build_test(test_entry);
 | 
			
		||||
                result = build_test(context, test_entry);
 | 
			
		||||
                if (result.status == test_status::successful)
 | 
			
		||||
                {
 | 
			
		||||
                    result = run_test(test_entry);
 | 
			
		||||
                    result = run_test(context, test_entry);
 | 
			
		||||
                }
 | 
			
		||||
                result = check_expectation(test_entry, result);
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user