xref: /aosp_15_r20/external/federated-compute/fcp/testing/testing.cc (revision 14675a029014e728ec732f129a32e299b2da0601)
1*14675a02SAndroid Build Coastguard Worker /*
2*14675a02SAndroid Build Coastguard Worker  * Copyright 2017 Google LLC
3*14675a02SAndroid Build Coastguard Worker  *
4*14675a02SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*14675a02SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*14675a02SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*14675a02SAndroid Build Coastguard Worker  *
8*14675a02SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*14675a02SAndroid Build Coastguard Worker  *
10*14675a02SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*14675a02SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*14675a02SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*14675a02SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*14675a02SAndroid Build Coastguard Worker  * limitations under the License.
15*14675a02SAndroid Build Coastguard Worker  */
16*14675a02SAndroid Build Coastguard Worker 
17*14675a02SAndroid Build Coastguard Worker #include "fcp/testing/testing.h"
18*14675a02SAndroid Build Coastguard Worker 
19*14675a02SAndroid Build Coastguard Worker #include <stdio.h>
20*14675a02SAndroid Build Coastguard Worker #include <stdlib.h>
21*14675a02SAndroid Build Coastguard Worker 
22*14675a02SAndroid Build Coastguard Worker #include <filesystem>
23*14675a02SAndroid Build Coastguard Worker #include <string>
24*14675a02SAndroid Build Coastguard Worker 
25*14675a02SAndroid Build Coastguard Worker #include "gtest/gtest.h"
26*14675a02SAndroid Build Coastguard Worker #include "absl/flags/flag.h"
27*14675a02SAndroid Build Coastguard Worker #include "absl/status/status.h"
28*14675a02SAndroid Build Coastguard Worker #include "absl/strings/str_cat.h"
29*14675a02SAndroid Build Coastguard Worker #include "absl/strings/str_replace.h"
30*14675a02SAndroid Build Coastguard Worker #include "absl/strings/string_view.h"
31*14675a02SAndroid Build Coastguard Worker #include "fcp/base/base_name.h"
32*14675a02SAndroid Build Coastguard Worker #include "fcp/base/monitoring.h"
33*14675a02SAndroid Build Coastguard Worker #include "fcp/base/platform.h"
34*14675a02SAndroid Build Coastguard Worker #include "fcp/testing/tracing_schema.h"
35*14675a02SAndroid Build Coastguard Worker #include "fcp/tracing/tracing_span.h"
36*14675a02SAndroid Build Coastguard Worker 
37*14675a02SAndroid Build Coastguard Worker namespace fcp {
38*14675a02SAndroid Build Coastguard Worker 
TestName()39*14675a02SAndroid Build Coastguard Worker std::string TestName() {
40*14675a02SAndroid Build Coastguard Worker   auto test_info = testing::UnitTest::GetInstance()->current_test_info();
41*14675a02SAndroid Build Coastguard Worker   return absl::StrReplaceAll(test_info->name(), {{"/", "_"}});
42*14675a02SAndroid Build Coastguard Worker }
43*14675a02SAndroid Build Coastguard Worker 
TestCaseName()44*14675a02SAndroid Build Coastguard Worker std::string TestCaseName() {
45*14675a02SAndroid Build Coastguard Worker   auto test_info = testing::UnitTest::GetInstance()->current_test_info();
46*14675a02SAndroid Build Coastguard Worker   return absl::StrReplaceAll(test_info->test_case_name(), {{"/", "_"}});
47*14675a02SAndroid Build Coastguard Worker }
48*14675a02SAndroid Build Coastguard Worker 
GetTestDataPath(absl::string_view relative_path)49*14675a02SAndroid Build Coastguard Worker std::string GetTestDataPath(absl::string_view relative_path) {
50*14675a02SAndroid Build Coastguard Worker   auto env = getenv("TEST_SRCDIR");
51*14675a02SAndroid Build Coastguard Worker   std::string test_srcdir = env ? env : "";
52*14675a02SAndroid Build Coastguard Worker   return ConcatPath(test_srcdir, ConcatPath("com_google_fcp", relative_path));
53*14675a02SAndroid Build Coastguard Worker }
54*14675a02SAndroid Build Coastguard Worker 
TemporaryTestFile(absl::string_view suffix)55*14675a02SAndroid Build Coastguard Worker std::string TemporaryTestFile(absl::string_view suffix) {
56*14675a02SAndroid Build Coastguard Worker   return ConcatPath(StripTrailingPathSeparator(testing::TempDir()),
57*14675a02SAndroid Build Coastguard Worker                     absl::StrCat(TestName(), suffix));
58*14675a02SAndroid Build Coastguard Worker }
59*14675a02SAndroid Build Coastguard Worker 
60*14675a02SAndroid Build Coastguard Worker namespace {
61*14675a02SAndroid Build Coastguard Worker 
EnsureDirExists(absl::string_view path)62*14675a02SAndroid Build Coastguard Worker absl::Status EnsureDirExists(absl::string_view path) {
63*14675a02SAndroid Build Coastguard Worker   if (FileExists(path)) {
64*14675a02SAndroid Build Coastguard Worker     return absl::OkStatus();
65*14675a02SAndroid Build Coastguard Worker   }
66*14675a02SAndroid Build Coastguard Worker   auto path_str = std::string(path);
67*14675a02SAndroid Build Coastguard Worker   int error;
68*14675a02SAndroid Build Coastguard Worker #ifndef _WIN32
69*14675a02SAndroid Build Coastguard Worker   error = mkdir(path_str.c_str(), 0733);
70*14675a02SAndroid Build Coastguard Worker #else
71*14675a02SAndroid Build Coastguard Worker   error = _mkdir(path_str.c_str());
72*14675a02SAndroid Build Coastguard Worker #endif
73*14675a02SAndroid Build Coastguard Worker   if (error) {
74*14675a02SAndroid Build Coastguard Worker     return absl::InternalError(absl::StrCat(
75*14675a02SAndroid Build Coastguard Worker         "cannot create directory ", path_str, "(error code ", error, ")"));
76*14675a02SAndroid Build Coastguard Worker   }
77*14675a02SAndroid Build Coastguard Worker   return absl::OkStatus();
78*14675a02SAndroid Build Coastguard Worker }
79*14675a02SAndroid Build Coastguard Worker 
80*14675a02SAndroid Build Coastguard Worker }  // namespace
81*14675a02SAndroid Build Coastguard Worker 
ShouldUpdateBaseline()82*14675a02SAndroid Build Coastguard Worker bool ShouldUpdateBaseline() {
83*14675a02SAndroid Build Coastguard Worker   return getenv("FCP_UPDATE_BASELINE");
84*14675a02SAndroid Build Coastguard Worker }
85*14675a02SAndroid Build Coastguard Worker 
86*14675a02SAndroid Build Coastguard Worker namespace {
87*14675a02SAndroid Build Coastguard Worker 
MakeTempFileName()88*14675a02SAndroid Build Coastguard Worker std::string MakeTempFileName() {
89*14675a02SAndroid Build Coastguard Worker #ifdef __APPLE__
90*14675a02SAndroid Build Coastguard Worker // Apple has marked tmpnam as deprecated. As we are compiling with -Werror,
91*14675a02SAndroid Build Coastguard Worker // turning this off for this case. Apple recommends to use mkstemp instead,
92*14675a02SAndroid Build Coastguard Worker // but because this opens a file, it's not exactly what we want, and it's not
93*14675a02SAndroid Build Coastguard Worker // portable. std::filesystem in C++17 should fix this issue.
94*14675a02SAndroid Build Coastguard Worker #pragma clang diagnostic push
95*14675a02SAndroid Build Coastguard Worker #pragma clang diagnostic ignored "-Wdeprecated-declarations"
96*14675a02SAndroid Build Coastguard Worker #endif
97*14675a02SAndroid Build Coastguard Worker   return tmpnam(nullptr);
98*14675a02SAndroid Build Coastguard Worker #ifdef __APPLE__
99*14675a02SAndroid Build Coastguard Worker #pragma clang diagnostic pop
100*14675a02SAndroid Build Coastguard Worker #endif
101*14675a02SAndroid Build Coastguard Worker }
102*14675a02SAndroid Build Coastguard Worker 
ShellCommand(absl::string_view command,std::string * stdout_result,std::string * stderr_result)103*14675a02SAndroid Build Coastguard Worker absl::Status ShellCommand(absl::string_view command, std::string* stdout_result,
104*14675a02SAndroid Build Coastguard Worker                           std::string* stderr_result) {
105*14675a02SAndroid Build Coastguard Worker #ifdef _WIN32
106*14675a02SAndroid Build Coastguard Worker   return absl::UnimplementedError("ShellCommand not implemented for Windows");
107*14675a02SAndroid Build Coastguard Worker #else
108*14675a02SAndroid Build Coastguard Worker   // Prepare command for output redirection.
109*14675a02SAndroid Build Coastguard Worker   std::string command_str = std::string(command);
110*14675a02SAndroid Build Coastguard Worker   std::string stdout_file;
111*14675a02SAndroid Build Coastguard Worker   if (stdout_result != nullptr) {
112*14675a02SAndroid Build Coastguard Worker     stdout_file = MakeTempFileName();
113*14675a02SAndroid Build Coastguard Worker     absl::StrAppend(&command_str, " 1>", stdout_file);
114*14675a02SAndroid Build Coastguard Worker   }
115*14675a02SAndroid Build Coastguard Worker   std::string stderr_file;
116*14675a02SAndroid Build Coastguard Worker   if (stderr_result != nullptr) {
117*14675a02SAndroid Build Coastguard Worker     stderr_file = MakeTempFileName();
118*14675a02SAndroid Build Coastguard Worker     absl::StrAppend(&command_str, " 2>", stderr_file);
119*14675a02SAndroid Build Coastguard Worker   }
120*14675a02SAndroid Build Coastguard Worker 
121*14675a02SAndroid Build Coastguard Worker   // Call the command.
122*14675a02SAndroid Build Coastguard Worker   int result = std::system(command_str.c_str());
123*14675a02SAndroid Build Coastguard Worker 
124*14675a02SAndroid Build Coastguard Worker   // Read and remove redirected output.
125*14675a02SAndroid Build Coastguard Worker   if (stdout_result != nullptr) {
126*14675a02SAndroid Build Coastguard Worker     auto status_or_result = ReadFileToString(stdout_file);
127*14675a02SAndroid Build Coastguard Worker     if (status_or_result.ok()) {
128*14675a02SAndroid Build Coastguard Worker       *stdout_result = status_or_result.value();
129*14675a02SAndroid Build Coastguard Worker       std::remove(stdout_file.c_str());
130*14675a02SAndroid Build Coastguard Worker     } else {
131*14675a02SAndroid Build Coastguard Worker       *stdout_result = "";
132*14675a02SAndroid Build Coastguard Worker     }
133*14675a02SAndroid Build Coastguard Worker   }
134*14675a02SAndroid Build Coastguard Worker   if (stderr_result != nullptr) {
135*14675a02SAndroid Build Coastguard Worker     auto status_or_result = ReadFileToString(stderr_file);
136*14675a02SAndroid Build Coastguard Worker     if (status_or_result.ok()) {
137*14675a02SAndroid Build Coastguard Worker       *stderr_result = status_or_result.value();
138*14675a02SAndroid Build Coastguard Worker       std::remove(stderr_file.c_str());
139*14675a02SAndroid Build Coastguard Worker     } else {
140*14675a02SAndroid Build Coastguard Worker       *stderr_result = "";
141*14675a02SAndroid Build Coastguard Worker     }
142*14675a02SAndroid Build Coastguard Worker   }
143*14675a02SAndroid Build Coastguard Worker 
144*14675a02SAndroid Build Coastguard Worker   // Construct result.
145*14675a02SAndroid Build Coastguard Worker   if (result != 0) {
146*14675a02SAndroid Build Coastguard Worker     return absl::InternalError(absl::StrCat(
147*14675a02SAndroid Build Coastguard Worker         "command execution failed: ", command_str, " returns ", result));
148*14675a02SAndroid Build Coastguard Worker   } else {
149*14675a02SAndroid Build Coastguard Worker     return absl::OkStatus();
150*14675a02SAndroid Build Coastguard Worker   }
151*14675a02SAndroid Build Coastguard Worker #endif
152*14675a02SAndroid Build Coastguard Worker }
153*14675a02SAndroid Build Coastguard Worker 
154*14675a02SAndroid Build Coastguard Worker }  // namespace
155*14675a02SAndroid Build Coastguard Worker 
ComputeDiff(absl::string_view baseline_file,absl::string_view content)156*14675a02SAndroid Build Coastguard Worker absl::StatusOr<std::string> ComputeDiff(absl::string_view baseline_file,
157*14675a02SAndroid Build Coastguard Worker                                         absl::string_view content) {
158*14675a02SAndroid Build Coastguard Worker   std::string diff_result;
159*14675a02SAndroid Build Coastguard Worker   std::string baseline_file_str = GetTestDataPath(baseline_file);
160*14675a02SAndroid Build Coastguard Worker   if (!FileExists(baseline_file_str)) {
161*14675a02SAndroid Build Coastguard Worker     diff_result = absl::StrCat("no recorded baseline file ", baseline_file_str);
162*14675a02SAndroid Build Coastguard Worker   } else {
163*14675a02SAndroid Build Coastguard Worker #ifndef _WIN32
164*14675a02SAndroid Build Coastguard Worker     // Expect Unix diff command to be available.
165*14675a02SAndroid Build Coastguard Worker     auto provided_file = TemporaryTestFile(".provided");
166*14675a02SAndroid Build Coastguard Worker     auto status = WriteStringToFile(provided_file, content);
167*14675a02SAndroid Build Coastguard Worker     if (!status.ok()) {
168*14675a02SAndroid Build Coastguard Worker       return status;
169*14675a02SAndroid Build Coastguard Worker     }
170*14675a02SAndroid Build Coastguard Worker     std::string std_out, std_err;
171*14675a02SAndroid Build Coastguard Worker     status = ShellCommand(
172*14675a02SAndroid Build Coastguard Worker         absl::StrCat("diff -u ", baseline_file_str, " ", provided_file),
173*14675a02SAndroid Build Coastguard Worker         &std_out, &std_err);
174*14675a02SAndroid Build Coastguard Worker     std::remove(provided_file.c_str());
175*14675a02SAndroid Build Coastguard Worker     if (status.code() != OK) {
176*14675a02SAndroid Build Coastguard Worker       if (!std_err.empty()) {
177*14675a02SAndroid Build Coastguard Worker         // Indicates a failure in diff execution itself.
178*14675a02SAndroid Build Coastguard Worker         return absl::InternalError(absl::StrCat("command failed: ", std_err));
179*14675a02SAndroid Build Coastguard Worker       }
180*14675a02SAndroid Build Coastguard Worker       diff_result = std_out;
181*14675a02SAndroid Build Coastguard Worker     }
182*14675a02SAndroid Build Coastguard Worker #else  // _WIN32
183*14675a02SAndroid Build Coastguard Worker     // For now we do a simple string compare on Windows.
184*14675a02SAndroid Build Coastguard Worker     auto status_or_string = ReadFileToString(baseline_file_str);
185*14675a02SAndroid Build Coastguard Worker     if (!status_or_string.ok()) {
186*14675a02SAndroid Build Coastguard Worker       return status_or_string.status();
187*14675a02SAndroid Build Coastguard Worker     }
188*14675a02SAndroid Build Coastguard Worker     if (status_or_string.value() != content) {
189*14675a02SAndroid Build Coastguard Worker       diff_result = "baseline and actual differ (see respective files)";
190*14675a02SAndroid Build Coastguard Worker     }
191*14675a02SAndroid Build Coastguard Worker #endif
192*14675a02SAndroid Build Coastguard Worker   }
193*14675a02SAndroid Build Coastguard Worker   return diff_result;
194*14675a02SAndroid Build Coastguard Worker }
195*14675a02SAndroid Build Coastguard Worker 
VerifyAgainstBaseline(absl::string_view baseline_file,absl::string_view content)196*14675a02SAndroid Build Coastguard Worker StatusOr<std::string> VerifyAgainstBaseline(absl::string_view baseline_file,
197*14675a02SAndroid Build Coastguard Worker                                             absl::string_view content) {
198*14675a02SAndroid Build Coastguard Worker   auto status_or_diff_result = ComputeDiff(baseline_file, content);
199*14675a02SAndroid Build Coastguard Worker   if (!status_or_diff_result.ok()) {
200*14675a02SAndroid Build Coastguard Worker     return status_or_diff_result;
201*14675a02SAndroid Build Coastguard Worker   }
202*14675a02SAndroid Build Coastguard Worker   auto& diff_result = status_or_diff_result.value();
203*14675a02SAndroid Build Coastguard Worker   if (diff_result.empty()) {
204*14675a02SAndroid Build Coastguard Worker     // success
205*14675a02SAndroid Build Coastguard Worker     return status_or_diff_result;
206*14675a02SAndroid Build Coastguard Worker   }
207*14675a02SAndroid Build Coastguard Worker 
208*14675a02SAndroid Build Coastguard Worker   // Determine the location where to store the new baseline.
209*14675a02SAndroid Build Coastguard Worker   std::string new_baseline_file;
210*14675a02SAndroid Build Coastguard Worker   bool auto_update = false;
211*14675a02SAndroid Build Coastguard Worker 
212*14675a02SAndroid Build Coastguard Worker   if (new_baseline_file.empty() && ShouldUpdateBaseline()) {
213*14675a02SAndroid Build Coastguard Worker     new_baseline_file = GetTestDataPath(baseline_file);
214*14675a02SAndroid Build Coastguard Worker     diff_result =
215*14675a02SAndroid Build Coastguard Worker         absl::StrCat("\nAutomatically updated baseline file: ", baseline_file);
216*14675a02SAndroid Build Coastguard Worker     auto_update = true;
217*14675a02SAndroid Build Coastguard Worker   }
218*14675a02SAndroid Build Coastguard Worker 
219*14675a02SAndroid Build Coastguard Worker   if (new_baseline_file.empty()) {
220*14675a02SAndroid Build Coastguard Worker     // Store new baseline file in a TMP location.
221*14675a02SAndroid Build Coastguard Worker #ifndef _WIN32
222*14675a02SAndroid Build Coastguard Worker     const char* temp_dir = "/tmp";
223*14675a02SAndroid Build Coastguard Worker #else
224*14675a02SAndroid Build Coastguard Worker     const char* temp_dir = getenv("TEMP");
225*14675a02SAndroid Build Coastguard Worker #endif
226*14675a02SAndroid Build Coastguard Worker     auto temp_output_dir =
227*14675a02SAndroid Build Coastguard Worker         ConcatPath(temp_dir, absl::StrCat("fcp_", TestCaseName()));
228*14675a02SAndroid Build Coastguard Worker     FCP_CHECK_STATUS(EnsureDirExists(temp_output_dir));
229*14675a02SAndroid Build Coastguard Worker     new_baseline_file = ConcatPath(temp_output_dir, BaseName(baseline_file));
230*14675a02SAndroid Build Coastguard Worker     absl::StrAppend(&diff_result, "\nNew baseline file: ", new_baseline_file);
231*14675a02SAndroid Build Coastguard Worker     absl::StrAppend(&diff_result, "\nTo update, use:");
232*14675a02SAndroid Build Coastguard Worker     absl::StrAppend(&diff_result, "\n\n cp ", new_baseline_file, " ",
233*14675a02SAndroid Build Coastguard Worker                     baseline_file, "\n");
234*14675a02SAndroid Build Coastguard Worker   }
235*14675a02SAndroid Build Coastguard Worker 
236*14675a02SAndroid Build Coastguard Worker   if (!auto_update) {
237*14675a02SAndroid Build Coastguard Worker     absl::StrAppend(&diff_result,
238*14675a02SAndroid Build Coastguard Worker                     "\nTo automatically update baseline files, use");
239*14675a02SAndroid Build Coastguard Worker     absl::StrAppend(&diff_result,
240*14675a02SAndroid Build Coastguard Worker                     "\nenvironment variable FCP_UPDATE_BASELINE.");
241*14675a02SAndroid Build Coastguard Worker   }
242*14675a02SAndroid Build Coastguard Worker 
243*14675a02SAndroid Build Coastguard Worker   // Write the new baseline.
244*14675a02SAndroid Build Coastguard Worker   auto status = WriteStringToFile(new_baseline_file, content);
245*14675a02SAndroid Build Coastguard Worker   if (!status.ok()) {
246*14675a02SAndroid Build Coastguard Worker     return status;
247*14675a02SAndroid Build Coastguard Worker   }
248*14675a02SAndroid Build Coastguard Worker 
249*14675a02SAndroid Build Coastguard Worker   // Deliver result.
250*14675a02SAndroid Build Coastguard Worker   if (auto_update) {
251*14675a02SAndroid Build Coastguard Worker     FCP_LOG(INFO) << diff_result;
252*14675a02SAndroid Build Coastguard Worker     diff_result = "";  // make test pass
253*14675a02SAndroid Build Coastguard Worker   }
254*14675a02SAndroid Build Coastguard Worker   return diff_result;
255*14675a02SAndroid Build Coastguard Worker }
256*14675a02SAndroid Build Coastguard Worker 
IsCode(StatusCode code)257*14675a02SAndroid Build Coastguard Worker StatusMatcher IsCode(StatusCode code) { return StatusMatcher(code); }
IsOk()258*14675a02SAndroid Build Coastguard Worker StatusMatcher IsOk() { return IsCode(OK); }
259*14675a02SAndroid Build Coastguard Worker 
TraceTestError(SourceLocation loc)260*14675a02SAndroid Build Coastguard Worker Error TraceTestError(SourceLocation loc) {
261*14675a02SAndroid Build Coastguard Worker   return TraceError<TestError>(loc.file_name(), loc.line());
262*14675a02SAndroid Build Coastguard Worker }
263*14675a02SAndroid Build Coastguard Worker 
264*14675a02SAndroid Build Coastguard Worker }  // namespace fcp
265