]> git.tdb.fi Git - libs/test.git/blob - source/test.cpp
Display failure reason after other test output
[libs/test.git] / source / test.cpp
1 #include <typeinfo>
2 #include <msp/debug/demangle.h>
3 #include <msp/io/print.h>
4 #include <msp/strings/utils.h>
5 #include "runner.h"
6 #include "test.h"
7
8 using namespace std;
9
10 namespace {
11
12 /* This makes the linker pull in the Runner class when linking msptest as a
13 static library */
14 void *dummy()
15 { return new Msp::Test::Runner(0, 0); }
16
17 }
18
19
20 namespace Msp {
21 namespace Test {
22
23 Test::Test(const string &n):
24         name(n)
25 { }
26
27 Test::~Test()
28 {
29         for(list<Function *>::iterator i=functions.begin(); i!=functions.end(); ++i)
30                 delete *i;
31 }
32
33 list<Test::Factory *> &Test::get_factories()
34 {
35         static list<Factory *> factories;
36         return factories;
37 }
38
39 void Test::run_single(const string &name, bool verbose)
40 {
41         const list<Factory *> &factories = get_factories();
42         for(list<Factory *>::const_iterator i=factories.begin(); i!=factories.end(); ++i)
43                 if((*i)->get_name()==name)
44                 {
45                         Test *test = (*i)->create();
46                         test->run(verbose);
47                         delete test;
48                 }
49 }
50
51 void Test::run_all(bool verbose)
52 {
53         const list<Factory *> &factories = get_factories();
54         IO::print("Running %d test suites\n", factories.size());
55         for(list<Factory *>::const_iterator i=factories.begin(); i!=factories.end(); ++i)
56         {
57                 Test *test = (*i)->create();
58                 test->run(verbose);
59                 delete test;
60         }
61 }
62
63 void Test::run(bool verbose)
64 {
65         IO::print("Running tests for %s\n", name);
66
67         unsigned n_passed = 0;
68         unsigned total = 0;
69         for(list<Function *>::const_iterator i=functions.begin(); i!=functions.end(); ++i)
70         {
71                 detail_info = string();
72                 detail_debug = string();
73                 fail_reason = string();
74                 passed = false;
75                 start_test((*i)->get_description());
76                 const ExceptionCheck *exc_check = (*i)->get_exception_check();
77                 try
78                 {
79                         (*i)->run(*this);
80                         if(exc_check)
81                                 fail_test("Exception expected but none thrown");
82                         else
83                                 pass_test();
84                 }
85                 catch(const test_failed &e)
86                 {
87                         fail_test(e.what());
88                 }
89                 catch(const exception &e)
90                 {
91                         if(exc_check && exc_check->check(e))
92                         {
93                                 pass_test();
94                                 debug(Debug::demangle(typeid(e).name()));
95                                 debug(e.what());
96                         }
97                         else
98                                 fail_test(e.what());
99                 }
100                 catch(...)
101                 {
102                         fail_test("Unknown object thrown");
103                 }
104
105                 if(verbose)
106                         detail_info += detail_debug;
107                 if(!passed)
108                 {
109                         if(fail_reason.empty())
110                                 detail_info += "Failed without reason";
111                         else
112                                 detail_info += fail_reason;
113                 }
114                 if(!detail_info.empty())
115                 {
116                         vector<string> lines = split(detail_info, '\n');
117                         for(vector<string>::const_iterator j=lines.begin(); j!=lines.end(); ++j)
118                                 IO::print("    %s\n", *j);
119                 }
120
121                 ++total;
122                 if(passed)
123                         ++n_passed;
124         }
125
126         IO::print("  %d/%d passed\n", n_passed, total);
127 }
128
129 void Test::start_test(const string &descr)
130 {
131         IO::print("  %s: ", descr);
132 }
133
134 void Test::pass_test()
135 {
136         IO::print("\033[32mok\033[0m\n");
137         passed = true;
138 }
139
140 void Test::fail_test(const string &why)
141 {
142         IO::print("\033[31mfailed\033[0m\n");
143         fail_reason = why;
144 }
145
146 void Test::expect(bool cond, const string &expr)
147 {
148         if(!cond)
149                 throw test_failed(format("!(%s)", expr));
150         debug(expr);
151 }
152
153 void Test::info(const string &str)
154 {
155         detail_info += format("%s\n", str);
156 }
157
158 void Test::debug(const string &str)
159 {
160         detail_debug += format("%s\n", str);
161 }
162
163 void Test::fail(const string &why)
164 {
165         throw test_failed(why);
166 }
167
168
169 Test::Factory::Factory()
170 {
171         get_factories().push_back(this);
172 }
173
174 } // namespace Test
175 } // namespace Msp