Imported Upstream version 0.9
[platform/upstream/syncevolution.git] / test / client-test-main.cpp
1 /*
2  * Copyright (C) 2008 Funambol, Inc.
3  * Copyright (C) 2008-2009 Patrick Ohly <patrick.ohly@gmx.de>
4  * Copyright (C) 2009 Intel Corporation
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) version 3.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301  USA
20  */
21
22 /** @cond API */
23 /** @addtogroup ClientTest */
24 /** @{ */
25
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29
30 #include "test.h"
31
32 #include <cppunit/CompilerOutputter.h>
33 #include <cppunit/ui/text/TestRunner.h>
34 #include <cppunit/TestListener.h>
35 #include <cppunit/TestResult.h>
36 #include <cppunit/TestFailure.h>
37 #include <cppunit/TestResultCollector.h>
38 #include <cppunit/extensions/TestFactoryRegistry.h>
39 #include <cppunit/extensions/HelperMacros.h>
40
41 #include <Logging.h>
42 #include <LogStdout.h>
43
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <string.h>
47 #ifdef HAVE_SIGNAL_H
48 # include <signal.h>
49 #endif
50
51 #include <string>
52 #include <stdexcept>
53 using namespace std;
54
55 void simplifyFilename(string &filename)
56 {
57     size_t pos = 0;
58     while (true) {
59         pos = filename.find(":", pos);
60         if (pos == filename.npos ) {
61             break;
62         }
63         filename.replace(pos, 1, "_");
64     }
65     pos = 0;
66     while (true) {
67         pos = filename.find("__", pos);
68         if (pos == filename.npos) {
69             break;
70         }
71         filename.erase(pos, 1);
72     }
73 }
74
75 class ClientOutputter : public CppUnit::CompilerOutputter {
76 public:
77     ClientOutputter(CppUnit::TestResultCollector *result, std::ostream &stream) :
78         CompilerOutputter(result, stream) {}
79     void write() {
80         CompilerOutputter::write();
81     }
82 };
83
84 class ClientListener : public CppUnit::TestListener {
85 public:
86     ClientListener() :
87         m_failed(false)
88     {
89 #ifdef HAVE_SIGNAL_H
90         // install signal handler which turns an alarm signal into a runtime exception
91         // to abort tests which run too long
92         const char *alarm = getenv("CLIENT_TEST_ALARM");
93         m_alarmSeconds = alarm ? atoi(alarm) : -1;
94
95         struct sigaction action;
96         memset(&action, 0, sizeof(action));
97         action.sa_handler = alarmTriggered;
98         action.sa_flags = SA_NOMASK;
99         sigaction(SIGALRM, &action, NULL);
100 #endif
101     }
102
103     ~ClientListener() {
104         if (&SyncEvolution::LoggerBase::instance() == m_logger.get()) {
105             SyncEvolution::LoggerBase::popLogger();
106         }
107     }
108
109     void addAllowedFailures(string allowedFailures) {
110         size_t start = 0, end;
111         while ((end = allowedFailures.find(',', start)) != allowedFailures.npos) {
112             size_t len = end - start;
113             if (len) {
114                 m_allowedFailures.insert(allowedFailures.substr(start, len));
115             }
116             start = end + 1;
117         }
118         if (allowedFailures.size() > start) {
119             m_allowedFailures.insert(allowedFailures.substr(start));
120         }
121     }
122
123     void startTest (CppUnit::Test *test) {
124         m_currentTest = test->getName();
125         cerr << m_currentTest;
126         string logfile = m_currentTest + ".log";
127         simplifyFilename(logfile);
128         m_logger.reset(new SyncEvolution::LoggerStdout(logfile));
129         m_logger->setLevel(SyncEvolution::Logger::DEBUG);
130         SyncEvolution::LoggerBase::pushLogger(m_logger.get());
131         SE_LOG_DEBUG(NULL, NULL, "*** starting %s ***", m_currentTest.c_str());
132         m_failures.reset();
133         m_testFailed = false;
134
135 #ifdef HAVE_SIGNAL_H
136         if (m_alarmSeconds > 0) {
137             alarm(m_alarmSeconds);
138         }
139 #endif
140     }
141
142     void addFailure(const CppUnit::TestFailure &failure) {
143         m_failures.addFailure(failure);
144         m_testFailed = true;
145     }
146
147     void endTest (CppUnit::Test *test) {
148 #ifdef HAVE_SIGNAL_H
149         if (m_alarmSeconds > 0) {
150             alarm(0);
151         }
152 #endif
153
154         std::string result;
155         std::string failure;
156         if (m_testFailed) {
157             stringstream output;
158             CppUnit::CompilerOutputter formatter(&m_failures, output);
159             formatter.printFailureReport();
160             failure = output.str();
161             if (m_allowedFailures.find(m_currentTest) == m_allowedFailures.end()) {
162                 result = "*** failed ***";
163                 m_failed = true;
164             } else {
165                 result = "*** failure ignored ***";
166             }
167         } else {
168             result = "okay";
169         }
170
171         SE_LOG_DEBUG(NULL, NULL, "*** ending %s: %s ***", m_currentTest.c_str(), result.c_str());
172         if (!failure.empty()) {
173             SE_LOG_DEBUG(NULL, NULL, "%s", failure.c_str());
174         }
175         SyncEvolution::LoggerBase::popLogger();
176         m_logger.reset();
177
178         string logfile = m_currentTest + ".log";
179         simplifyFilename(logfile);
180         
181         const char* compareLog = getenv("CLIENT_TEST_COMPARE_LOG");
182         if(compareLog && strlen(compareLog)) {
183             FILE *fd = fopen ("____compare.log","r");
184             if (fd != NULL) {
185                 fclose(fd);
186                 system ((string("cat ____compare.log >>")+logfile).c_str());
187             }
188         }
189
190         cerr << " " << result << "\n";
191         if (!failure.empty()) {
192             cerr << failure << "\n";
193         }
194     }
195
196     bool hasFailed() { return m_failed; }
197     const string &getCurrentTest() const { return m_currentTest; }
198
199 private:
200     set<string> m_allowedFailures;
201     bool m_failed, m_testFailed;
202     string m_currentTest;
203     int m_alarmSeconds;
204     auto_ptr<SyncEvolution::LoggerStdout> m_logger;
205     CppUnit::TestResultCollector m_failures;
206
207     static void alarmTriggered(int signal) {
208         CPPUNIT_ASSERT_MESSAGE("test timed out", false);
209     }
210 } syncListener;
211
212 const string &getCurrentTest() {
213     return syncListener.getCurrentTest();
214 }
215
216 static void printTests(CppUnit::Test *test, int indention)
217 {
218     if (!test) {
219         return;
220     }
221
222     std::string name = test->getName();
223     printf("%*s%s\n", indention * 3, "", name.c_str());
224     for (int i = 0; i < test->getChildTestCount(); i++) {
225         printTests(test->getChildTestAt(i), indention+1);
226     }
227 }
228
229 int main(int argc, char* argv[])
230 {
231   // Get the top level suite from the registry
232   CppUnit::Test *suite = CppUnit::TestFactoryRegistry::getRegistry().makeTest();
233
234   if (argc >= 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
235       printf("usage: %s [test name]+\n\n"
236              "Without arguments all available tests are run.\n"
237              "Otherwise only the tests or group of tests listed are run.\n"
238              "Here is the test hierarchy of this test program:\n",
239              argv[0]);
240       printTests(suite, 1);
241       return 0;
242   }
243
244   // Adds the test to the list of test to run
245   CppUnit::TextUi::TestRunner runner;
246   runner.addTest( suite );
247
248   // Change the default outputter to a compiler error format outputter
249   runner.setOutputter( new ClientOutputter( &runner.result(),
250                                             std::cerr ) );
251
252   // track current test and failure state
253   const char *allowedFailures = getenv("CLIENT_TEST_FAILURES");
254   if (allowedFailures) {
255       syncListener.addAllowedFailures(allowedFailures);
256   }
257   runner.eventManager().addListener(&syncListener);
258
259   try {
260       // Run the tests.
261       if (argc <= 1) {
262           // all tests
263           runner.run("", false, true, false);
264       } else {
265           // run selected tests individually
266           for (int test = 1; test < argc; test++) {
267               runner.run(argv[test], false, true, false);
268           }
269       }
270
271       // Return error code 1 if the one of test failed.
272       return syncListener.hasFailed() ? 1 : 0;
273   } catch (invalid_argument e) {
274       // Test path not resolved
275       std::cerr << std::endl
276                 << "ERROR: " << e.what()
277                 << std::endl;
278       return 1;
279   }
280 }
281
282 /** @} */
283 /** @endcond */