xref: /aosp_15_r20/external/federated-compute/fcp/testing/testing.h (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 #ifndef FCP_TESTING_TESTING_H_
18*14675a02SAndroid Build Coastguard Worker #define FCP_TESTING_TESTING_H_
19*14675a02SAndroid Build Coastguard Worker 
20*14675a02SAndroid Build Coastguard Worker #include <iostream>
21*14675a02SAndroid Build Coastguard Worker #include <memory>
22*14675a02SAndroid Build Coastguard Worker #include <string>
23*14675a02SAndroid Build Coastguard Worker #include <type_traits>
24*14675a02SAndroid Build Coastguard Worker 
25*14675a02SAndroid Build Coastguard Worker #include "google/protobuf/util/message_differencer.h"
26*14675a02SAndroid Build Coastguard Worker #include "gmock/gmock.h"
27*14675a02SAndroid Build Coastguard Worker #include "gtest/gtest.h"
28*14675a02SAndroid Build Coastguard Worker #include "absl/status/status.h"
29*14675a02SAndroid Build Coastguard Worker #include "absl/strings/string_view.h"
30*14675a02SAndroid Build Coastguard Worker #include "fcp/base/error.h"
31*14675a02SAndroid Build Coastguard Worker #include "fcp/base/monitoring.h"
32*14675a02SAndroid Build Coastguard Worker #include "fcp/base/platform.h"
33*14675a02SAndroid Build Coastguard Worker #include "fcp/base/result.h"
34*14675a02SAndroid Build Coastguard Worker #include "fcp/base/source_location.h"
35*14675a02SAndroid Build Coastguard Worker #include "fcp/testing/result_matchers.h"
36*14675a02SAndroid Build Coastguard Worker 
37*14675a02SAndroid Build Coastguard Worker #include "fcp/testing/parse_text_proto.h"
38*14675a02SAndroid Build Coastguard Worker 
39*14675a02SAndroid Build Coastguard Worker // This file defines platform dependent utilities for testing,
40*14675a02SAndroid Build Coastguard Worker // based on the public version of googletest.
41*14675a02SAndroid Build Coastguard Worker 
42*14675a02SAndroid Build Coastguard Worker namespace fcp {
43*14675a02SAndroid Build Coastguard Worker 
44*14675a02SAndroid Build Coastguard Worker // A macro for use inside a GTest test that executes the provided code as a
45*14675a02SAndroid Build Coastguard Worker // function returning a Result and asserts that the return value is not an
46*14675a02SAndroid Build Coastguard Worker // Error.
47*14675a02SAndroid Build Coastguard Worker //
48*14675a02SAndroid Build Coastguard Worker // The code provided to the macro will be much like the code one would write in
49*14675a02SAndroid Build Coastguard Worker // the body of a regular test, with the differences being that the code must
50*14675a02SAndroid Build Coastguard Worker // return Result<Unit>, and only EXPECT_* statements are allowed, not ASSERT_*.
51*14675a02SAndroid Build Coastguard Worker //
52*14675a02SAndroid Build Coastguard Worker // This makes it possible to greatly simplify the test body by using FCP_TRY(),
53*14675a02SAndroid Build Coastguard Worker // rather than having to check in the test body that every return value of
54*14675a02SAndroid Build Coastguard Worker // Result type is not an error.
55*14675a02SAndroid Build Coastguard Worker //
56*14675a02SAndroid Build Coastguard Worker // Example:
57*14675a02SAndroid Build Coastguard Worker //
58*14675a02SAndroid Build Coastguard Worker //   TEST(FooTest, GetFoo) {
59*14675a02SAndroid Build Coastguard Worker //     FCP_EXPECT_NO_ERROR(
60*14675a02SAndroid Build Coastguard Worker //       Foo foo = FCP_TRY(GetFoo());
61*14675a02SAndroid Build Coastguard Worker //       EXPECT_TRUE(foo.HasBar());
62*14675a02SAndroid Build Coastguard Worker //       return Unit{};
63*14675a02SAndroid Build Coastguard Worker //     );
64*14675a02SAndroid Build Coastguard Worker //   }
65*14675a02SAndroid Build Coastguard Worker #define FCP_EXPECT_NO_ERROR(test_contents)           \
66*14675a02SAndroid Build Coastguard Worker   auto test_fn = []() -> Result<Unit> test_contents; \
67*14675a02SAndroid Build Coastguard Worker   ASSERT_THAT(test_fn(), testing::Not(IsError()))
68*14675a02SAndroid Build Coastguard Worker 
69*14675a02SAndroid Build Coastguard Worker // Convenience macros for `EXPECT_THAT(s, IsOk())`, where `s` is either
70*14675a02SAndroid Build Coastguard Worker // a `Status` or a `StatusOr<T>`.
71*14675a02SAndroid Build Coastguard Worker // Old versions of the protobuf library define EXPECT_OK as well, so we only
72*14675a02SAndroid Build Coastguard Worker // conditionally define our version.
73*14675a02SAndroid Build Coastguard Worker #if !defined(EXPECT_OK)
74*14675a02SAndroid Build Coastguard Worker #define EXPECT_OK(result) EXPECT_THAT(result, fcp::IsOk())
75*14675a02SAndroid Build Coastguard Worker #endif
76*14675a02SAndroid Build Coastguard Worker #define ASSERT_OK(result) ASSERT_THAT(result, fcp::IsOk())
77*14675a02SAndroid Build Coastguard Worker 
78*14675a02SAndroid Build Coastguard Worker /** Returns the current test's name. */
79*14675a02SAndroid Build Coastguard Worker std::string TestName();
80*14675a02SAndroid Build Coastguard Worker 
81*14675a02SAndroid Build Coastguard Worker /**
82*14675a02SAndroid Build Coastguard Worker  * Gets path to a test data file based on a path relative to project root.
83*14675a02SAndroid Build Coastguard Worker  */
84*14675a02SAndroid Build Coastguard Worker std::string GetTestDataPath(absl::string_view relative_path);
85*14675a02SAndroid Build Coastguard Worker 
86*14675a02SAndroid Build Coastguard Worker /**
87*14675a02SAndroid Build Coastguard Worker  * Creates a temporary file name with given suffix unique for the running test.
88*14675a02SAndroid Build Coastguard Worker  */
89*14675a02SAndroid Build Coastguard Worker std::string TemporaryTestFile(absl::string_view suffix);
90*14675a02SAndroid Build Coastguard Worker 
91*14675a02SAndroid Build Coastguard Worker /**
92*14675a02SAndroid Build Coastguard Worker  * Verifies a provided content against an expected stored in a baseline file.
93*14675a02SAndroid Build Coastguard Worker  * Returns an empty string if both are identical, otherwise a diagnostic
94*14675a02SAndroid Build Coastguard Worker  * message for error reports.
95*14675a02SAndroid Build Coastguard Worker  *
96*14675a02SAndroid Build Coastguard Worker  * A return status of not ok indicates an operational error which made the
97*14675a02SAndroid Build Coastguard Worker  * comparison impossible.
98*14675a02SAndroid Build Coastguard Worker  *
99*14675a02SAndroid Build Coastguard Worker  * The baseline file name must be provided relative to the project root.
100*14675a02SAndroid Build Coastguard Worker  */
101*14675a02SAndroid Build Coastguard Worker StatusOr<std::string> VerifyAgainstBaseline(absl::string_view baseline_file,
102*14675a02SAndroid Build Coastguard Worker                                             absl::string_view content);
103*14675a02SAndroid Build Coastguard Worker 
104*14675a02SAndroid Build Coastguard Worker /**
105*14675a02SAndroid Build Coastguard Worker  * Polymorphic matchers for Status or StatusOr on status code.
106*14675a02SAndroid Build Coastguard Worker  */
107*14675a02SAndroid Build Coastguard Worker template <typename T>
IsCode(StatusOr<T> const & x,StatusCode code)108*14675a02SAndroid Build Coastguard Worker bool IsCode(StatusOr<T> const& x, StatusCode code) {
109*14675a02SAndroid Build Coastguard Worker   return x.status().code() == code;
110*14675a02SAndroid Build Coastguard Worker }
IsCode(Status const & x,StatusCode code)111*14675a02SAndroid Build Coastguard Worker inline bool IsCode(Status const& x, StatusCode code) {
112*14675a02SAndroid Build Coastguard Worker   return x.code() == code;
113*14675a02SAndroid Build Coastguard Worker }
114*14675a02SAndroid Build Coastguard Worker 
115*14675a02SAndroid Build Coastguard Worker template <typename T>
116*14675a02SAndroid Build Coastguard Worker class StatusMatcherImpl : public ::testing::MatcherInterface<T> {
117*14675a02SAndroid Build Coastguard Worker  public:
StatusMatcherImpl(StatusCode code)118*14675a02SAndroid Build Coastguard Worker   explicit StatusMatcherImpl(StatusCode code) : code_(code) {}
DescribeTo(::std::ostream * os)119*14675a02SAndroid Build Coastguard Worker   void DescribeTo(::std::ostream* os) const override {
120*14675a02SAndroid Build Coastguard Worker     *os << "is " << absl::StatusCodeToString(code_);
121*14675a02SAndroid Build Coastguard Worker   }
DescribeNegationTo(::std::ostream * os)122*14675a02SAndroid Build Coastguard Worker   void DescribeNegationTo(::std::ostream* os) const override {
123*14675a02SAndroid Build Coastguard Worker     *os << "is not " << absl::StatusCodeToString(code_);
124*14675a02SAndroid Build Coastguard Worker   }
MatchAndExplain(T x,::testing::MatchResultListener * listener)125*14675a02SAndroid Build Coastguard Worker   bool MatchAndExplain(
126*14675a02SAndroid Build Coastguard Worker       T x, ::testing::MatchResultListener* listener) const override {
127*14675a02SAndroid Build Coastguard Worker     return IsCode(x, code_);
128*14675a02SAndroid Build Coastguard Worker   }
129*14675a02SAndroid Build Coastguard Worker 
130*14675a02SAndroid Build Coastguard Worker  private:
131*14675a02SAndroid Build Coastguard Worker   StatusCode code_;
132*14675a02SAndroid Build Coastguard Worker };
133*14675a02SAndroid Build Coastguard Worker 
134*14675a02SAndroid Build Coastguard Worker class StatusMatcher {
135*14675a02SAndroid Build Coastguard Worker  public:
StatusMatcher(StatusCode code)136*14675a02SAndroid Build Coastguard Worker   explicit StatusMatcher(StatusCode code) : code_(code) {}
137*14675a02SAndroid Build Coastguard Worker 
138*14675a02SAndroid Build Coastguard Worker   template <typename T>
139*14675a02SAndroid Build Coastguard Worker   operator testing::Matcher<T>() const {  // NOLINT
140*14675a02SAndroid Build Coastguard Worker     return ::testing::MakeMatcher(new StatusMatcherImpl<T>(code_));
141*14675a02SAndroid Build Coastguard Worker   }
142*14675a02SAndroid Build Coastguard Worker 
143*14675a02SAndroid Build Coastguard Worker  private:
144*14675a02SAndroid Build Coastguard Worker   StatusCode code_;
145*14675a02SAndroid Build Coastguard Worker };
146*14675a02SAndroid Build Coastguard Worker 
147*14675a02SAndroid Build Coastguard Worker StatusMatcher IsCode(StatusCode code);
148*14675a02SAndroid Build Coastguard Worker StatusMatcher IsOk();
149*14675a02SAndroid Build Coastguard Worker 
150*14675a02SAndroid Build Coastguard Worker template <typename T>
151*14675a02SAndroid Build Coastguard Worker class ProtoMatcherImpl : public ::testing::MatcherInterface<T> {
152*14675a02SAndroid Build Coastguard Worker  public:
ProtoMatcherImpl(const google::protobuf::Message & arg)153*14675a02SAndroid Build Coastguard Worker   explicit ProtoMatcherImpl(const google::protobuf::Message& arg)
154*14675a02SAndroid Build Coastguard Worker       : arg_(CloneMessage(arg)) {}
155*14675a02SAndroid Build Coastguard Worker 
ProtoMatcherImpl(const std::string & arg)156*14675a02SAndroid Build Coastguard Worker   explicit ProtoMatcherImpl(const std::string& arg) : arg_(ParseMessage(arg)) {}
157*14675a02SAndroid Build Coastguard Worker 
DescribeTo(::std::ostream * os)158*14675a02SAndroid Build Coastguard Worker   void DescribeTo(::std::ostream* os) const override {
159*14675a02SAndroid Build Coastguard Worker     *os << "is " << arg_->DebugString();
160*14675a02SAndroid Build Coastguard Worker   }
DescribeNegationTo(::std::ostream * os)161*14675a02SAndroid Build Coastguard Worker   void DescribeNegationTo(::std::ostream* os) const override {
162*14675a02SAndroid Build Coastguard Worker     *os << "is not " << arg_->DebugString();
163*14675a02SAndroid Build Coastguard Worker   }
MatchAndExplain(T x,::testing::MatchResultListener * listener)164*14675a02SAndroid Build Coastguard Worker   bool MatchAndExplain(
165*14675a02SAndroid Build Coastguard Worker       T x, ::testing::MatchResultListener* listener) const override {
166*14675a02SAndroid Build Coastguard Worker     if (x.GetDescriptor()->full_name() != arg_->GetDescriptor()->full_name()) {
167*14675a02SAndroid Build Coastguard Worker       *listener << "Argument proto is of type "
168*14675a02SAndroid Build Coastguard Worker                 << arg_->GetDescriptor()->full_name()
169*14675a02SAndroid Build Coastguard Worker                 << " but expected proto of type "
170*14675a02SAndroid Build Coastguard Worker                 << x.GetDescriptor()->full_name();
171*14675a02SAndroid Build Coastguard Worker       return false;
172*14675a02SAndroid Build Coastguard Worker     }
173*14675a02SAndroid Build Coastguard Worker 
174*14675a02SAndroid Build Coastguard Worker     google::protobuf::util::MessageDifferencer differencer;
175*14675a02SAndroid Build Coastguard Worker     std::string reported_differences;
176*14675a02SAndroid Build Coastguard Worker     differencer.ReportDifferencesToString(&reported_differences);
177*14675a02SAndroid Build Coastguard Worker     if (!differencer.Compare(*arg_, x)) {
178*14675a02SAndroid Build Coastguard Worker       *listener << reported_differences;
179*14675a02SAndroid Build Coastguard Worker       return false;
180*14675a02SAndroid Build Coastguard Worker     }
181*14675a02SAndroid Build Coastguard Worker     return true;
182*14675a02SAndroid Build Coastguard Worker   }
183*14675a02SAndroid Build Coastguard Worker 
184*14675a02SAndroid Build Coastguard Worker  private:
CloneMessage(const google::protobuf::Message & message)185*14675a02SAndroid Build Coastguard Worker   static std::unique_ptr<google::protobuf::Message> CloneMessage(
186*14675a02SAndroid Build Coastguard Worker       const google::protobuf::Message& message) {
187*14675a02SAndroid Build Coastguard Worker     std::unique_ptr<google::protobuf::Message> copy_of_message =
188*14675a02SAndroid Build Coastguard Worker         absl::WrapUnique(message.New());
189*14675a02SAndroid Build Coastguard Worker     copy_of_message->CopyFrom(message);
190*14675a02SAndroid Build Coastguard Worker     return copy_of_message;
191*14675a02SAndroid Build Coastguard Worker   }
192*14675a02SAndroid Build Coastguard Worker 
ParseMessage(const std::string & proto_text)193*14675a02SAndroid Build Coastguard Worker   static std::unique_ptr<google::protobuf::Message> ParseMessage(
194*14675a02SAndroid Build Coastguard Worker       const std::string& proto_text) {
195*14675a02SAndroid Build Coastguard Worker     using V = std::remove_cv_t<std::remove_reference_t<T>>;
196*14675a02SAndroid Build Coastguard Worker     std::unique_ptr<V> message = std::make_unique<V>();
197*14675a02SAndroid Build Coastguard Worker     *message = PARSE_TEXT_PROTO(proto_text);
198*14675a02SAndroid Build Coastguard Worker     return message;
199*14675a02SAndroid Build Coastguard Worker   }
200*14675a02SAndroid Build Coastguard Worker 
201*14675a02SAndroid Build Coastguard Worker   std::unique_ptr<google::protobuf::Message> arg_;
202*14675a02SAndroid Build Coastguard Worker };
203*14675a02SAndroid Build Coastguard Worker 
204*14675a02SAndroid Build Coastguard Worker template <typename T>
205*14675a02SAndroid Build Coastguard Worker class ProtoMatcher {
206*14675a02SAndroid Build Coastguard Worker  public:
ProtoMatcher(const T & arg)207*14675a02SAndroid Build Coastguard Worker   explicit ProtoMatcher(const T& arg) : arg_(arg) {}
208*14675a02SAndroid Build Coastguard Worker 
209*14675a02SAndroid Build Coastguard Worker   template <typename U>
210*14675a02SAndroid Build Coastguard Worker   operator testing::Matcher<U>() const {  // NOLINT
211*14675a02SAndroid Build Coastguard Worker     using V = std::remove_cv_t<std::remove_reference_t<U>>;
212*14675a02SAndroid Build Coastguard Worker     static_assert(std::is_base_of<google::protobuf::Message, V>::value &&
213*14675a02SAndroid Build Coastguard Worker                   !std::is_same<google::protobuf::Message, V>::value);
214*14675a02SAndroid Build Coastguard Worker     return ::testing::MakeMatcher(new ProtoMatcherImpl<U>(arg_));
215*14675a02SAndroid Build Coastguard Worker   }
216*14675a02SAndroid Build Coastguard Worker 
217*14675a02SAndroid Build Coastguard Worker  private:
218*14675a02SAndroid Build Coastguard Worker   T arg_;
219*14675a02SAndroid Build Coastguard Worker };
220*14675a02SAndroid Build Coastguard Worker 
221*14675a02SAndroid Build Coastguard Worker // Proto matcher that takes another proto message reference as an argument.
222*14675a02SAndroid Build Coastguard Worker template <class T,
223*14675a02SAndroid Build Coastguard Worker           typename std::enable_if<std::is_base_of<google::protobuf::Message, T>::value &&
224*14675a02SAndroid Build Coastguard Worker                                       !std::is_same<google::protobuf::Message, T>::value,
225*14675a02SAndroid Build Coastguard Worker                                   int>::type = 0>
EqualsProto(const T & arg)226*14675a02SAndroid Build Coastguard Worker inline ProtoMatcher<T> EqualsProto(const T& arg) {
227*14675a02SAndroid Build Coastguard Worker   return ProtoMatcher<T>(arg);
228*14675a02SAndroid Build Coastguard Worker }
229*14675a02SAndroid Build Coastguard Worker 
230*14675a02SAndroid Build Coastguard Worker // Proto matcher that takes a text proto as an argument.
EqualsProto(const std::string & arg)231*14675a02SAndroid Build Coastguard Worker inline ProtoMatcher<std::string> EqualsProto(const std::string& arg) {
232*14675a02SAndroid Build Coastguard Worker   return ProtoMatcher<std::string>(arg);
233*14675a02SAndroid Build Coastguard Worker }
234*14675a02SAndroid Build Coastguard Worker 
235*14675a02SAndroid Build Coastguard Worker // Utility function which creates and traces an instance of test error
236*14675a02SAndroid Build Coastguard Worker Error TraceTestError(SourceLocation loc = SourceLocation::current());
237*14675a02SAndroid Build Coastguard Worker 
238*14675a02SAndroid Build Coastguard Worker }  // namespace fcp
239*14675a02SAndroid Build Coastguard Worker 
240*14675a02SAndroid Build Coastguard Worker #endif  // FCP_TESTING_TESTING_H_
241