diff options
author | John Hodge <tpg@ucc.asn.au> | 2017-04-13 12:46:01 +0800 |
---|---|---|
committer | John Hodge <tpg@ucc.asn.au> | 2017-04-13 12:46:01 +0800 |
commit | a223ffb9d0629e8b498f07dc67f056a7fb95385e (patch) | |
tree | 2e0f8dd5110e282bd31a071e7060e6d56651269b | |
parent | 9ee40f479d9b655fefcd45bd10691473d23cf0f0 (diff) | |
download | mrust-a223ffb9d0629e8b498f07dc67f056a7fb95385e.tar.gz |
All - Add rough support for #[test] (runs basic tests)
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | src/ast/crate.hpp | 21 | ||||
-rw-r--r-- | src/expand/test.cpp | 14 | ||||
-rw-r--r-- | src/expand/test_harness.cpp | 116 | ||||
-rw-r--r-- | src/include/main_bindings.hpp | 1 | ||||
-rw-r--r-- | src/main.cpp | 24 | ||||
-rw-r--r-- | src/trans/codegen_c.cpp | 1 | ||||
-rw-r--r-- | src/trans/main_bindings.hpp | 1 |
8 files changed, 176 insertions, 3 deletions
@@ -84,6 +84,7 @@ OBJ += expand/include.o OBJ += expand/env.o OBJ += expand/test.o OBJ += expand/rustc_diagnostics.o +OBJ += expand/test_harness.o OBJ += macro_rules/mod.o macro_rules/eval.o macro_rules/parse.o OBJ += resolve/use.o resolve/index.o resolve/absolute.o OBJ += hir/from_ast.o hir/from_ast_expr.o diff --git a/src/ast/crate.hpp b/src/ast/crate.hpp index b74012a3..f9594a83 100644 --- a/src/ast/crate.hpp +++ b/src/ast/crate.hpp @@ -10,6 +10,23 @@ namespace AST { class ExternCrate; +class TestDesc +{ +public: + ::AST::Path path; + ::std::string name; + bool ignore = false; + bool is_benchmark = false; + + enum class ShouldPanic { + No, + Yes, + YesWithMessage, + } panic_type = ShouldPanic::No; + + ::std::string expected_panic_message; +}; + class Crate { public: @@ -22,6 +39,10 @@ public: // Mapping filled by searching for (?visible) macros with is_pub=true ::std::map< ::std::string, const MacroRules*> m_exported_macros; + // List of tests (populated in expand if --test is passed) + bool m_test_harness = false; + ::std::vector<TestDesc> m_tests; + enum class Type { Unknown, RustLib, diff --git a/src/expand/test.cpp b/src/expand/test.cpp index fba6556f..26639203 100644 --- a/src/expand/test.cpp +++ b/src/expand/test.cpp @@ -7,6 +7,7 @@ */ #include <synext_decorator.hpp> #include <ast/ast.hpp> +#include <ast/crate.hpp> class CTestHandler: public ExpandDecorator @@ -18,8 +19,17 @@ class CTestHandler: ERROR(sp, E0000, "#[test] can only be put on functions - found on " << i.tag_str()); } - // TODO: Proper #[test] support, for now just remove them - i = AST::Item::make_None({}); + if( crate.m_test_harness ) + { + ::AST::TestDesc td; + td.name = path.nodes().back().name(); + td.path = ::AST::Path(path); + crate.m_tests.push_back( mv$(td) ); + } + else + { + i = AST::Item::make_None({}); + } } }; diff --git a/src/expand/test_harness.cpp b/src/expand/test_harness.cpp new file mode 100644 index 00000000..883bd2aa --- /dev/null +++ b/src/expand/test_harness.cpp @@ -0,0 +1,116 @@ +/* + * MRustC - Rust Compiler + * - By John Hodge (Mutabah/thePowersGang) + * + * expand/mod.cpp + * - Expand pass core code + */ +#include <ast/ast.hpp> +#include <ast/expr.hpp> +#include <ast/crate.hpp> +#include <main_bindings.hpp> +#include <hir/hir.hpp> // ABI_RUST + +#define NEWNODE(_ty, ...) ::AST::ExprNodeP(new ::AST::ExprNode##_ty(__VA_ARGS__)) + +void Expand_TestHarness(::AST::Crate& crate) +{ + // Create the following module: + // ``` + // mod `#test` { + // extern crate std; + // extern crate test; + // fn main() { + // self::test::test_main_static(&::`#test`::TESTS); + // } + // static TESTS: [test::TestDescAndFn; _] = [ + // test::TestDescAndFn { desc: test::TestDesc { name: "foo", ignore: false, should_panic: test::ShouldPanic::No }, testfn: ::path::to::foo }, + // ]; + // } + // ``` + + // ---- main function ---- + auto main_fn = ::AST::Function { Span(), {}, ABI_RUST, false, false, false, TypeRef(TypeRef::TagUnit(), Span()), {} }; + { + auto call_node = NEWNODE(_CallPath, + ::AST::Path("test", { ::AST::PathNode("test_main_static") }), + ::make_vec1( + NEWNODE(_UniOp, ::AST::ExprNode_UniOp::REF, + NEWNODE(_NamedValue, ::AST::Path("", { ::AST::PathNode("test#"), ::AST::PathNode("TESTS") })) + ) + ) + ); + main_fn.set_code( mv$(call_node) ); + } + + + // ---- test list ---- + ::std::vector< ::AST::ExprNodeP> test_nodes; + + for(const auto& test : crate.m_tests) + { + ::AST::ExprNode_StructLiteral::t_values desc_vals; + // `name: "foo",` + desc_vals.push_back( ::std::make_pair("name", NEWNODE(_CallPath, + ::AST::Path("test", { ::AST::PathNode("StaticTestName") }), + ::make_vec1( NEWNODE(_String, test.name) ) + ) )); + // `ignore: false,` + desc_vals.push_back( ::std::make_pair("ignore", NEWNODE(_Bool, test.ignore)) ); + // `should_panic: ShouldPanic::No,` + { + ::AST::ExprNodeP should_panic_val; + switch(test.panic_type) + { + case ::AST::TestDesc::ShouldPanic::No: + should_panic_val = NEWNODE(_NamedValue, ::AST::Path("test", { ::AST::PathNode("ShouldPanic"), ::AST::PathNode("No") })); + break; + case ::AST::TestDesc::ShouldPanic::Yes: + should_panic_val = NEWNODE(_NamedValue, ::AST::Path("test", { ::AST::PathNode("ShouldPanic"), ::AST::PathNode("Yes") })); + break; + case ::AST::TestDesc::ShouldPanic::YesWithMessage: + should_panic_val = NEWNODE(_CallPath, + ::AST::Path("test", { ::AST::PathNode("ShouldPanic"), ::AST::PathNode("YesWithMessage") }), + make_vec1( NEWNODE(_String, test.expected_panic_message) ) + ); + break; + } + desc_vals.push_back( ::std::make_pair("should_panic", mv$(should_panic_val)) ); + } + auto desc_expr = NEWNODE(_StructLiteral, ::AST::Path("test", { ::AST::PathNode("TestDesc")}), nullptr, mv$(desc_vals)); + + ::AST::ExprNode_StructLiteral::t_values descandfn_vals; + descandfn_vals.push_back( ::std::make_pair(::std::string("desc"), mv$(desc_expr)) ); + + auto test_type_var_name = test.is_benchmark ? "StaticBenchFn" : "StaticTestFn"; + descandfn_vals.push_back( ::std::make_pair(::std::string("testfn"), NEWNODE(_CallPath, + ::AST::Path("test", { ::AST::PathNode(test_type_var_name) }), + ::make_vec1( NEWNODE(_NamedValue, AST::Path(test.path)) ) + ) ) ); + + test_nodes.push_back( NEWNODE(_StructLiteral, ::AST::Path("test", { ::AST::PathNode("TestDescAndFn")}), nullptr, mv$(descandfn_vals) ) ); + } + auto* tests_array = new ::AST::ExprNode_Array(mv$(test_nodes)); + + size_t test_count = tests_array->m_values.size(); + auto tests_list = ::AST::Static { ::AST::Static::Class::STATIC, + TypeRef(TypeRef::TagSizedArray(), Span(), + TypeRef(Span(), ::AST::Path("test", { ::AST::PathNode("TestDescAndFn") })), + ::std::shared_ptr<::AST::ExprNode>( new ::AST::ExprNode_Integer(test_count, CORETYPE_UINT) ) + ), + ::AST::Expr( mv$(tests_array) ) + }; + + // ---- module ---- + auto newmod = ::AST::Module { ::AST::Path("", { ::AST::PathNode("test#") }) }; + // - TODO: These need to be loaded too. + // > They don't actually need to exist here, just be loaded (and use absolute paths) + newmod.add_ext_crate(false, "std", "std", {}); + newmod.add_ext_crate(false, "test", "test", {}); + + newmod.add_item(false, "main", mv$(main_fn), {}); + newmod.add_item(false, "TESTS", mv$(tests_list), {}); + + crate.m_root_module.add_item(false, "test#", mv$(newmod), {}); + crate.m_lang_items["mrustc-main"] = ::AST::Path("", { AST::PathNode("test#"), AST::PathNode("main") }); +} diff --git a/src/include/main_bindings.hpp b/src/include/main_bindings.hpp index c01ff86d..184f266f 100644 --- a/src/include/main_bindings.hpp +++ b/src/include/main_bindings.hpp @@ -16,6 +16,7 @@ extern AST::Crate Parse_Crate(::std::string mainfile); extern void Expand(::AST::Crate& crate); +extern void Expand_TestHarness(::AST::Crate& crate); /// Process #[] decorators extern void Process_Decorators(AST::Crate& crate); diff --git a/src/main.cpp b/src/main.cpp index c534ff15..0c181c29 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -134,6 +134,8 @@ struct ProgramParams unsigned opt_level = 0; bool emit_debug_info = false; + bool test_harness = false; + ::std::vector<const char*> lib_search_dirs; ::std::vector<const char*> libraries; @@ -192,6 +194,11 @@ int main(int argc, char *argv[]) }); + if( params.test_harness ) + { + Cfg_SetFlag("test"); + } + try { @@ -199,6 +206,7 @@ int main(int argc, char *argv[]) AST::Crate crate = CompilePhase<AST::Crate>("Parse", [&]() { return Parse_Crate(params.infile); }); + crate.m_test_harness = params.test_harness; if( params.last_stage == ProgramParams::STAGE_PARSE ) { return 0; @@ -214,6 +222,12 @@ int main(int argc, char *argv[]) Expand(crate); }); + if( params.test_harness ) + { + // TODO: Generate harness main (and override the mrustc-main lang item) + Expand_TestHarness(crate); + } + // Extract the crate type and name from the crate attributes auto crate_type = params.crate_type; if( crate_type == ::AST::Crate::Type::Unknown ) { @@ -286,7 +300,7 @@ int main(int argc, char *argv[]) } // Allocator and panic strategies - if( crate.m_crate_type == ::AST::Crate::Type::Executable ) + if( crate.m_crate_type == ::AST::Crate::Type::Executable || params.test_harness ) { // TODO: Detect if an allocator crate is already present. crate.load_extern_crate(Span(), "alloc_system"); @@ -460,6 +474,11 @@ int main(int argc, char *argv[]) trans_opt.emit_debug_info = params.emit_debug_info; // Generate code for non-generic public items (if requested) + if( params.test_harness ) + { + // If the test harness is enabled, override crate type to "Executable" + crate_type = ::AST::Crate::Type::Executable; + } switch( crate_type ) { case ::AST::Crate::Type::Unknown: @@ -684,6 +703,9 @@ ProgramParams::ProgramParams(int argc, char *argv[]) exit(1); } } + else if( strcmp(arg, "--test") == 0 ) { + this->test_harness = true; + } else { ::std::cerr << "Unknown option '" << arg << "'" << ::std::endl; exit(1); diff --git a/src/trans/codegen_c.cpp b/src/trans/codegen_c.cpp index e74231e8..5d599dd5 100644 --- a/src/trans/codegen_c.cpp +++ b/src/trans/codegen_c.cpp @@ -123,6 +123,7 @@ namespace { << "(uint8_t*)" << Trans_Mangle( ::HIR::GenericPath(m_resolve.m_crate.get_lang_item_path(Span(), "mrustc-main")) ) << ", argc, (uint8_t**)argv" << ");\n"; } + // TODO: Test framework? else { m_of << "\t" << Trans_Mangle(::HIR::GenericPath(c_start_path)) << "(argc, argv);\n"; diff --git a/src/trans/main_bindings.hpp b/src/trans/main_bindings.hpp index 2878cc66..59933863 100644 --- a/src/trans/main_bindings.hpp +++ b/src/trans/main_bindings.hpp @@ -23,6 +23,7 @@ struct TransOptions }; extern TransList Trans_Enumerate_Main(const ::HIR::Crate& crate); +extern TransList Trans_Enumerate_Test(const ::HIR::Crate& crate); // NOTE: This also sets the saveout flags extern TransList Trans_Enumerate_Public(::HIR::Crate& crate); |