Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / base / test / launcher / test_results_tracker.cc
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/test/launcher/test_results_tracker.h"
6
7 #include "base/base64.h"
8 #include "base/command_line.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/format_macros.h"
12 #include "base/json/json_file_value_serializer.h"
13 #include "base/json/string_escape.h"
14 #include "base/logging.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/test/launcher/test_launcher.h"
18 #include "base/values.h"
19
20 namespace base {
21
22 namespace {
23
24 // The default output file for XML output.
25 const FilePath::CharType kDefaultOutputFile[] = FILE_PATH_LITERAL(
26     "test_detail.xml");
27
28 // Utility function to print a list of test names. Uses iterator to be
29 // compatible with different containers, like vector and set.
30 template<typename InputIterator>
31 void PrintTests(InputIterator first,
32                 InputIterator last,
33                 const std::string& description) {
34   size_t count = std::distance(first, last);
35   if (count == 0)
36     return;
37
38   fprintf(stdout,
39           "%" PRIuS " test%s %s:\n",
40           count,
41           count != 1 ? "s" : "",
42           description.c_str());
43   for (InputIterator i = first; i != last; ++i)
44     fprintf(stdout, "    %s\n", (*i).c_str());
45   fflush(stdout);
46 }
47
48 std::string TestNameWithoutDisabledPrefix(const std::string& test_name) {
49   std::string test_name_no_disabled(test_name);
50   ReplaceSubstringsAfterOffset(&test_name_no_disabled, 0, "DISABLED_", "");
51   return test_name_no_disabled;
52 }
53
54 }  // namespace
55
56 TestResultsTracker::TestResultsTracker() : iteration_(-1), out_(NULL) {
57 }
58
59 TestResultsTracker::~TestResultsTracker() {
60   DCHECK(thread_checker_.CalledOnValidThread());
61
62   if (!out_)
63     return;
64   fprintf(out_, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
65   fprintf(out_, "<testsuites name=\"AllTests\" tests=\"\" failures=\"\""
66           " disabled=\"\" errors=\"\" time=\"\">\n");
67
68   // Maps test case names to test results.
69   typedef std::map<std::string, std::vector<TestResult> > TestCaseMap;
70   TestCaseMap test_case_map;
71
72   for (PerIterationData::ResultsMap::iterator i =
73            per_iteration_data_[iteration_].results.begin();
74        i != per_iteration_data_[iteration_].results.end();
75        ++i) {
76     // Use the last test result as the final one.
77     TestResult result = i->second.test_results.back();
78     test_case_map[result.GetTestCaseName()].push_back(result);
79   }
80   for (TestCaseMap::iterator i = test_case_map.begin();
81        i != test_case_map.end();
82        ++i) {
83     fprintf(out_, "  <testsuite name=\"%s\" tests=\"%" PRIuS "\" failures=\"\""
84             " disabled=\"\" errors=\"\" time=\"\">\n",
85             i->first.c_str(), i->second.size());
86     for (size_t j = 0; j < i->second.size(); ++j) {
87       const TestResult& result = i->second[j];
88       fprintf(out_, "    <testcase name=\"%s\" status=\"run\" time=\"%.3f\""
89               " classname=\"%s\">\n",
90               result.GetTestName().c_str(),
91               result.elapsed_time.InSecondsF(),
92               result.GetTestCaseName().c_str());
93       if (result.status != TestResult::TEST_SUCCESS)
94         fprintf(out_, "      <failure message=\"\" type=\"\"></failure>\n");
95       fprintf(out_, "    </testcase>\n");
96     }
97     fprintf(out_, "  </testsuite>\n");
98   }
99   fprintf(out_, "</testsuites>\n");
100   fclose(out_);
101 }
102
103 bool TestResultsTracker::Init(const CommandLine& command_line) {
104   DCHECK(thread_checker_.CalledOnValidThread());
105
106   // Prevent initializing twice.
107   if (out_) {
108     NOTREACHED();
109     return false;
110   }
111
112   if (!command_line.HasSwitch(kGTestOutputFlag))
113     return true;
114
115   std::string flag = command_line.GetSwitchValueASCII(kGTestOutputFlag);
116   size_t colon_pos = flag.find(':');
117   FilePath path;
118   if (colon_pos != std::string::npos) {
119     FilePath flag_path =
120         command_line.GetSwitchValuePath(kGTestOutputFlag);
121     FilePath::StringType path_string = flag_path.value();
122     path = FilePath(path_string.substr(colon_pos + 1));
123     // If the given path ends with '/', consider it is a directory.
124     // Note: This does NOT check that a directory (or file) actually exists
125     // (the behavior is same as what gtest does).
126     if (path.EndsWithSeparator()) {
127       FilePath executable = command_line.GetProgram().BaseName();
128       path = path.Append(executable.ReplaceExtension(
129                              FilePath::StringType(FILE_PATH_LITERAL("xml"))));
130     }
131   }
132   if (path.value().empty())
133     path = FilePath(kDefaultOutputFile);
134   FilePath dir_name = path.DirName();
135   if (!DirectoryExists(dir_name)) {
136     LOG(WARNING) << "The output directory does not exist. "
137                  << "Creating the directory: " << dir_name.value();
138     // Create the directory if necessary (because the gtest does the same).
139     if (!base::CreateDirectory(dir_name)) {
140       LOG(ERROR) << "Failed to created directory " << dir_name.value();
141       return false;
142     }
143   }
144   out_ = OpenFile(path, "w");
145   if (!out_) {
146     LOG(ERROR) << "Cannot open output file: "
147                << path.value() << ".";
148     return false;
149   }
150
151   return true;
152 }
153
154 void TestResultsTracker::OnTestIterationStarting() {
155   DCHECK(thread_checker_.CalledOnValidThread());
156
157   // Start with a fresh state for new iteration.
158   iteration_++;
159   per_iteration_data_.push_back(PerIterationData());
160 }
161
162 void TestResultsTracker::AddTest(const std::string& test_name) {
163   // Record disabled test names without DISABLED_ prefix so that they are easy
164   // to compare with regular test names, e.g. before or after disabling.
165   all_tests_.insert(TestNameWithoutDisabledPrefix(test_name));
166 }
167
168 void TestResultsTracker::AddDisabledTest(const std::string& test_name) {
169   // Record disabled test names without DISABLED_ prefix so that they are easy
170   // to compare with regular test names, e.g. before or after disabling.
171   disabled_tests_.insert(TestNameWithoutDisabledPrefix(test_name));
172 }
173
174 void TestResultsTracker::AddTestResult(const TestResult& result) {
175   DCHECK(thread_checker_.CalledOnValidThread());
176
177   per_iteration_data_[iteration_].results[
178       result.full_name].test_results.push_back(result);
179 }
180
181 void TestResultsTracker::PrintSummaryOfCurrentIteration() const {
182   TestStatusMap tests_by_status(GetTestStatusMapForCurrentIteration());
183
184   PrintTests(tests_by_status[TestResult::TEST_FAILURE].begin(),
185              tests_by_status[TestResult::TEST_FAILURE].end(),
186              "failed");
187   PrintTests(tests_by_status[TestResult::TEST_FAILURE_ON_EXIT].begin(),
188              tests_by_status[TestResult::TEST_FAILURE_ON_EXIT].end(),
189              "failed on exit");
190   PrintTests(tests_by_status[TestResult::TEST_TIMEOUT].begin(),
191              tests_by_status[TestResult::TEST_TIMEOUT].end(),
192              "timed out");
193   PrintTests(tests_by_status[TestResult::TEST_CRASH].begin(),
194              tests_by_status[TestResult::TEST_CRASH].end(),
195              "crashed");
196   PrintTests(tests_by_status[TestResult::TEST_SKIPPED].begin(),
197              tests_by_status[TestResult::TEST_SKIPPED].end(),
198              "skipped");
199   PrintTests(tests_by_status[TestResult::TEST_UNKNOWN].begin(),
200              tests_by_status[TestResult::TEST_UNKNOWN].end(),
201              "had unknown result");
202 }
203
204 void TestResultsTracker::PrintSummaryOfAllIterations() const {
205   DCHECK(thread_checker_.CalledOnValidThread());
206
207   TestStatusMap tests_by_status(GetTestStatusMapForAllIterations());
208
209   fprintf(stdout, "Summary of all test iterations:\n");
210   fflush(stdout);
211
212   PrintTests(tests_by_status[TestResult::TEST_FAILURE].begin(),
213              tests_by_status[TestResult::TEST_FAILURE].end(),
214              "failed");
215   PrintTests(tests_by_status[TestResult::TEST_FAILURE_ON_EXIT].begin(),
216              tests_by_status[TestResult::TEST_FAILURE_ON_EXIT].end(),
217              "failed on exit");
218   PrintTests(tests_by_status[TestResult::TEST_TIMEOUT].begin(),
219              tests_by_status[TestResult::TEST_TIMEOUT].end(),
220              "timed out");
221   PrintTests(tests_by_status[TestResult::TEST_CRASH].begin(),
222              tests_by_status[TestResult::TEST_CRASH].end(),
223              "crashed");
224   PrintTests(tests_by_status[TestResult::TEST_SKIPPED].begin(),
225              tests_by_status[TestResult::TEST_SKIPPED].end(),
226              "skipped");
227   PrintTests(tests_by_status[TestResult::TEST_UNKNOWN].begin(),
228              tests_by_status[TestResult::TEST_UNKNOWN].end(),
229              "had unknown result");
230
231   fprintf(stdout, "End of the summary.\n");
232   fflush(stdout);
233 }
234
235 void TestResultsTracker::AddGlobalTag(const std::string& tag) {
236   global_tags_.insert(tag);
237 }
238
239 bool TestResultsTracker::SaveSummaryAsJSON(const FilePath& path) const {
240   scoped_ptr<DictionaryValue> summary_root(new DictionaryValue);
241
242   ListValue* global_tags = new ListValue;
243   summary_root->Set("global_tags", global_tags);
244
245   for (std::set<std::string>::const_iterator i = global_tags_.begin();
246        i != global_tags_.end();
247        ++i) {
248     global_tags->AppendString(*i);
249   }
250
251   ListValue* all_tests = new ListValue;
252   summary_root->Set("all_tests", all_tests);
253
254   for (std::set<std::string>::const_iterator i = all_tests_.begin();
255        i != all_tests_.end();
256        ++i) {
257     all_tests->AppendString(*i);
258   }
259
260   ListValue* disabled_tests = new ListValue;
261   summary_root->Set("disabled_tests", disabled_tests);
262
263   for (std::set<std::string>::const_iterator i = disabled_tests_.begin();
264        i != disabled_tests_.end();
265        ++i) {
266     disabled_tests->AppendString(*i);
267   }
268
269   ListValue* per_iteration_data = new ListValue;
270   summary_root->Set("per_iteration_data", per_iteration_data);
271
272   for (int i = 0; i <= iteration_; i++) {
273     DictionaryValue* current_iteration_data = new DictionaryValue;
274     per_iteration_data->Append(current_iteration_data);
275
276     for (PerIterationData::ResultsMap::const_iterator j =
277              per_iteration_data_[i].results.begin();
278          j != per_iteration_data_[i].results.end();
279          ++j) {
280       ListValue* test_results = new ListValue;
281       current_iteration_data->SetWithoutPathExpansion(j->first, test_results);
282
283       for (size_t k = 0; k < j->second.test_results.size(); k++) {
284         const TestResult& test_result = j->second.test_results[k];
285
286         DictionaryValue* test_result_value = new DictionaryValue;
287         test_results->Append(test_result_value);
288
289         test_result_value->SetString("status", test_result.StatusAsString());
290         test_result_value->SetInteger(
291             "elapsed_time_ms",
292             static_cast<int>(test_result.elapsed_time.InMilliseconds()));
293
294         // There are no guarantees about character encoding of the output
295         // snippet. Escape it and record whether it was losless.
296         // It's useful to have the output snippet as string in the summary
297         // for easy viewing.
298         std::string escaped_output_snippet;
299         bool losless_snippet = EscapeJSONString(
300             test_result.output_snippet, false, &escaped_output_snippet);
301         test_result_value->SetString("output_snippet",
302                                      escaped_output_snippet);
303         test_result_value->SetBoolean("losless_snippet", losless_snippet);
304
305         // Also include the raw version (base64-encoded so that it can be safely
306         // JSON-serialized - there are no guarantees about character encoding
307         // of the snippet). This can be very useful piece of information when
308         // debugging a test failure related to character encoding.
309         std::string base64_output_snippet;
310         Base64Encode(test_result.output_snippet, &base64_output_snippet);
311         test_result_value->SetString("output_snippet_base64",
312                                      base64_output_snippet);
313       }
314     }
315   }
316
317   JSONFileValueSerializer serializer(path);
318   return serializer.Serialize(*summary_root);
319 }
320
321 TestResultsTracker::TestStatusMap
322     TestResultsTracker::GetTestStatusMapForCurrentIteration() const {
323   TestStatusMap tests_by_status;
324   GetTestStatusForIteration(iteration_, &tests_by_status);
325   return tests_by_status;
326 }
327
328 TestResultsTracker::TestStatusMap
329     TestResultsTracker::GetTestStatusMapForAllIterations() const {
330   TestStatusMap tests_by_status;
331   for (int i = 0; i <= iteration_; i++)
332     GetTestStatusForIteration(i, &tests_by_status);
333   return tests_by_status;
334 }
335
336 void TestResultsTracker::GetTestStatusForIteration(
337     int iteration, TestStatusMap* map) const {
338   for (PerIterationData::ResultsMap::const_iterator j =
339            per_iteration_data_[iteration].results.begin();
340        j != per_iteration_data_[iteration].results.end();
341        ++j) {
342     // Use the last test result as the final one.
343     const TestResult& result = j->second.test_results.back();
344     (*map)[result.status].insert(result.full_name);
345   }
346 }
347
348 TestResultsTracker::AggregateTestResult::AggregateTestResult() {
349 }
350
351 TestResultsTracker::AggregateTestResult::~AggregateTestResult() {
352 }
353
354 TestResultsTracker::PerIterationData::PerIterationData() {
355 }
356
357 TestResultsTracker::PerIterationData::~PerIterationData() {
358 }
359
360 }  // namespace base