// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. #pragma once #include #include #include #include #include #include #include #include #include #include #include #include "arrow/compare.h" #include "arrow/result.h" #include "arrow/status.h" #include "arrow/testing/gtest_compat.h" #include "arrow/testing/visibility.h" #include "arrow/type_fwd.h" #include "arrow/type_traits.h" #include "arrow/util/macros.h" #include "arrow/util/optional.h" #include "arrow/util/string_builder.h" #include "arrow/util/string_view.h" #include "arrow/util/type_fwd.h" // NOTE: failing must be inline in the macros below, to get correct file / line number // reporting on test failures. // NOTE: using a for loop for this macro allows extra failure messages to be // appended with operator<< #define ASSERT_RAISES(ENUM, expr) \ for (::arrow::Status _st = ::arrow::internal::GenericToStatus((expr)); \ !_st.Is##ENUM();) \ FAIL() << "Expected '" ARROW_STRINGIFY(expr) "' to fail with " ARROW_STRINGIFY( \ ENUM) ", but got " \ << _st.ToString() #define ASSERT_RAISES_WITH_MESSAGE(ENUM, message, expr) \ do { \ auto _res = (expr); \ ::arrow::Status _st = ::arrow::internal::GenericToStatus(_res); \ if (!_st.Is##ENUM()) { \ FAIL() << "Expected '" ARROW_STRINGIFY(expr) "' to fail with " ARROW_STRINGIFY( \ ENUM) ", but got " \ << _st.ToString(); \ } \ ASSERT_EQ((message), _st.ToString()); \ } while (false) #define EXPECT_RAISES_WITH_MESSAGE_THAT(ENUM, matcher, expr) \ do { \ auto _res = (expr); \ ::arrow::Status _st = ::arrow::internal::GenericToStatus(_res); \ EXPECT_TRUE(_st.Is##ENUM()) << "Expected '" ARROW_STRINGIFY(expr) "' to fail with " \ << ARROW_STRINGIFY(ENUM) ", but got " << _st.ToString(); \ EXPECT_THAT(_st.ToString(), (matcher)); \ } while (false) #define EXPECT_RAISES_WITH_CODE_AND_MESSAGE_THAT(code, matcher, expr) \ do { \ auto _res = (expr); \ ::arrow::Status _st = ::arrow::internal::GenericToStatus(_res); \ EXPECT_EQ(_st.CodeAsString(), Status::CodeAsString(code)); \ EXPECT_THAT(_st.ToString(), (matcher)); \ } while (false) #define ASSERT_OK(expr) \ for (::arrow::Status _st = ::arrow::internal::GenericToStatus((expr)); !_st.ok();) \ FAIL() << "'" ARROW_STRINGIFY(expr) "' failed with " << _st.ToString() #define ASSERT_OK_NO_THROW(expr) ASSERT_NO_THROW(ASSERT_OK(expr)) #define ARROW_EXPECT_OK(expr) \ do { \ auto _res = (expr); \ ::arrow::Status _st = ::arrow::internal::GenericToStatus(_res); \ EXPECT_TRUE(_st.ok()) << "'" ARROW_STRINGIFY(expr) "' failed with " \ << _st.ToString(); \ } while (false) #define ASSERT_NOT_OK(expr) \ for (::arrow::Status _st = ::arrow::internal::GenericToStatus((expr)); _st.ok();) \ FAIL() << "'" ARROW_STRINGIFY(expr) "' did not failed" << _st.ToString() #define ABORT_NOT_OK(expr) \ do { \ auto _res = (expr); \ ::arrow::Status _st = ::arrow::internal::GenericToStatus(_res); \ if (ARROW_PREDICT_FALSE(!_st.ok())) { \ _st.Abort(); \ } \ } while (false); #define ASSIGN_OR_HANDLE_ERROR_IMPL(handle_error, status_name, lhs, rexpr) \ auto&& status_name = (rexpr); \ handle_error(status_name.status()); \ lhs = std::move(status_name).ValueOrDie(); #define ASSERT_OK_AND_ASSIGN(lhs, rexpr) \ ASSIGN_OR_HANDLE_ERROR_IMPL( \ ASSERT_OK, ARROW_ASSIGN_OR_RAISE_NAME(_error_or_value, __COUNTER__), lhs, rexpr); #define ASSIGN_OR_ABORT(lhs, rexpr) \ ASSIGN_OR_HANDLE_ERROR_IMPL(ABORT_NOT_OK, \ ARROW_ASSIGN_OR_RAISE_NAME(_error_or_value, __COUNTER__), \ lhs, rexpr); #define EXPECT_OK_AND_ASSIGN(lhs, rexpr) \ ASSIGN_OR_HANDLE_ERROR_IMPL(ARROW_EXPECT_OK, \ ARROW_ASSIGN_OR_RAISE_NAME(_error_or_value, __COUNTER__), \ lhs, rexpr); #define ASSERT_OK_AND_EQ(expected, expr) \ do { \ ASSERT_OK_AND_ASSIGN(auto _actual, (expr)); \ ASSERT_EQ(expected, _actual); \ } while (0) // A generalized version of GTest's SCOPED_TRACE that takes arbitrary arguments. // ARROW_SCOPED_TRACE("some variable = ", some_variable, ...) #define ARROW_SCOPED_TRACE(...) SCOPED_TRACE(::arrow::util::StringBuilder(__VA_ARGS__)) namespace arrow { // ---------------------------------------------------------------------- // Useful testing::Types declarations inline void PrintTo(StatusCode code, std::ostream* os) { *os << Status::CodeAsString(code); } using NumericArrowTypes = ::testing::Types; using RealArrowTypes = ::testing::Types; using IntegralArrowTypes = ::testing::Types; using PhysicalIntegralArrowTypes = ::testing::Types; using PrimitiveArrowTypes = ::testing::Types; using TemporalArrowTypes = ::testing::Types; using DecimalArrowTypes = ::testing::Types; using BaseBinaryArrowTypes = ::testing::Types; using BinaryArrowTypes = ::testing::Types; using StringArrowTypes = ::testing::Types; using ListArrowTypes = ::testing::Types; using UnionArrowTypes = ::testing::Types; class Array; class ChunkedArray; class RecordBatch; class Table; struct Datum; ARROW_TESTING_EXPORT std::vector AllTypeIds(); #define ASSERT_ARRAYS_EQUAL(lhs, rhs) AssertArraysEqual((lhs), (rhs)) #define ASSERT_BATCHES_EQUAL(lhs, rhs) AssertBatchesEqual((lhs), (rhs)) #define ASSERT_BATCHES_APPROX_EQUAL(lhs, rhs) AssertBatchesApproxEqual((lhs), (rhs)) #define ASSERT_TABLES_EQUAL(lhs, rhs) AssertTablesEqual((lhs), (rhs)) // Default EqualOptions for testing static inline EqualOptions TestingEqualOptions() { return EqualOptions{}.nans_equal(true).signed_zeros_equal(false); } // If verbose is true, then the arrays will be pretty printed ARROW_TESTING_EXPORT void AssertArraysEqual( const Array& expected, const Array& actual, bool verbose = false, const EqualOptions& options = TestingEqualOptions()); ARROW_TESTING_EXPORT void AssertArraysApproxEqual( const Array& expected, const Array& actual, bool verbose = false, const EqualOptions& options = TestingEqualOptions()); // Returns true when values are both null ARROW_TESTING_EXPORT void AssertScalarsEqual( const Scalar& expected, const Scalar& actual, bool verbose = false, const EqualOptions& options = TestingEqualOptions()); ARROW_TESTING_EXPORT void AssertScalarsApproxEqual( const Scalar& expected, const Scalar& actual, bool verbose = false, const EqualOptions& options = TestingEqualOptions()); ARROW_TESTING_EXPORT void AssertBatchesEqual(const RecordBatch& expected, const RecordBatch& actual, bool check_metadata = false); ARROW_TESTING_EXPORT void AssertBatchesApproxEqual(const RecordBatch& expected, const RecordBatch& actual); ARROW_TESTING_EXPORT void AssertChunkedEqual(const ChunkedArray& expected, const ChunkedArray& actual); ARROW_TESTING_EXPORT void AssertChunkedEqual(const ChunkedArray& actual, const ArrayVector& expected); // Like ChunkedEqual, but permits different chunk layout ARROW_TESTING_EXPORT void AssertChunkedEquivalent(const ChunkedArray& expected, const ChunkedArray& actual); ARROW_TESTING_EXPORT void AssertChunkedApproxEquivalent( const ChunkedArray& expected, const ChunkedArray& actual, const EqualOptions& options = TestingEqualOptions()); ARROW_TESTING_EXPORT void AssertBufferEqual(const Buffer& buffer, const std::vector& expected); ARROW_TESTING_EXPORT void AssertBufferEqual(const Buffer& buffer, const std::string& expected); ARROW_TESTING_EXPORT void AssertBufferEqual(const Buffer& buffer, const Buffer& expected); ARROW_TESTING_EXPORT void AssertTypeEqual(const DataType& lhs, const DataType& rhs, bool check_metadata = false); ARROW_TESTING_EXPORT void AssertTypeEqual(const std::shared_ptr& lhs, const std::shared_ptr& rhs, bool check_metadata = false); ARROW_TESTING_EXPORT void AssertFieldEqual(const Field& lhs, const Field& rhs, bool check_metadata = false); ARROW_TESTING_EXPORT void AssertFieldEqual(const std::shared_ptr& lhs, const std::shared_ptr& rhs, bool check_metadata = false); ARROW_TESTING_EXPORT void AssertSchemaEqual(const Schema& lhs, const Schema& rhs, bool check_metadata = false); ARROW_TESTING_EXPORT void AssertSchemaEqual(const std::shared_ptr& lhs, const std::shared_ptr& rhs, bool check_metadata = false); ARROW_TESTING_EXPORT void AssertTypeNotEqual(const DataType& lhs, const DataType& rhs, bool check_metadata = false); ARROW_TESTING_EXPORT void AssertTypeNotEqual(const std::shared_ptr& lhs, const std::shared_ptr& rhs, bool check_metadata = false); ARROW_TESTING_EXPORT void AssertFieldNotEqual(const Field& lhs, const Field& rhs, bool check_metadata = false); ARROW_TESTING_EXPORT void AssertFieldNotEqual(const std::shared_ptr& lhs, const std::shared_ptr& rhs, bool check_metadata = false); ARROW_TESTING_EXPORT void AssertSchemaNotEqual(const Schema& lhs, const Schema& rhs, bool check_metadata = false); ARROW_TESTING_EXPORT void AssertSchemaNotEqual(const std::shared_ptr& lhs, const std::shared_ptr& rhs, bool check_metadata = false); ARROW_TESTING_EXPORT Result> PrintArrayDiff( const ChunkedArray& expected, const ChunkedArray& actual); ARROW_TESTING_EXPORT void AssertTablesEqual(const Table& expected, const Table& actual, bool same_chunk_layout = true, bool flatten = false); ARROW_TESTING_EXPORT void AssertDatumsEqual(const Datum& expected, const Datum& actual, bool verbose = false); ARROW_TESTING_EXPORT void AssertDatumsApproxEqual( const Datum& expected, const Datum& actual, bool verbose = false, const EqualOptions& options = TestingEqualOptions()); template void AssertNumericDataEqual(const C_TYPE* raw_data, const std::vector& expected_values) { for (auto expected : expected_values) { ASSERT_EQ(expected, *raw_data); ++raw_data; } } ARROW_TESTING_EXPORT void CompareBatch(const RecordBatch& left, const RecordBatch& right, bool compare_metadata = true); ARROW_TESTING_EXPORT void ApproxCompareBatch(const RecordBatch& left, const RecordBatch& right, bool compare_metadata = true); // Check if the padding of the buffers of the array is zero. // Also cause valgrind warnings if the padding bytes are uninitialized. ARROW_TESTING_EXPORT void AssertZeroPadded(const Array& array); // Check if the valid buffer bytes are initialized // and cause valgrind warnings otherwise. ARROW_TESTING_EXPORT void TestInitialized(const ArrayData& array); ARROW_TESTING_EXPORT void TestInitialized(const Array& array); #define DECL_T() typedef typename TestFixture::T T; #define DECL_TYPE() typedef typename TestFixture::Type Type; // ArrayFromJSON: construct an Array from a simple JSON representation ARROW_TESTING_EXPORT std::shared_ptr ArrayFromJSON(const std::shared_ptr&, util::string_view json); ARROW_TESTING_EXPORT std::shared_ptr DictArrayFromJSON(const std::shared_ptr& type, util::string_view indices_json, util::string_view dictionary_json); ARROW_TESTING_EXPORT std::shared_ptr RecordBatchFromJSON(const std::shared_ptr&, util::string_view); ARROW_TESTING_EXPORT std::shared_ptr ChunkedArrayFromJSON(const std::shared_ptr&, const std::vector& json); ARROW_TESTING_EXPORT std::shared_ptr ScalarFromJSON(const std::shared_ptr&, util::string_view json); ARROW_TESTING_EXPORT std::shared_ptr DictScalarFromJSON(const std::shared_ptr&, util::string_view index_json, util::string_view dictionary_json); ARROW_TESTING_EXPORT std::shared_ptr TableFromJSON(const std::shared_ptr&, const std::vector& json); // Given an array, return a new identical array except for one validity bit // set to a new value. // This is useful to force the underlying "value" of null entries to otherwise // invalid data and check that errors don't get reported. ARROW_TESTING_EXPORT std::shared_ptr TweakValidityBit(const std::shared_ptr& array, int64_t index, bool validity); ARROW_TESTING_EXPORT void SleepFor(double seconds); // Sleeps for a very small amount of time. The thread will be yielded // at least once ensuring that context switches could happen. It is intended // to be used for stress testing parallel code and shouldn't be assumed to do any // reliable timing. ARROW_TESTING_EXPORT void SleepABit(); // Wait until predicate is true or timeout in seconds expires. ARROW_TESTING_EXPORT void BusyWait(double seconds, std::function predicate); ARROW_TESTING_EXPORT Future<> SleepAsync(double seconds); // \see SleepABit ARROW_TESTING_EXPORT Future<> SleepABitAsync(); template std::vector IteratorToVector(Iterator iterator) { EXPECT_OK_AND_ASSIGN(auto out, iterator.ToVector()); return out; } ARROW_TESTING_EXPORT bool LocaleExists(const char* locale); // A RAII-style object that switches to a new locale, and switches back // to the old locale when going out of scope. Doesn't do anything if the // new locale doesn't exist on the local machine. // ATTENTION: may crash with an assertion failure on Windows debug builds. // See ARROW-6108, also https://gerrit.libreoffice.org/#/c/54110/ class ARROW_TESTING_EXPORT LocaleGuard { public: explicit LocaleGuard(const char* new_locale); ~LocaleGuard(); protected: class Impl; std::unique_ptr impl_; }; class ARROW_TESTING_EXPORT EnvVarGuard { public: EnvVarGuard(const std::string& name, const std::string& value); ~EnvVarGuard(); protected: const std::string name_; std::string old_value_; bool was_set_; }; namespace internal { class SignalHandler; } class ARROW_TESTING_EXPORT SignalHandlerGuard { public: typedef void (*Callback)(int); SignalHandlerGuard(int signum, Callback cb); SignalHandlerGuard(int signum, const internal::SignalHandler& handler); ~SignalHandlerGuard(); protected: struct Impl; std::unique_ptr impl_; }; #ifndef ARROW_LARGE_MEMORY_TESTS #define LARGE_MEMORY_TEST(name) DISABLED_##name #else #define LARGE_MEMORY_TEST(name) name #endif inline void PrintTo(const Status& st, std::ostream* os) { *os << st.ToString(); } template void PrintTo(const Result& result, std::ostream* os) { if (result.ok()) { ::testing::internal::UniversalPrint(result.ValueOrDie(), os); } else { *os << result.status(); } } // A data type with only move constructors (no copy, no default). struct MoveOnlyDataType { explicit MoveOnlyDataType(int x) : data(new int(x)) {} MoveOnlyDataType(const MoveOnlyDataType& other) = delete; MoveOnlyDataType& operator=(const MoveOnlyDataType& other) = delete; MoveOnlyDataType(MoveOnlyDataType&& other) { MoveFrom(&other); } MoveOnlyDataType& operator=(MoveOnlyDataType&& other) { MoveFrom(&other); return *this; } MoveOnlyDataType& operator=(int x) { if (data != nullptr) { delete data; } data = new int(x); return *this; } ~MoveOnlyDataType() { Destroy(); } void Destroy() { if (data != nullptr) { delete data; data = nullptr; moves = -1; } } void MoveFrom(MoveOnlyDataType* other) { Destroy(); data = other->data; other->data = nullptr; moves = other->moves + 1; } int ToInt() const { return data == nullptr ? -42 : *data; } bool operator==(const MoveOnlyDataType& other) const { return data != nullptr && other.data != nullptr && *data == *other.data; } bool operator<(const MoveOnlyDataType& other) const { return data == nullptr || (other.data != nullptr && *data < *other.data); } bool operator==(int other) const { return data != nullptr && *data == other; } friend bool operator==(int left, const MoveOnlyDataType& right) { return right == left; } int* data = nullptr; int moves = 0; }; // A task that blocks until unlocked. Useful for timing tests. class ARROW_TESTING_EXPORT GatingTask { public: explicit GatingTask(double timeout_seconds = 10); /// \brief During destruction we wait for all pending tasks to finish ~GatingTask(); /// \brief Creates a new waiting task (presumably to spawn on a thread). It will return /// invalid if the timeout arrived before the unlock. The task will not complete until /// unlocked or timed out /// /// Note: The GatingTask must outlive any Task instances std::function Task(); /// \brief Creates a new waiting task as a future. The future will not complete /// until unlocked. Future<> AsyncTask(); /// \brief Waits until at least count tasks are running. Status WaitForRunning(int count); /// \brief Unlocks all waiting tasks. Returns an invalid status if any waiting task has /// timed out Status Unlock(); static std::shared_ptr Make(double timeout_seconds = 10); private: class Impl; std::shared_ptr impl_; }; } // namespace arrow namespace nonstd { namespace sv_lite { // Without this hint, GTest will print string_views as a container of char template > void PrintTo(const basic_string_view& view, std::ostream* os) { *os << view; } } // namespace sv_lite namespace optional_lite { template void PrintTo(const optional& opt, std::ostream* os) { if (opt.has_value()) { *os << "{"; ::testing::internal::UniversalPrint(*opt, os); *os << "}"; } else { *os << "nullopt"; } } inline void PrintTo(const decltype(nullopt)&, std::ostream* os) { *os << "nullopt"; } } // namespace optional_lite } // namespace nonstd