Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-08-28 08:27:01

0001 // Licensed to the Apache Software Foundation (ASF) under one
0002 // or more contributor license agreements.  See the NOTICE file
0003 // distributed with this work for additional information
0004 // regarding copyright ownership.  The ASF licenses this file
0005 // to you under the Apache License, Version 2.0 (the
0006 // "License"); you may not use this file except in compliance
0007 // with the License.  You may obtain a copy of the License at
0008 //
0009 //   http://www.apache.org/licenses/LICENSE-2.0
0010 //
0011 // Unless required by applicable law or agreed to in writing,
0012 // software distributed under the License is distributed on an
0013 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0014 // KIND, either express or implied.  See the License for the
0015 // specific language governing permissions and limitations
0016 // under the License.
0017 
0018 #pragma once
0019 
0020 #include <utility>
0021 
0022 #include <gmock/gmock-matchers.h>
0023 
0024 #include "arrow/datum.h"
0025 #include "arrow/result.h"
0026 #include "arrow/status.h"
0027 #include "arrow/stl_iterator.h"
0028 #include "arrow/testing/future_util.h"
0029 #include "arrow/testing/gtest_util.h"
0030 #include "arrow/util/future.h"
0031 #include "arrow/util/unreachable.h"
0032 
0033 namespace arrow {
0034 
0035 class PointeesEqualMatcher {
0036  public:
0037   template <typename PtrPair>
0038   operator testing::Matcher<PtrPair>() const {  // NOLINT runtime/explicit
0039     struct Impl : testing::MatcherInterface<const PtrPair&> {
0040       void DescribeTo(::std::ostream* os) const override { *os << "pointees are equal"; }
0041 
0042       void DescribeNegationTo(::std::ostream* os) const override {
0043         *os << "pointees are not equal";
0044       }
0045 
0046       bool MatchAndExplain(const PtrPair& pair,
0047                            testing::MatchResultListener* listener) const override {
0048         const auto& first = *std::get<0>(pair);
0049         const auto& second = *std::get<1>(pair);
0050         const bool match = first.Equals(second);
0051         *listener << "whose pointees " << testing::PrintToString(first) << " and "
0052                   << testing::PrintToString(second)
0053                   << (match ? " are equal" : " are not equal");
0054         return match;
0055       }
0056     };
0057 
0058     return testing::Matcher<PtrPair>(new Impl());
0059   }
0060 };
0061 
0062 // A matcher that checks that the values pointed to are Equals().
0063 // Useful in conjunction with other googletest matchers.
0064 inline PointeesEqualMatcher PointeesEqual() { return {}; }
0065 
0066 class AnyOfJSONMatcher {
0067  public:
0068   AnyOfJSONMatcher(std::shared_ptr<DataType> type, std::string array_json)
0069       : type_(std::move(type)), array_json_(std::move(array_json)) {}
0070 
0071   template <typename arg_type>
0072   operator testing::Matcher<arg_type>() const {  // NOLINT runtime/explicit
0073     struct Impl : testing::MatcherInterface<const arg_type&> {
0074       static_assert(std::is_same<arg_type, std::shared_ptr<Scalar>>(),
0075                     "AnyOfJSON only supported for std::shared_ptr<Scalar>");
0076       Impl(std::shared_ptr<DataType> type, std::string array_json)
0077           : type_(std::move(type)), array_json_(std::move(array_json)) {
0078         array = ArrayFromJSON(type_, array_json_);
0079       }
0080       void DescribeTo(std::ostream* os) const override {
0081         *os << "matches at least one scalar from ";
0082         *os << array->ToString();
0083       }
0084       void DescribeNegationTo(::std::ostream* os) const override {
0085         *os << "matches no scalar from ";
0086         *os << array->ToString();
0087       }
0088       bool MatchAndExplain(
0089           const arg_type& arg,
0090           ::testing::MatchResultListener* result_listener) const override {
0091         for (int64_t i = 0; i < array->length(); ++i) {
0092           std::shared_ptr<Scalar> scalar;
0093           auto maybe_scalar = array->GetScalar(i);
0094           if (maybe_scalar.ok()) {
0095             scalar = maybe_scalar.ValueOrDie();
0096           } else {
0097             *result_listener << "GetScalar() had status "
0098                              << maybe_scalar.status().ToString() << "at index " << i
0099                              << " in the input JSON Array";
0100             return false;
0101           }
0102 
0103           if (scalar->Equals(*arg)) return true;
0104         }
0105         *result_listener << "Argument scalar: '" << arg->ToString()
0106                          << "' matches no scalar from " << array->ToString();
0107         return false;
0108       }
0109       const std::shared_ptr<DataType> type_;
0110       const std::string array_json_;
0111       std::shared_ptr<Array> array;
0112     };
0113 
0114     return testing::Matcher<arg_type>(new Impl(type_, array_json_));
0115   }
0116 
0117  private:
0118   const std::shared_ptr<DataType> type_;
0119   const std::string array_json_;
0120 };
0121 
0122 inline AnyOfJSONMatcher AnyOfJSON(std::shared_ptr<DataType> type,
0123                                   std::string array_json) {
0124   return {std::move(type), std::move(array_json)};
0125 }
0126 
0127 template <typename ResultMatcher>
0128 class FutureMatcher {
0129  public:
0130   explicit FutureMatcher(ResultMatcher result_matcher, double wait_seconds)
0131       : result_matcher_(std::move(result_matcher)), wait_seconds_(wait_seconds) {}
0132 
0133   template <typename Fut,
0134             typename ValueType = typename std::decay<Fut>::type::ValueType>
0135   operator testing::Matcher<Fut>() const {  // NOLINT runtime/explicit
0136     struct Impl : testing::MatcherInterface<const Fut&> {
0137       explicit Impl(const ResultMatcher& result_matcher, double wait_seconds)
0138           : result_matcher_(testing::MatcherCast<Result<ValueType>>(result_matcher)),
0139             wait_seconds_(wait_seconds) {}
0140 
0141       void DescribeTo(::std::ostream* os) const override {
0142         *os << "value ";
0143         result_matcher_.DescribeTo(os);
0144       }
0145 
0146       void DescribeNegationTo(::std::ostream* os) const override {
0147         *os << "value ";
0148         result_matcher_.DescribeNegationTo(os);
0149       }
0150 
0151       bool MatchAndExplain(const Fut& fut,
0152                            testing::MatchResultListener* listener) const override {
0153         if (!fut.Wait(wait_seconds_)) {
0154           *listener << "which didn't finish within " << wait_seconds_ << " seconds";
0155           return false;
0156         }
0157         return result_matcher_.MatchAndExplain(fut.result(), listener);
0158       }
0159 
0160       const testing::Matcher<Result<ValueType>> result_matcher_;
0161       const double wait_seconds_;
0162     };
0163 
0164     return testing::Matcher<Fut>(new Impl(result_matcher_, wait_seconds_));
0165   }
0166 
0167  private:
0168   const ResultMatcher result_matcher_;
0169   const double wait_seconds_;
0170 };
0171 
0172 template <typename ValueMatcher>
0173 class ResultMatcher {
0174  public:
0175   explicit ResultMatcher(ValueMatcher value_matcher)
0176       : value_matcher_(std::move(value_matcher)) {}
0177 
0178   template <typename Res,
0179             typename ValueType = typename std::decay<Res>::type::ValueType>
0180   operator testing::Matcher<Res>() const {  // NOLINT runtime/explicit
0181     struct Impl : testing::MatcherInterface<const Res&> {
0182       explicit Impl(const ValueMatcher& value_matcher)
0183           : value_matcher_(testing::MatcherCast<ValueType>(value_matcher)) {}
0184 
0185       void DescribeTo(::std::ostream* os) const override {
0186         *os << "value ";
0187         value_matcher_.DescribeTo(os);
0188       }
0189 
0190       void DescribeNegationTo(::std::ostream* os) const override {
0191         *os << "value ";
0192         value_matcher_.DescribeNegationTo(os);
0193       }
0194 
0195       bool MatchAndExplain(const Res& maybe_value,
0196                            testing::MatchResultListener* listener) const override {
0197         if (!maybe_value.status().ok()) {
0198           *listener << "whose error "
0199                     << testing::PrintToString(maybe_value.status().ToString())
0200                     << " doesn't match";
0201           return false;
0202         }
0203         const ValueType& value = maybe_value.ValueOrDie();
0204         testing::StringMatchResultListener value_listener;
0205         const bool match = value_matcher_.MatchAndExplain(value, &value_listener);
0206         *listener << "whose value " << testing::PrintToString(value)
0207                   << (match ? " matches" : " doesn't match");
0208         testing::internal::PrintIfNotEmpty(value_listener.str(), listener->stream());
0209         return match;
0210       }
0211 
0212       const testing::Matcher<ValueType> value_matcher_;
0213     };
0214 
0215     return testing::Matcher<Res>(new Impl(value_matcher_));
0216   }
0217 
0218  private:
0219   const ValueMatcher value_matcher_;
0220 };
0221 
0222 class ErrorMatcher {
0223  public:
0224   explicit ErrorMatcher(StatusCode code,
0225                         std::optional<testing::Matcher<std::string>> message_matcher)
0226       : code_(code), message_matcher_(std::move(message_matcher)) {}
0227 
0228   template <typename Res>
0229   operator testing::Matcher<Res>() const {  // NOLINT runtime/explicit
0230     struct Impl : testing::MatcherInterface<const Res&> {
0231       explicit Impl(StatusCode code,
0232                     std::optional<testing::Matcher<std::string>> message_matcher)
0233           : code_(code), message_matcher_(std::move(message_matcher)) {}
0234 
0235       void DescribeTo(::std::ostream* os) const override {
0236         *os << "raises StatusCode::" << Status::CodeAsString(code_);
0237         if (message_matcher_) {
0238           *os << " and message ";
0239           message_matcher_->DescribeTo(os);
0240         }
0241       }
0242 
0243       void DescribeNegationTo(::std::ostream* os) const override {
0244         *os << "does not raise StatusCode::" << Status::CodeAsString(code_);
0245         if (message_matcher_) {
0246           *os << " or message ";
0247           message_matcher_->DescribeNegationTo(os);
0248         }
0249       }
0250 
0251       bool MatchAndExplain(const Res& maybe_value,
0252                            testing::MatchResultListener* listener) const override {
0253         const Status& status = internal::GenericToStatus(maybe_value);
0254         testing::StringMatchResultListener value_listener;
0255 
0256         bool match = status.code() == code_;
0257         if (message_matcher_) {
0258           match = match &&
0259                   message_matcher_->MatchAndExplain(status.message(), &value_listener);
0260         }
0261 
0262         if (match) {
0263           *listener << "whose error matches";
0264         } else if (status.ok()) {
0265           *listener << "whose non-error doesn't match";
0266         } else {
0267           *listener << "whose error doesn't match";
0268         }
0269 
0270         testing::internal::PrintIfNotEmpty(value_listener.str(), listener->stream());
0271         return match;
0272       }
0273 
0274       const StatusCode code_;
0275       const std::optional<testing::Matcher<std::string>> message_matcher_;
0276     };
0277 
0278     return testing::Matcher<Res>(new Impl(code_, message_matcher_));
0279   }
0280 
0281  private:
0282   const StatusCode code_;
0283   const std::optional<testing::Matcher<std::string>> message_matcher_;
0284 };
0285 
0286 class OkMatcher {
0287  public:
0288   template <typename Res>
0289   operator testing::Matcher<Res>() const {  // NOLINT runtime/explicit
0290     struct Impl : testing::MatcherInterface<const Res&> {
0291       void DescribeTo(::std::ostream* os) const override { *os << "is ok"; }
0292 
0293       void DescribeNegationTo(::std::ostream* os) const override { *os << "is not ok"; }
0294 
0295       bool MatchAndExplain(const Res& maybe_value,
0296                            testing::MatchResultListener* listener) const override {
0297         const Status& status = internal::GenericToStatus(maybe_value);
0298 
0299         const bool match = status.ok();
0300         *listener << "whose " << (match ? "non-error matches" : "error doesn't match");
0301         return match;
0302       }
0303     };
0304 
0305     return testing::Matcher<Res>(new Impl());
0306   }
0307 };
0308 
0309 // Returns a matcher that waits on a Future (by default for 16 seconds)
0310 // then applies a matcher to the result.
0311 template <typename ResultMatcher>
0312 FutureMatcher<ResultMatcher> Finishes(
0313     const ResultMatcher& result_matcher,
0314     double wait_seconds = kDefaultAssertFinishesWaitSeconds) {
0315   return FutureMatcher<ResultMatcher>(result_matcher, wait_seconds);
0316 }
0317 
0318 // Returns a matcher that matches the value of a successful Result<T>.
0319 template <typename ValueMatcher>
0320 ResultMatcher<ValueMatcher> ResultWith(const ValueMatcher& value_matcher) {
0321   return ResultMatcher<ValueMatcher>(value_matcher);
0322 }
0323 
0324 // Returns a matcher that matches an ok Status or Result<T>.
0325 inline OkMatcher Ok() { return {}; }
0326 
0327 // Returns a matcher that matches the StatusCode of a Status or Result<T>.
0328 // Do not use Raises(StatusCode::OK) to match a non error code.
0329 inline ErrorMatcher Raises(StatusCode code) { return ErrorMatcher(code, std::nullopt); }
0330 
0331 // Returns a matcher that matches the StatusCode and message of a Status or Result<T>.
0332 template <typename MessageMatcher>
0333 ErrorMatcher Raises(StatusCode code, const MessageMatcher& message_matcher) {
0334   return ErrorMatcher(code, testing::MatcherCast<std::string>(message_matcher));
0335 }
0336 
0337 class DataEqMatcher {
0338  public:
0339   // TODO(bkietz) support EqualOptions, ApproxEquals, etc
0340   // Probably it's better to use something like config-through-key_value_metadata
0341   // as with the random generators to decouple this from EqualOptions etc.
0342   explicit DataEqMatcher(Datum expected) : expected_(std::move(expected)) {}
0343 
0344   template <typename Data>
0345   operator testing::Matcher<Data>() const {  // NOLINT runtime/explicit
0346     struct Impl : testing::MatcherInterface<const Data&> {
0347       explicit Impl(Datum expected) : expected_(std::move(expected)) {}
0348 
0349       void DescribeTo(::std::ostream* os) const override {
0350         *os << "has data ";
0351         PrintTo(expected_, os);
0352       }
0353 
0354       void DescribeNegationTo(::std::ostream* os) const override {
0355         *os << "doesn't have data ";
0356         PrintTo(expected_, os);
0357       }
0358 
0359       bool MatchAndExplain(const Data& data,
0360                            testing::MatchResultListener* listener) const override {
0361         Datum boxed(data);
0362 
0363         if (boxed.kind() != expected_.kind()) {
0364           *listener << "whose Datum::kind " << boxed.ToString() << " doesn't match "
0365                     << expected_.ToString();
0366           return false;
0367         }
0368 
0369         if (const auto& boxed_type = boxed.type()) {
0370           if (*boxed_type != *expected_.type()) {
0371             *listener << "whose DataType " << boxed_type->ToString() << " doesn't match "
0372                       << expected_.type()->ToString();
0373             return false;
0374           }
0375         } else if (const auto& boxed_schema = boxed.schema()) {
0376           if (*boxed_schema != *expected_.schema()) {
0377             *listener << "whose Schema " << boxed_schema->ToString() << " doesn't match "
0378                       << expected_.schema()->ToString();
0379             return false;
0380           }
0381         } else {
0382           Unreachable();
0383         }
0384 
0385         if (boxed == expected_) {
0386           *listener << "whose value matches";
0387           return true;
0388         }
0389 
0390         if (listener->IsInterested() && boxed.kind() == Datum::ARRAY) {
0391           *listener << "whose value differs from the expected value by "
0392                     << boxed.make_array()->Diff(*expected_.make_array());
0393         } else {
0394           *listener << "whose value doesn't match";
0395         }
0396         return false;
0397       }
0398 
0399       Datum expected_;
0400     };
0401 
0402     return testing::Matcher<Data>(new Impl(expected_));
0403   }
0404 
0405  private:
0406   Datum expected_;
0407 };
0408 
0409 /// Constructs a datum against which arguments are matched
0410 template <typename Data>
0411 DataEqMatcher DataEq(Data&& dat) {
0412   return DataEqMatcher(Datum(std::forward<Data>(dat)));
0413 }
0414 
0415 /// Constructs an array with ArrayFromJSON against which arguments are matched
0416 inline DataEqMatcher DataEqArray(const std::shared_ptr<DataType>& type,
0417                                  std::string_view json) {
0418   return DataEq(ArrayFromJSON(type, json));
0419 }
0420 
0421 /// Constructs an array from a vector of optionals against which arguments are matched
0422 template <typename T, typename ArrayType = typename TypeTraits<T>::ArrayType,
0423           typename BuilderType = typename TypeTraits<T>::BuilderType,
0424           typename ValueType =
0425               typename ::arrow::stl::detail::DefaultValueAccessor<ArrayType>::ValueType>
0426 DataEqMatcher DataEqArray(T type, const std::vector<std::optional<ValueType>>& values) {
0427   // FIXME(bkietz) broken until DataType is move constructible
0428   BuilderType builder(std::make_shared<T>(std::move(type)), default_memory_pool());
0429   DCHECK_OK(builder.Reserve(static_cast<int64_t>(values.size())));
0430 
0431   // pseudo constexpr:
0432   static const bool need_safe_append = !is_fixed_width(T::type_id);
0433 
0434   for (auto value : values) {
0435     if (need_safe_append) {
0436       DCHECK_OK(builder.AppendOrNull(value));
0437     } else {
0438       builder.UnsafeAppendOrNull(value);
0439     }
0440   }
0441 
0442   return DataEq(builder.Finish().ValueOrDie());
0443 }
0444 
0445 /// Constructs a scalar with ScalarFromJSON against which arguments are matched
0446 inline DataEqMatcher DataEqScalar(const std::shared_ptr<DataType>& type,
0447                                   std::string_view json) {
0448   return DataEq(ScalarFromJSON(type, json));
0449 }
0450 
0451 /// Constructs a scalar against which arguments are matched
0452 template <typename T, typename ScalarType = typename TypeTraits<T>::ScalarType,
0453           typename ValueType = typename ScalarType::ValueType>
0454 DataEqMatcher DataEqScalar(T type, std::optional<ValueType> value) {
0455   ScalarType expected(std::make_shared<T>(std::move(type)));
0456 
0457   if (value) {
0458     expected.is_valid = true;
0459     expected.value = std::move(*value);
0460   }
0461 
0462   return DataEq(std::move(expected));
0463 }
0464 
0465 // HasType, HasSchema matchers
0466 
0467 }  // namespace arrow