.
1 /*
2 Copyright (C) 2012, 2014 Johan Mattsson
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 3 of the
7 License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13 */
14
15 namespace BirdFont {
16
17 /** Class for executing tests cases. */
18 class TestBirdFont : GLib.Object {
19
20 const int NOT_STARTED = 0;
21 const int RUNNING = 1;
22 const int PAUSED = 2;
23 const int DONE = 3;
24
25 static int state = NOT_STARTED;
26
27 TestCases tests;
28
29 unowned List<Test> test_cases;
30 unowned List<Test?>? current_case = null;
31
32 List<Test> passed;
33 List<Test> failed;
34 List<Test> skipped;
35
36 List<Test> bechmarks;
37
38 static TestBirdFont? singleton = null;
39
40 bool has_failed = false;
41 bool has_skipped = false;
42
43 static bool slow_test = false;
44
45 public string test_cases_to_run; // name of specific test case or all to run all test cases
46
47 public TestBirdFont () {
48 assert (singleton == null);
49 tests = new TestCases ();
50 test_cases = tests.get_test_functions ();
51 current_case = test_cases.first ();
52 test_cases_to_run = "All";
53 from_command_line ();
54 }
55
56 public static bool is_slow_test () {
57 return slow_test;
58 }
59
60 public static void set_slow_test (bool s) {
61 slow_test = s;
62 }
63
64 public static void run_tests () {
65 TestBirdFont t = get_singleton ();
66 state = RUNNING;
67 t.run_all_tests ();
68 }
69
70 public static TestBirdFont get_singleton () {
71 if (singleton == null) {
72 singleton = new TestBirdFont ();
73 }
74
75 return (!) singleton;
76 }
77
78 private bool has_test_case (string s) {
79 foreach (var t in test_cases) {
80 if (t.name == s) return true;
81 }
82
83 return s == "" || s == "All";
84 }
85
86 /** Run only test specified on the command line. */
87 private void from_command_line () {
88 string? stn = BirdFont.get_argument ("--test");
89
90 if (stn != null) {
91 string st = (!) stn;
92
93 if (!has_test_case (st)) {
94 stderr.printf (@"Test case \"$st\" does not exist.\n");
95 stderr.printf ("\nAvailable test cases:\n");
96
97 foreach (var t in test_cases) {
98 stderr.printf (t.name);
99 stderr.printf ("\n");
100 }
101
102 Process.exit(1);
103 }
104
105 if (st == "All" || st == "") {
106 return;
107 } else {
108 stderr.printf (@"Run test case \"$st\" \n");
109 }
110
111 test_cases_to_run = st;
112 }
113 }
114
115 public static void log (string? log_domain, LogLevelFlags log_levels, string message) {
116 Test t = (!)((!)get_singleton ().current_case).data;
117
118 if (log_domain != null) {
119 stderr.printf ("%s: \n", (!) log_domain);
120 }
121
122 stderr.printf ("Testcase \"%s\" failed because:\n", t.name);
123 stderr.printf ("%s\n\n", message);
124
125 get_singleton ().has_failed = true;
126
127 assert (!BirdFont.fatal_wanings);
128 }
129
130 public static void @continue () {
131 int s = AtomicInt.get (ref state);
132 if (s == DONE) {
133 singleton = null;
134 }
135
136 TestBirdFont t = get_singleton ();
137
138 LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING;
139 Log.set_handler (null, levels, log);
140
141 AtomicInt.set (ref state, RUNNING);
142 t.run_all_tests ();
143 }
144
145 public static bool is_running () {
146 int r = AtomicInt.get (ref state);
147 return (r == RUNNING);
148 }
149
150 public static void pause () {
151 AtomicInt.set (ref state, PAUSED);
152 Log.set_handler (null, LogLevelFlags.LEVEL_MASK, Log.default_handler);
153 }
154
155 private static void pad (int t) {
156 for (int i = 0; i < t; i++) {
157 stdout.printf (" ");
158 }
159 }
160
161 public void print_result () {
162 stdout.printf ("\n");
163 stdout.printf ("Test case results:\n");
164
165 foreach (Test t in skipped) {
166 stdout.printf ("%s", t.name);
167 pad (40 - t.name.char_count());
168 stdout.printf ("Skipped\n");
169 }
170
171 if (skipped.length () > 0) {
172 stdout.printf ("\n");
173 }
174
175 foreach (Test t in passed) {
176 stdout.printf ("%s", t.name);
177 pad (40 - t.name.char_count());
178 stdout.printf ("Passed\n");
179 }
180
181 foreach (Test t in failed) {
182 stdout.printf ("%s", t.name);
183 pad (40 - t.name.char_count());
184 stdout.printf ("Failed\n");
185 }
186
187 foreach (Test t in bechmarks) {
188 stdout.printf ("%s", t.name);
189 pad (40 - t.name.char_count());
190 stdout.printf (@"$(t.get_time ())s\n");
191 }
192
193 stdout.printf ("\n");
194 stdout.printf ("Total %u test cases executed, %u passed and %u failed.\n", (passed.length () + failed.length ()), passed.length (), failed.length ());
195 }
196
197 /** Run tests in main loop. */
198 public void run_all_tests () {
199 TimeoutSource idle = new TimeoutSource (20);
200
201 idle.set_callback(() => {
202 int s = AtomicInt.get (ref state);
203
204 if (s != RUNNING || current_case == null) {
205 return false;
206 }
207
208 Test test = (!)((!) current_case).data;
209
210 has_failed = false;
211 has_skipped = false;
212
213 if (test_cases_to_run != "All" && test_cases_to_run != test.name) {
214 has_skipped = true;
215 } else {
216 if (test.is_benchmark ()) {
217 test.timer_start ();
218 test.callback ();
219 test.print ();
220 } else {
221 test.callback ();
222 }
223 }
224
225 if (test.is_benchmark ()) {
226 bechmarks.append ((!) test);
227 } else if (has_failed) {
228 failed.append ((!) test);
229
230 if (BirdFont.has_argument ("--exit")) {
231 print_result ();
232 Process.exit (1);
233 }
234
235 } else if (has_skipped) {
236 skipped.append ((!) test);
237 } else {
238 passed.append ((!) test);
239 }
240
241 if (unlikely (current_case == test_cases.last ())) {
242 stdout.printf ("Finished running test suite.\n");
243
244 AtomicInt.set (ref state, DONE);
245 Log.set_handler (null, LogLevelFlags.LEVEL_MASK, Log.default_handler);
246
247 print_result ();
248
249 if (BirdFont.has_argument ("--exit")) {
250 print_result ();
251 Process.exit ((failed.length () == 0) ? 0 : 1);
252 }
253 return false;
254 }
255
256 current_case = ((!) current_case).next;
257
258 return true;
259 });
260
261 idle.attach (null);
262 }
263
264 }
265
266 }
267