]> git.tdb.fi Git - libs/test.git/blob - source/test.cpp
Better output for exception tests
[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)
92                         {
93                                 if(exc_check->check(e))
94                                 {
95                                         pass_test();
96                                         debug(Debug::demangle(typeid(e).name()));
97                                         debug(e.what());
98                                 }
99                                 else
100                                 {
101                                         fail_test("Exception of incorrect type thrown");
102                                         debug(format("%s (expected %s)", Debug::demangle(typeid(e).name()), Debug::demangle(exc_check->get_type_info().name())));
103                                         debug(e.what());
104                                 }
105                         }
106                         else
107                         {
108                                 debug(Debug::demangle(typeid(e).name()));
109                                 fail_test(e.what());
110                         }
111                 }
112                 catch(...)
113                 {
114                         fail_test("Unknown object thrown");
115                 }
116
117                 if(verbose)
118                         detail_info += detail_debug;
119                 if(!passed)
120                 {
121                         if(fail_reason.empty())
122                                 detail_info += "Failed without reason";
123                         else
124                                 detail_info += fail_reason;
125                 }
126                 if(!detail_info.empty())
127                 {
128                         vector<string> lines = split(detail_info, '\n');
129                         for(vector<string>::const_iterator j=lines.begin(); j!=lines.end(); ++j)
130                                 IO::print("    %s\n", *j);
131                 }
132
133                 ++total;
134                 if(passed)
135                         ++n_passed;
136         }
137
138         IO::print("  %d/%d passed\n", n_passed, total);
139 }
140
141 void Test::start_test(const string &descr)
142 {
143         IO::print("  %s: ", descr);
144 }
145
146 void Test::pass_test()
147 {
148         IO::print("\033[32mok\033[0m\n");
149         passed = true;
150 }
151
152 void Test::fail_test(const string &why)
153 {
154         IO::print("\033[31mfailed\033[0m\n");
155         fail_reason = why;
156 }
157
158 void Test::expect(bool cond, const string &expr)
159 {
160         if(!cond)
161                 throw test_failed(format("!(%s)", expr));
162         debug(expr);
163 }
164
165 void Test::info(const string &str)
166 {
167         detail_info += format("%s\n", str);
168 }
169
170 void Test::debug(const string &str)
171 {
172         detail_debug += format("%s\n", str);
173 }
174
175 void Test::fail(const string &why)
176 {
177         throw test_failed(why);
178 }
179
180
181 Test::Factory::Factory()
182 {
183         get_factories().push_back(this);
184 }
185
186 } // namespace Test
187 } // namespace Msp