lxy-9602 commented on code in PR #336:
URL: https://github.com/apache/paimon-cpp/pull/336#discussion_r4011630718


##########
src/paimon/common/utils/arrow/arrow_utils_test.cpp:
##########
@@ -1215,4 +1217,124 @@ TEST(ArrowUtilsTest, 
TestFlattenUnresolvableDictionaries) {
     }
 }
 
+namespace {
+
+/// What `ArrowUtils::UnpackBooleansToBytes` has to agree with: every row 
taken one at a time
+/// through the very accessors the helper replaces.
+std::vector<char> UnpackRowByRow(const arrow::BooleanArray& array, bool 
negate) {
+    std::vector<char> is_valid(static_cast<size_t>(array.length()), 0);
+    for (int64_t i = 0; i < array.length(); i++) {
+        if (array.IsNull(i)) {
+            continue;
+        }
+        is_valid[i] = static_cast<char>(array.Value(i) != negate);
+    }
+    return is_valid;
+}
+
+/// A boolean array of `values.size()` rows holding the value `values[i]` at 
row `i`, with a
+/// validity bitmap that marks row `i` as holding one when `valid[i]` is true.
+std::shared_ptr<arrow::BooleanArray> BooleansOf(const std::vector<bool>& 
values,
+                                                const std::vector<bool>& 
valid) {
+    const auto length = static_cast<int64_t>(values.size());
+    std::shared_ptr<arrow::Buffer> value_bits = 
arrow::AllocateBitmap(length).ValueOrDie();
+    std::shared_ptr<arrow::Buffer> valid_bits = 
arrow::AllocateBitmap(length).ValueOrDie();
+    int64_t null_count = 0;
+    for (int64_t i = 0; i < length; i++) {
+        arrow::bit_util::SetBitTo(value_bits->mutable_data(), i, 
values[static_cast<size_t>(i)]);
+        arrow::bit_util::SetBitTo(valid_bits->mutable_data(), i, 
valid[static_cast<size_t>(i)]);
+        null_count += valid[static_cast<size_t>(i)] ? 0 : 1;
+    }
+    return std::make_shared<arrow::BooleanArray>(length, value_bits, 
valid_bits, null_count);
+}
+
+}  // namespace
+
+TEST(ArrowUtilsTest, TestUnpackBooleansToBytes) {
+    std::shared_ptr<arrow::Array> made =
+        arrow::ipc::internal::json::ArrayFromJSON(arrow::boolean(), R"([true, 
false, null, true])")
+            .ValueOrDie();
+    const auto& array = checked_cast<const arrow::BooleanArray&>(*made);
+
+    // A null row is left at 0 whatever the value bitmap holds for it.
+    ASSERT_EQ(ArrowUtils::UnpackBooleansToBytes(array, /*negate=*/false),
+              std::vector<char>({1, 0, 0, 1}));
+    ASSERT_EQ(ArrowUtils::UnpackBooleansToBytes(array, /*negate=*/true),
+              std::vector<char>({0, 1, 0, 0}));
+}
+
+TEST(ArrowUtilsTest, 
TestUnpackBooleansToBytesAgreesWithTheAccessorsOnEverySlice) {
+    // Neither the row count nor the periods the values and the nulls repeat 
on are a multiple of
+    // eight, so the slices below land on a partial leading byte, on whole 
bytes and on a partial
+    // trailing one, whichever offset and length they take.
+    constexpr int64_t kRows = 41;
+    std::vector<bool> values;
+    std::vector<bool> valid;
+    for (int64_t i = 0; i < kRows; i++) {
+        values.push_back((i * 7 + 3) % 5 != 0);
+        valid.push_back(i % 4 != 0);
+    }
+    const std::shared_ptr<arrow::BooleanArray> source = BooleansOf(values, 
valid);
+
+    for (int64_t offset = 0; offset <= kRows; offset++) {
+        for (int64_t length = 0; length <= kRows - offset; length++) {
+            // `Slice` returns a new owner, so it has to be held for as long 
as the slice is read.
+            std::shared_ptr<arrow::Array> sliced = source->Slice(offset, 
length);
+            const auto& slice = checked_cast<const 
arrow::BooleanArray&>(*sliced);
+            for (bool negate : {false, true}) {
+                ASSERT_EQ(ArrowUtils::UnpackBooleansToBytes(slice, negate),
+                          UnpackRowByRow(slice, negate))
+                    << "offset=" << offset << " length=" << length << " 
negate=" << negate;
+            }
+        }
+    }
+}
+
+TEST(ArrowUtilsTest, TestUnpackBooleansToBytesWithoutAValidityBitmap) {
+    // A bitmap written for a column with no null in it carries no validity 
buffer at all, which
+    // `Array::IsValid` reads as every row holding a value.
+    const int64_t length = 10;
+    std::shared_ptr<arrow::Buffer> value_bits = 
arrow::AllocateBitmap(length).ValueOrDie();
+    for (int64_t i = 0; i < length; i++) {
+        arrow::bit_util::SetBitTo(value_bits->mutable_data(), i, i % 3 != 0);
+    }
+    const arrow::BooleanArray array(length, value_bits, 
/*null_bitmap=*/nullptr,
+                                    /*null_count=*/0);
+
+    std::vector<char> expected(static_cast<size_t>(length));
+    for (int64_t i = 0; i < length; i++) {
+        expected[static_cast<size_t>(i)] = static_cast<char>(i % 3 != 0);
+    }
+    ASSERT_EQ(ArrowUtils::UnpackBooleansToBytes(array, /*negate=*/false), 
expected);
+    ASSERT_EQ(UnpackRowByRow(array, /*negate=*/false), expected);
+
+    // `negate` still applies once every row holds a value.
+    ASSERT_EQ(ArrowUtils::UnpackBooleansToBytes(array, /*negate=*/true),
+              UnpackRowByRow(array, /*negate=*/true));
+}
+
+TEST(ArrowUtilsTest, TestUnpackBooleansToBytesOfAnArrayThatIsNullThroughout) {
+    // Without a validity bitmap `Array::IsValid` falls back to `null_count != 
length`, so an array
+    // whose null count is its length holds no value at any row, however its 
value bitmap reads.
+    const int64_t length = 9;
+    std::shared_ptr<arrow::Buffer> value_bits = 
arrow::AllocateBitmap(length).ValueOrDie();
+    arrow::bit_util::SetBitsTo(value_bits->mutable_data(), /*offset=*/0, 
length, /*bits=*/true);
+    const arrow::BooleanArray array(length, value_bits, 
/*null_bitmap=*/nullptr,
+                                    /*null_count=*/length);
+
+    ASSERT_EQ(ArrowUtils::UnpackBooleansToBytes(array, /*negate=*/false),
+              std::vector<char>(static_cast<size_t>(length), 0));
+    ASSERT_EQ(ArrowUtils::UnpackBooleansToBytes(array, /*negate=*/true),
+              std::vector<char>(static_cast<size_t>(length), 0));
+    ASSERT_EQ(UnpackRowByRow(array, /*negate=*/true),
+              std::vector<char>(static_cast<size_t>(length), 0));

Review Comment:
   Isn’t this an invalid array? Could you confirm that arrow `Validate` should 
already fail for this case (see `arrow/array/validate.cc`)? If so, testing an 
invalid array here does not seem very meaningful.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to