1 // Copyright 2013 The Chromium Authors
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 "testing/data_driven_testing/data_driven_test.h"
6
7 #include "base/files/file_enumerator.h"
8 #include "base/files/file_util.h"
9 #include "base/strings/string_util.h"
10 #include "base/threading/thread_restrictions.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12 #include "third_party/re2/src/re2/re2.h"
13
14 namespace testing {
15 namespace {
16
17 // Reads |file| into |content|, and converts Windows line-endings to Unix ones.
18 // Returns true on success.
ReadFile(const base::FilePath & file,std::string * content)19 bool ReadFile(const base::FilePath& file, std::string* content) {
20 if (!base::ReadFileToString(file, content))
21 return false;
22
23 base::ReplaceSubstringsAfterOffset(content, 0, "\r\n", "\n");
24 return true;
25 }
26
27 // Write |content| to |file|. Returns true on success.
WriteFile(const base::FilePath & file,const std::string & content)28 bool WriteFile(const base::FilePath& file, const std::string& content) {
29 return base::WriteFile(file, content);
30 }
31
32 // Removes lines starting with (optional) whitespace and a #.
StripComments(std::string * content)33 void StripComments(std::string* content) {
34 RE2::GlobalReplace(
35 content,
36 // Enable multi-line mode, ^ and $ match begin/end line in addition to
37 // begin/end text.
38 "(?m)"
39 // Search for start of lines (^), ignore spaces (\\s*), and then look for
40 // '#'.
41 "^\\s*#"
42 // Consume all characters (.*) until end of line ($).
43 ".*$"
44 // Consume the line wrapping so that the entire line is gone.
45 "[\\r\\n]*",
46 // Replace entire line with empty string.
47 "");
48 }
49
50 } // namespace
51
RunDataDrivenTest(const base::FilePath & input_directory,const base::FilePath & output_directory,const base::FilePath::StringType & file_name_pattern)52 void DataDrivenTest::RunDataDrivenTest(
53 const base::FilePath& input_directory,
54 const base::FilePath& output_directory,
55 const base::FilePath::StringType& file_name_pattern) {
56 base::ScopedAllowBlockingForTesting allow_blocking;
57 ASSERT_TRUE(base::DirectoryExists(input_directory));
58 ASSERT_TRUE(base::DirectoryExists(output_directory));
59 base::FileEnumerator input_files(
60 input_directory, false, base::FileEnumerator::FILES, file_name_pattern);
61 const bool kIsExpectedToPass = true;
62 for (base::FilePath input_file = input_files.Next(); !input_file.empty();
63 input_file = input_files.Next()) {
64 RunOneDataDrivenTest(input_file, output_directory, kIsExpectedToPass);
65 }
66 }
67
RunOneDataDrivenTest(const base::FilePath & test_file_name,const base::FilePath & output_directory,bool is_expected_to_pass)68 void DataDrivenTest::RunOneDataDrivenTest(
69 const base::FilePath& test_file_name,
70 const base::FilePath& output_directory,
71 bool is_expected_to_pass) {
72 base::ScopedAllowBlockingForTesting allow_blocking;
73 // iOS doesn't get rid of removed test files. TODO(estade): remove this after
74 // all iOS bots are clobbered.
75 if (test_file_name.BaseName().value() == FILE_PATH_LITERAL("multimerge.in"))
76 return;
77
78 ASSERT_TRUE(base::DirectoryExists(output_directory));
79 SCOPED_TRACE(test_file_name.BaseName().value());
80
81 std::string input;
82 ReadFile(test_file_name, &input);
83
84 std::string output;
85 {
86 base::ScopedDisallowBlocking disallow_blocking;
87 GenerateResults(input, &output);
88 }
89
90 base::FilePath output_file = output_directory.Append(
91 test_file_name.BaseName().StripTrailingSeparators().ReplaceExtension(
92 FILE_PATH_LITERAL(".out")));
93
94 std::string output_file_contents;
95 if (!ReadFile(output_file, &output_file_contents)) {
96 ASSERT_TRUE(WriteFile(output_file, output));
97 return;
98 }
99 // Remove comment lines (lead by '#' character).
100 StripComments(&output_file_contents);
101
102 if (is_expected_to_pass) {
103 EXPECT_EQ(output_file_contents, output);
104 } else {
105 EXPECT_NE(output_file_contents, output);
106 }
107 }
108
GetInputDirectory()109 base::FilePath DataDrivenTest::GetInputDirectory() {
110 return test_data_directory_.Append(feature_directory_)
111 .Append(test_name_)
112 .AppendASCII("input");
113 }
114
GetOutputDirectory()115 base::FilePath DataDrivenTest::GetOutputDirectory() {
116 return test_data_directory_.Append(feature_directory_)
117 .Append(test_name_)
118 .AppendASCII("output");
119 }
120
DataDrivenTest(const base::FilePath & test_data_directory,const base::FilePath::StringType & feature_directory,const base::FilePath::StringType & test_name)121 DataDrivenTest::DataDrivenTest(
122 const base::FilePath& test_data_directory,
123 const base::FilePath::StringType& feature_directory,
124 const base::FilePath::StringType& test_name)
125 : test_data_directory_(test_data_directory),
126 feature_directory_(feature_directory),
127 test_name_(test_name) {}
128
~DataDrivenTest()129 DataDrivenTest::~DataDrivenTest() {}
130
131 } // namespace testing
132