Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / base / test / launcher / test_results_tracker.cc
blob1e3b2b059470c26bdb32c5b27cbf66d664113557
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.
5 #include "base/test/launcher/test_results_tracker.h"
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"
20 namespace base {
22 namespace {
24 // The default output file for XML output.
25 const FilePath::CharType kDefaultOutputFile[] = FILE_PATH_LITERAL(
26 "test_detail.xml");
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;
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);
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;
54 } // namespace
56 TestResultsTracker::TestResultsTracker() : iteration_(-1), out_(NULL) {
59 TestResultsTracker::~TestResultsTracker() {
60 DCHECK(thread_checker_.CalledOnValidThread());
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");
68 // Maps test case names to test results.
69 typedef std::map<std::string, std::vector<TestResult> > TestCaseMap;
70 TestCaseMap test_case_map;
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);
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");
97 fprintf(out_, " </testsuite>\n");
99 fprintf(out_, "</testsuites>\n");
100 fclose(out_);
103 bool TestResultsTracker::Init(const CommandLine& command_line) {
104 DCHECK(thread_checker_.CalledOnValidThread());
106 // Prevent initializing twice.
107 if (out_) {
108 NOTREACHED();
109 return false;
112 if (!command_line.HasSwitch(kGTestOutputFlag))
113 return true;
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"))));
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;
144 out_ = OpenFile(path, "w");
145 if (!out_) {
146 LOG(ERROR) << "Cannot open output file: "
147 << path.value() << ".";
148 return false;
151 return true;
154 void TestResultsTracker::OnTestIterationStarting() {
155 DCHECK(thread_checker_.CalledOnValidThread());
157 // Start with a fresh state for new iteration.
158 iteration_++;
159 per_iteration_data_.push_back(PerIterationData());
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));
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));
174 void TestResultsTracker::AddTestResult(const TestResult& result) {
175 DCHECK(thread_checker_.CalledOnValidThread());
177 per_iteration_data_[iteration_].results[
178 result.full_name].test_results.push_back(result);
181 void TestResultsTracker::PrintSummaryOfCurrentIteration() const {
182 TestStatusMap tests_by_status(GetTestStatusMapForCurrentIteration());
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");
204 void TestResultsTracker::PrintSummaryOfAllIterations() const {
205 DCHECK(thread_checker_.CalledOnValidThread());
207 TestStatusMap tests_by_status(GetTestStatusMapForAllIterations());
209 fprintf(stdout, "Summary of all test iterations:\n");
210 fflush(stdout);
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");
231 fprintf(stdout, "End of the summary.\n");
232 fflush(stdout);
235 void TestResultsTracker::AddGlobalTag(const std::string& tag) {
236 global_tags_.insert(tag);
239 bool TestResultsTracker::SaveSummaryAsJSON(const FilePath& path) const {
240 scoped_ptr<DictionaryValue> summary_root(new DictionaryValue);
242 ListValue* global_tags = new ListValue;
243 summary_root->Set("global_tags", global_tags);
245 for (std::set<std::string>::const_iterator i = global_tags_.begin();
246 i != global_tags_.end();
247 ++i) {
248 global_tags->AppendString(*i);
251 ListValue* all_tests = new ListValue;
252 summary_root->Set("all_tests", all_tests);
254 for (std::set<std::string>::const_iterator i = all_tests_.begin();
255 i != all_tests_.end();
256 ++i) {
257 all_tests->AppendString(*i);
260 ListValue* disabled_tests = new ListValue;
261 summary_root->Set("disabled_tests", disabled_tests);
263 for (std::set<std::string>::const_iterator i = disabled_tests_.begin();
264 i != disabled_tests_.end();
265 ++i) {
266 disabled_tests->AppendString(*i);
269 ListValue* per_iteration_data = new ListValue;
270 summary_root->Set("per_iteration_data", per_iteration_data);
272 for (int i = 0; i <= iteration_; i++) {
273 DictionaryValue* current_iteration_data = new DictionaryValue;
274 per_iteration_data->Append(current_iteration_data);
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);
283 for (size_t k = 0; k < j->second.test_results.size(); k++) {
284 const TestResult& test_result = j->second.test_results[k];
286 DictionaryValue* test_result_value = new DictionaryValue;
287 test_results->Append(test_result_value);
289 test_result_value->SetString("status", test_result.StatusAsString());
290 test_result_value->SetInteger(
291 "elapsed_time_ms", test_result.elapsed_time.InMilliseconds());
293 // There are no guarantees about character encoding of the output
294 // snippet. Escape it and record whether it was losless.
295 // It's useful to have the output snippet as string in the summary
296 // for easy viewing.
297 std::string escaped_output_snippet;
298 bool losless_snippet = EscapeJSONString(
299 test_result.output_snippet, false, &escaped_output_snippet);
300 test_result_value->SetString("output_snippet",
301 escaped_output_snippet);
302 test_result_value->SetBoolean("losless_snippet", losless_snippet);
304 // Also include the raw version (base64-encoded so that it can be safely
305 // JSON-serialized - there are no guarantees about character encoding
306 // of the snippet). This can be very useful piece of information when
307 // debugging a test failure related to character encoding.
308 std::string base64_output_snippet;
309 Base64Encode(test_result.output_snippet, &base64_output_snippet);
310 test_result_value->SetString("output_snippet_base64",
311 base64_output_snippet);
316 JSONFileValueSerializer serializer(path);
317 return serializer.Serialize(*summary_root);
320 TestResultsTracker::TestStatusMap
321 TestResultsTracker::GetTestStatusMapForCurrentIteration() const {
322 TestStatusMap tests_by_status;
323 GetTestStatusForIteration(iteration_, &tests_by_status);
324 return tests_by_status;
327 TestResultsTracker::TestStatusMap
328 TestResultsTracker::GetTestStatusMapForAllIterations() const {
329 TestStatusMap tests_by_status;
330 for (int i = 0; i <= iteration_; i++)
331 GetTestStatusForIteration(i, &tests_by_status);
332 return tests_by_status;
335 void TestResultsTracker::GetTestStatusForIteration(
336 int iteration, TestStatusMap* map) const {
337 for (PerIterationData::ResultsMap::const_iterator j =
338 per_iteration_data_[iteration].results.begin();
339 j != per_iteration_data_[iteration].results.end();
340 ++j) {
341 // Use the last test result as the final one.
342 const TestResult& result = j->second.test_results.back();
343 (*map)[result.status].insert(result.full_name);
347 TestResultsTracker::AggregateTestResult::AggregateTestResult() {
350 TestResultsTracker::AggregateTestResult::~AggregateTestResult() {
353 TestResultsTracker::PerIterationData::PerIterationData() {
356 TestResultsTracker::PerIterationData::~PerIterationData() {
359 } // namespace base