1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 #include "google/protobuf/compiler/command_line_interface_tester.h"
9
10 #include <cstddef>
11 #include <string>
12 #include <vector>
13
14 #include "google/protobuf/testing/file.h"
15 #include "google/protobuf/testing/file.h"
16 #include "google/protobuf/testing/file.h"
17 #include <gmock/gmock.h>
18 #include "google/protobuf/testing/googletest.h"
19 #include <gtest/gtest.h>
20 #include "absl/log/absl_check.h"
21 #include "absl/status/status.h"
22 #include "absl/strings/str_cat.h"
23 #include "absl/strings/str_replace.h"
24 #include "absl/strings/str_split.h"
25 #include "absl/strings/string_view.h"
26
27 namespace google {
28 namespace protobuf {
29 namespace compiler {
30 namespace {
31
32 using ::testing::HasSubstr;
33
FileExists(const std::string & path)34 bool FileExists(const std::string& path) {
35 return File::Exists(path);
36 }
37
38 } // namespace
39
CommandLineInterfaceTester()40 CommandLineInterfaceTester::CommandLineInterfaceTester() {
41 temp_directory_ = absl::StrCat(TestTempDir(), "/proto2_cli_test_temp");
42
43 // If the temp directory already exists, it must be left over from a
44 // previous run. Delete it.
45 if (FileExists(temp_directory_)) {
46 File::DeleteRecursively(temp_directory_, NULL, NULL);
47 }
48
49 // Create the temp directory.
50 ABSL_CHECK_OK(File::CreateDir(temp_directory_, 0777));
51 }
52
~CommandLineInterfaceTester()53 CommandLineInterfaceTester::~CommandLineInterfaceTester() {
54 // Delete the temp directory.
55 if (FileExists(temp_directory_)) {
56 File::DeleteRecursively(temp_directory_, NULL, NULL);
57 }
58 }
59
RunProtoc(absl::string_view command)60 void CommandLineInterfaceTester::RunProtoc(absl::string_view command) {
61 RunProtocWithArgs(absl::StrSplit(command, ' ', absl::SkipEmpty()));
62 }
63
RunProtocWithArgs(std::vector<std::string> args)64 void CommandLineInterfaceTester::RunProtocWithArgs(
65 std::vector<std::string> args) {
66 std::vector<const char*> argv(args.size());
67
68 for (size_t i = 0; i < args.size(); i++) {
69 args[i] = absl::StrReplaceAll(args[i], {{"$tmpdir", temp_directory_}});
70 argv[i] = args[i].c_str();
71 }
72
73 // TODO: Cygwin doesn't work well if we try to capture stderr and
74 // stdout at the same time. Need to figure out why and add this capture back
75 // for Cygwin.
76 #if !defined(__CYGWIN__)
77 CaptureTestStdout();
78 #endif
79 CaptureTestStderr();
80
81 return_code_ = cli_.Run(static_cast<int>(args.size()), argv.data());
82
83 error_text_ = GetCapturedTestStderr();
84 #if !defined(__CYGWIN__)
85 captured_stdout_ = GetCapturedTestStdout();
86 #endif
87 }
88
89 // -------------------------------------------------------------------
90
CreateTempFile(absl::string_view name,absl::string_view contents)91 void CommandLineInterfaceTester::CreateTempFile(absl::string_view name,
92 absl::string_view contents) {
93 // Create parent directory, if necessary.
94 std::string::size_type slash_pos = name.find_last_of('/');
95 if (slash_pos != std::string::npos) {
96 absl::string_view dir = name.substr(0, slash_pos);
97 if (!FileExists(absl::StrCat(temp_directory_, "/", dir))) {
98 ABSL_CHECK_OK(File::RecursivelyCreateDir(
99 absl::StrCat(temp_directory_, "/", dir), 0777));
100 }
101 }
102
103 // Write file.
104 std::string full_name = absl::StrCat(temp_directory_, "/", name);
105 ABSL_CHECK_OK(File::SetContents(
106 full_name, absl::StrReplaceAll(contents, {{"$tmpdir", temp_directory_}}),
107 true));
108 }
109
CreateTempDir(absl::string_view name)110 void CommandLineInterfaceTester::CreateTempDir(absl::string_view name) {
111 ABSL_CHECK_OK(File::RecursivelyCreateDir(
112 absl::StrCat(temp_directory_, "/", name), 0777));
113 }
114
115 // -------------------------------------------------------------------
116
ExpectNoErrors()117 void CommandLineInterfaceTester::ExpectNoErrors() {
118 EXPECT_EQ(0, return_code_);
119 EXPECT_EQ("", error_text_);
120 }
121
ExpectErrorText(absl::string_view expected_text)122 void CommandLineInterfaceTester::ExpectErrorText(
123 absl::string_view expected_text) {
124 EXPECT_NE(0, return_code_);
125 EXPECT_EQ(absl::StrReplaceAll(expected_text, {{"$tmpdir", temp_directory_}}),
126 error_text_);
127 }
128
ExpectErrorSubstring(absl::string_view expected_substring)129 void CommandLineInterfaceTester::ExpectErrorSubstring(
130 absl::string_view expected_substring) {
131 EXPECT_NE(0, return_code_);
132 EXPECT_THAT(error_text_, HasSubstr(expected_substring));
133 }
134
ExpectWarningSubstring(absl::string_view expected_substring)135 void CommandLineInterfaceTester::ExpectWarningSubstring(
136 absl::string_view expected_substring) {
137 EXPECT_EQ(0, return_code_);
138 EXPECT_THAT(error_text_, HasSubstr(expected_substring));
139 }
140
141 #if defined(_WIN32) && !defined(__CYGWIN__)
HasAlternateErrorSubstring(const std::string & expected_substring)142 bool CommandLineInterfaceTester::HasAlternateErrorSubstring(
143 const std::string& expected_substring) {
144 EXPECT_NE(0, return_code_);
145 return error_text_.find(expected_substring) != std::string::npos;
146 }
147 #endif // _WIN32 && !__CYGWIN__
148
ExpectCapturedStdout(absl::string_view expected_text)149 void CommandLineInterfaceTester::ExpectCapturedStdout(
150 absl::string_view expected_text) {
151 EXPECT_EQ(expected_text, captured_stdout_);
152 }
153
154 void CommandLineInterfaceTester::
ExpectCapturedStdoutSubstringWithZeroReturnCode(absl::string_view expected_substring)155 ExpectCapturedStdoutSubstringWithZeroReturnCode(
156 absl::string_view expected_substring) {
157 EXPECT_EQ(0, return_code_);
158 EXPECT_THAT(captured_stdout_, HasSubstr(expected_substring));
159 }
160
161 void CommandLineInterfaceTester::
ExpectCapturedStderrSubstringWithZeroReturnCode(absl::string_view expected_substring)162 ExpectCapturedStderrSubstringWithZeroReturnCode(
163 absl::string_view expected_substring) {
164 EXPECT_EQ(0, return_code_);
165 EXPECT_THAT(error_text_, HasSubstr(expected_substring));
166 }
167
ExpectFileContent(absl::string_view filename,absl::string_view content)168 void CommandLineInterfaceTester::ExpectFileContent(absl::string_view filename,
169 absl::string_view content) {
170 std::string path = absl::StrCat(temp_directory_, "/", filename);
171 std::string file_contents;
172 ABSL_CHECK_OK(File::GetContents(path, &file_contents, true));
173
174 EXPECT_EQ(absl::StrReplaceAll(content, {{"$tmpdir", temp_directory_}}),
175 file_contents);
176 }
177
178 } // namespace compiler
179 } // namespace protobuf
180 } // namespace google
181