summaryrefslogtreecommitdiff
path: root/src/expand/test_harness.cpp
blob: 456f90e4bc454bb9a7bc48328c80b5704e21a3b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
 * 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)
    {
        // HACK: Don't emit should_panic tests
        if( test.panic_type != ::AST::TestDesc::ShouldPanic::No )
            continue ;

        ::AST::ExprNode_StructLiteral::t_values   desc_vals;
        // `name: "foo",`
        desc_vals.push_back({ {}, "name", NEWNODE(_CallPath,
                        ::AST::Path("test", { ::AST::PathNode("StaticTestName") }),
                        ::make_vec1( NEWNODE(_String,  test.name) )
                        ) });
        // `ignore: false,`
        desc_vals.push_back({ {}, "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({ {}, "should_panic", mv$(should_panic_val) });
        }
        if( TARGETVER_1_29 )
        {
            // TODO: Get this from attributes
            desc_vals.push_back({ {}, "allow_fail", NEWNODE(_Bool, false) });
        }
        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({ {}, RcString::new_interned("desc"), mv$(desc_expr) });

        auto test_type_var_name  = test.is_benchmark ? "StaticBenchFn" : "StaticTestFn";
        descandfn_vals.push_back({ {}, RcString::new_interned("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(Span(), false, "std", "std", {});
    newmod.add_ext_crate(Span(), false, "test", "test", {});

    newmod.add_item(Span(), false, "main", mv$(main_fn), {});
    newmod.add_item(Span(), false, "TESTS", mv$(tests_list), {});

    crate.m_root_module.add_item(Span(), false, "test#", mv$(newmod), {});
    crate.m_lang_items["mrustc-main"] = ::AST::Path("", { AST::PathNode("test#"), AST::PathNode("main") });
}