summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Hodge <tpg@ucc.asn.au>2017-04-13 12:46:01 +0800
committerJohn Hodge <tpg@ucc.asn.au>2017-04-13 12:46:01 +0800
commita223ffb9d0629e8b498f07dc67f056a7fb95385e (patch)
tree2e0f8dd5110e282bd31a071e7060e6d56651269b
parent9ee40f479d9b655fefcd45bd10691473d23cf0f0 (diff)
downloadmrust-a223ffb9d0629e8b498f07dc67f056a7fb95385e.tar.gz
All - Add rough support for #[test] (runs basic tests)
-rw-r--r--Makefile1
-rw-r--r--src/ast/crate.hpp21
-rw-r--r--src/expand/test.cpp14
-rw-r--r--src/expand/test_harness.cpp116
-rw-r--r--src/include/main_bindings.hpp1
-rw-r--r--src/main.cpp24
-rw-r--r--src/trans/codegen_c.cpp1
-rw-r--r--src/trans/main_bindings.hpp1
8 files changed, 176 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index 17fbf665..2e907516 100644
--- a/Makefile
+++ b/Makefile
@@ -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);