Support failure tests
This commit is contained in:
parent
7e5f7f492d
commit
f2a20c2825
2
TODO
2
TODO
@ -14,6 +14,8 @@
|
|||||||
- Support immediates greater than 12 bits.
|
- Support immediates greater than 12 bits.
|
||||||
- It seems instructions are correctly encoded only if the compiler is running
|
- It seems instructions are correctly encoded only if the compiler is running
|
||||||
on a little endian architecture.
|
on a little endian architecture.
|
||||||
|
- Print filename in the error message.
|
||||||
|
- Merge declaration and definition nodes.
|
||||||
|
|
||||||
# Shell
|
# Shell
|
||||||
- Persist the history.
|
- Persist the history.
|
||||||
|
@ -43,12 +43,19 @@ namespace elna::cli
|
|||||||
}
|
}
|
||||||
return 1;
|
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)
|
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;
|
return 2;
|
||||||
}
|
}
|
||||||
source::name_analysis_visitor().visit(ast.get());
|
source::name_analysis_visitor().visit(ast.get());
|
||||||
|
source::type_analysis_visitor().visit(ast.get());
|
||||||
source::allocator_visitor().visit(ast.get());
|
source::allocator_visitor().visit(ast.get());
|
||||||
riscv::riscv32_elf(ast.get(), out_file);
|
riscv::riscv32_elf(ast.get(), out_file);
|
||||||
|
|
||||||
|
@ -107,6 +107,8 @@ namespace elna::source
|
|||||||
std::int32_t number() const;
|
std::int32_t number() const;
|
||||||
const elna::source::position& position() const noexcept;
|
const elna::source::position& position() const noexcept;
|
||||||
|
|
||||||
|
std::string to_string() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
type m_type;
|
type m_type;
|
||||||
value m_value{};
|
value m_value{};
|
||||||
|
@ -279,6 +279,7 @@ namespace elna::source
|
|||||||
std::unique_ptr<expression> parse_term();
|
std::unique_ptr<expression> parse_term();
|
||||||
std::unique_ptr<expression> parse_expression();
|
std::unique_ptr<expression> parse_expression();
|
||||||
std::unique_ptr<constant_definition> parse_constant_definition();
|
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<declaration> parse_declaration();
|
||||||
std::unique_ptr<statement> parse_statement();
|
std::unique_ptr<statement> parse_statement();
|
||||||
std::unique_ptr<call_statement> parse_call_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<assign_statement> parse_assign_statement();
|
||||||
std::unique_ptr<if_statement> parse_if_statement();
|
std::unique_ptr<if_statement> parse_if_statement();
|
||||||
std::unique_ptr<while_statement> parse_while_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::vector<std::unique_ptr<declaration>> parse_declarations();
|
||||||
std::unique_ptr<block> parse_block();
|
std::unique_ptr<block> parse_block();
|
||||||
|
|
||||||
|
@ -70,6 +70,11 @@ namespace elna::source
|
|||||||
errors().emplace_back(std::make_unique<U>(first));
|
errors().emplace_back(std::make_unique<U>(first));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
explicit result(E&& errors)
|
||||||
|
: payload(std::move(errors))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
bool has_errors() const noexcept
|
bool has_errors() const noexcept
|
||||||
{
|
{
|
||||||
return std::holds_alternative<E>(payload);
|
return std::holds_alternative<E>(payload);
|
||||||
|
@ -22,4 +22,9 @@ namespace elna::source
|
|||||||
void visit(declaration *declaration) override;
|
void visit(declaration *declaration) override;
|
||||||
void visit(block *block) 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
|
#define BOOST_PROCESS_USE_STD_FS
|
||||||
|
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/process.hpp>
|
||||||
|
#include <boost/process/v2.hpp>
|
||||||
|
|
||||||
namespace elna
|
namespace elna
|
||||||
{
|
{
|
||||||
enum class test_status
|
enum class test_status
|
||||||
@ -14,6 +18,7 @@ namespace elna
|
|||||||
compile_failed,
|
compile_failed,
|
||||||
build_failed,
|
build_failed,
|
||||||
expectation_failed,
|
expectation_failed,
|
||||||
|
expectation_not_found
|
||||||
};
|
};
|
||||||
|
|
||||||
struct test_result final
|
struct test_result final
|
||||||
@ -39,5 +44,9 @@ namespace elna
|
|||||||
void add_exit_code(const test_status result) noexcept;
|
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);
|
void print_result(const std::filesystem::directory_entry& test_entry, const test_result& result);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "elna/source/lexer.hpp"
|
#include "elna/source/lexer.hpp"
|
||||||
|
#include <cassert>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
@ -212,6 +213,60 @@ namespace elna::source
|
|||||||
|| of() == type::boolean;
|
|| 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)
|
unexpected_character::unexpected_character(const std::string& character, const source::position position)
|
||||||
: error(position), character(character)
|
: error(position), character(character)
|
||||||
{
|
{
|
||||||
@ -233,7 +288,7 @@ namespace elna::source
|
|||||||
|
|
||||||
std::string unexpected_token::what() const
|
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)
|
lexer::lexer(std::vector<token>&& tokens, const position last_position)
|
||||||
|
@ -49,9 +49,9 @@ namespace elna::source
|
|||||||
|
|
||||||
void empty_visitor::visit(block *block)
|
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())
|
for (const auto& block_declaration : block->declarations())
|
||||||
{
|
{
|
||||||
@ -83,9 +83,6 @@ namespace elna::source
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* AST node.
|
|
||||||
*/
|
|
||||||
void node::accept(parser_visitor *)
|
void node::accept(parser_visitor *)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -381,14 +378,26 @@ namespace elna::source
|
|||||||
|
|
||||||
std::unique_ptr<program> parser::parse()
|
std::unique_ptr<program> parser::parse()
|
||||||
{
|
{
|
||||||
auto definitions = parse_definitions();
|
auto constants = parse_constant_definitions();
|
||||||
auto declarations = parse_declarations();
|
auto declarations = parse_declarations();
|
||||||
|
auto procedures = parse_procedure_definitions();
|
||||||
auto parsed_statement = parse_statement();
|
auto parsed_statement = parse_statement();
|
||||||
|
|
||||||
if (parsed_statement == nullptr)
|
if (parsed_statement == nullptr)
|
||||||
{
|
{
|
||||||
return 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),
|
return std::make_unique<program>(std::move(definitions),
|
||||||
std::move(declarations), std::move(parsed_statement));
|
std::move(declarations), std::move(parsed_statement));
|
||||||
}
|
}
|
||||||
@ -492,6 +501,28 @@ namespace elna::source
|
|||||||
return nullptr;
|
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()
|
std::unique_ptr<declaration> parser::parse_declaration()
|
||||||
{
|
{
|
||||||
auto declaration_identifier = iterator.advance(token::type::identifier);
|
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));
|
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)
|
if (iterator->of() != token::type::let)
|
||||||
{
|
{
|
||||||
@ -651,7 +682,7 @@ namespace elna::source
|
|||||||
}
|
}
|
||||||
++iterator; // Skip const.
|
++iterator; // Skip const.
|
||||||
|
|
||||||
std::unique_ptr<definition> parsed_definition;
|
std::unique_ptr<constant_definition> parsed_definition;
|
||||||
while ((parsed_definition = parse_constant_definition()) != nullptr)
|
while ((parsed_definition = parse_constant_definition()) != nullptr)
|
||||||
{
|
{
|
||||||
definitions.push_back(std::move(parsed_definition));
|
definitions.push_back(std::move(parsed_definition));
|
||||||
@ -674,6 +705,23 @@ namespace elna::source
|
|||||||
return definitions;
|
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>> parser::parse_declarations()
|
||||||
{
|
{
|
||||||
std::vector<std::unique_ptr<declaration>> declarations;
|
std::vector<std::unique_ptr<declaration>> declarations;
|
||||||
@ -709,7 +757,7 @@ namespace elna::source
|
|||||||
|
|
||||||
std::unique_ptr<block> parser::parse_block()
|
std::unique_ptr<block> parser::parse_block()
|
||||||
{
|
{
|
||||||
auto definitions = parse_definitions();
|
auto constants = parse_constant_definitions();
|
||||||
auto declarations = parse_declarations();
|
auto declarations = parse_declarations();
|
||||||
auto parsed_statement = parse_statement();
|
auto parsed_statement = parse_statement();
|
||||||
|
|
||||||
@ -717,6 +765,13 @@ namespace elna::source
|
|||||||
{
|
{
|
||||||
return nullptr;
|
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),
|
return std::make_unique<block>(std::move(definitions),
|
||||||
std::move(declarations), std::move(parsed_statement));
|
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
|
117
tests/tester.cpp
117
tests/tester.cpp
@ -1,11 +1,7 @@
|
|||||||
#include "elna/tester.hpp"
|
#include "elna/tester.hpp"
|
||||||
|
|
||||||
#include <future>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <boost/process.hpp>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
|
|
||||||
namespace elna
|
namespace elna
|
||||||
{
|
{
|
||||||
std::uint32_t test_results::total() const noexcept
|
std::uint32_t test_results::total() const noexcept
|
||||||
@ -47,7 +43,36 @@ namespace elna
|
|||||||
return in_build_directory() / path;
|
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();
|
const std::filesystem::path test_filename = test_entry.path().filename();
|
||||||
|
|
||||||
@ -59,37 +84,57 @@ namespace elna
|
|||||||
std::filesystem::remove(test_binary);
|
std::filesystem::remove(test_binary);
|
||||||
std::filesystem::remove(test_object);
|
std::filesystem::remove(test_object);
|
||||||
|
|
||||||
auto status = boost::process::system("./build/bin/elna",
|
test_result result = run_for_output(context, "./build/bin/elna",
|
||||||
"-o", test_object.string(), test_entry.path().string());
|
{ "-o", test_object.string(), test_entry.path().string() });
|
||||||
if (status != 0)
|
|
||||||
|
if (result.code != 0)
|
||||||
{
|
{
|
||||||
return test_result{ test_status::compile_failed, status };
|
result.status = test_status::compile_failed;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
status = boost::process::system(
|
boost::process::v2::execute(boost::process::v2::process(
|
||||||
"/opt/riscv/bin/riscv32-unknown-elf-ld",
|
context, "/opt/riscv/bin/riscv32-unknown-elf-ld",
|
||||||
"-o", test_binary.string(),
|
{
|
||||||
"-L/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/",
|
"-o", test_binary.string(),
|
||||||
"-L/opt/riscv/riscv32-unknown-elf/lib",
|
"-L/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/",
|
||||||
"/opt/riscv/riscv32-unknown-elf/lib/crt0.o",
|
"-L/opt/riscv/riscv32-unknown-elf/lib",
|
||||||
"/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/crtbegin.o",
|
"/opt/riscv/riscv32-unknown-elf/lib/crt0.o",
|
||||||
test_object.string(),
|
"/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/crtbegin.o",
|
||||||
"--start-group", "-lgcc", "-lc", "-lgloss", "--end-group",
|
test_object.string(),
|
||||||
"/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/crtend.o"
|
"--start-group", "-lgcc", "-lc", "-lgloss", "--end-group",
|
||||||
);
|
"/opt/riscv/lib/gcc/riscv32-unknown-elf/13.2.0/crtend.o"
|
||||||
|
}
|
||||||
|
));
|
||||||
return test_result{};
|
return test_result{};
|
||||||
}
|
}
|
||||||
|
|
||||||
static test_result check_expectation(const std::filesystem::directory_entry& test_entry, const test_result& run)
|
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;
|
const std::filesystem::path expectation_path =
|
||||||
expectation_path.replace_extension(".txt");
|
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::opstream pipe_stream;
|
||||||
boost::process::child diff(
|
boost::process::child diff(
|
||||||
boost::process::search_path("diff"), "-Nur", "--color",
|
boost::process::search_path("diff"), "-Nur", "--color",
|
||||||
expectation_path.string(), "-",
|
expected_result_path, "-",
|
||||||
boost::process::std_in < pipe_stream
|
boost::process::std_in < pipe_stream
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -100,35 +145,27 @@ namespace elna
|
|||||||
|
|
||||||
if (diff.exit_code() == 0)
|
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 };
|
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();
|
const std::filesystem::path test_filename = test_entry.path().filename();
|
||||||
|
|
||||||
std::filesystem::path test_binary = in_build_directory(test_filename);
|
std::filesystem::path test_binary = in_build_directory(test_filename);
|
||||||
test_binary.replace_extension();
|
test_binary.replace_extension();
|
||||||
|
|
||||||
boost::asio::io_service io_service;
|
return run_for_output(context,
|
||||||
std::future<std::string> buffer;
|
boost::process::search_path("qemu-riscv32"),
|
||||||
boost::process::child qemu(
|
{ test_binary.string() });
|
||||||
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() };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static test_results run_in_path(const std::filesystem::path test_directory)
|
static test_results run_in_path(const std::filesystem::path test_directory)
|
||||||
{
|
{
|
||||||
test_results results;
|
test_results results;
|
||||||
|
boost::asio::io_context context;
|
||||||
|
|
||||||
for (const auto& test_entry : std::filesystem::directory_iterator(test_directory))
|
for (const auto& test_entry : std::filesystem::directory_iterator(test_directory))
|
||||||
{
|
{
|
||||||
@ -141,10 +178,10 @@ namespace elna
|
|||||||
std::cout << "Running " << test_entry << std::endl;
|
std::cout << "Running " << test_entry << std::endl;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
result = build_test(test_entry);
|
result = build_test(context, test_entry);
|
||||||
if (result.status == test_status::successful)
|
if (result.status == test_status::successful)
|
||||||
{
|
{
|
||||||
result = run_test(test_entry);
|
result = run_test(context, test_entry);
|
||||||
}
|
}
|
||||||
result = check_expectation(test_entry, result);
|
result = check_expectation(test_entry, result);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user