File indexing completed on 2025-08-28 08:27:01
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
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 {
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
0063
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 {
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 {
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 {
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 {
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 {
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
0310
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
0319 template <typename ValueMatcher>
0320 ResultMatcher<ValueMatcher> ResultWith(const ValueMatcher& value_matcher) {
0321 return ResultMatcher<ValueMatcher>(value_matcher);
0322 }
0323
0324
0325 inline OkMatcher Ok() { return {}; }
0326
0327
0328
0329 inline ErrorMatcher Raises(StatusCode code) { return ErrorMatcher(code, std::nullopt); }
0330
0331
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
0340
0341
0342 explicit DataEqMatcher(Datum expected) : expected_(std::move(expected)) {}
0343
0344 template <typename Data>
0345 operator testing::Matcher<Data>() const {
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
0410 template <typename Data>
0411 DataEqMatcher DataEq(Data&& dat) {
0412 return DataEqMatcher(Datum(std::forward<Data>(dat)));
0413 }
0414
0415
0416 inline DataEqMatcher DataEqArray(const std::shared_ptr<DataType>& type,
0417 std::string_view json) {
0418 return DataEq(ArrayFromJSON(type, json));
0419 }
0420
0421
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
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
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
0446 inline DataEqMatcher DataEqScalar(const std::shared_ptr<DataType>& type,
0447 std::string_view json) {
0448 return DataEq(ScalarFromJSON(type, json));
0449 }
0450
0451
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
0466
0467 }