blob: 3734650b116894d16c83752bcae27c2d4fca9654 [file]
/*
* 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.
*/
#include "paimon/common/io/offset_input_stream.h"
#include <memory>
#include <utility>
#include "gtest/gtest.h"
#include "paimon/io/byte_array_input_stream.h"
#include "paimon/testing/utils/testharness.h"
namespace paimon::test {
TEST(OffsetInputStreamTest, TestBasicConstruction) {
auto inner_stream = std::make_unique<ByteArrayInputStream>("abcdefghij", /*length=*/10);
ASSERT_OK_AND_ASSIGN(auto offset_stream, OffsetInputStream::Create(std::move(inner_stream),
/*length=*/6, /*offset=*/2));
ASSERT_OK_AND_ASSIGN(auto length, offset_stream->Length());
ASSERT_EQ(6, length);
ASSERT_OK_AND_ASSIGN(auto pos, offset_stream->GetPos());
ASSERT_EQ(0, pos);
ASSERT_OK_AND_ASSIGN(auto uri, offset_stream->GetUri());
ASSERT_EQ("", uri);
}
TEST(OffsetInputStreamTest, TestSeekOperations) {
auto inner_stream = std::make_unique<ByteArrayInputStream>("abcdefghij", /*length=*/10);
ASSERT_OK_AND_ASSIGN(auto offset_stream, OffsetInputStream::Create(std::move(inner_stream),
/*length=*/6, /*offset=*/2));
// Test FS_SEEK_SET
ASSERT_OK(offset_stream->Seek(3, SeekOrigin::FS_SEEK_SET));
ASSERT_OK_AND_ASSIGN(auto pos, offset_stream->GetPos());
ASSERT_EQ(3, pos);
// Test FS_SEEK_CUR
ASSERT_OK(offset_stream->Seek(1, SeekOrigin::FS_SEEK_CUR));
ASSERT_OK_AND_ASSIGN(pos, offset_stream->GetPos());
ASSERT_EQ(4, pos);
// Test FS_SEEK_END
ASSERT_OK(offset_stream->Seek(-2, SeekOrigin::FS_SEEK_END));
ASSERT_OK_AND_ASSIGN(pos, offset_stream->GetPos());
ASSERT_EQ(4, pos);
// Test boundary conditions
ASSERT_NOK(offset_stream->Seek(-10, SeekOrigin::FS_SEEK_SET));
ASSERT_NOK(offset_stream->Seek(10, SeekOrigin::FS_SEEK_SET));
ASSERT_NOK(offset_stream->Seek(10, SeekOrigin::FS_SEEK_CUR));
ASSERT_NOK(offset_stream->Seek(10, SeekOrigin::FS_SEEK_END));
}
TEST(OffsetInputStreamTest, TestReadOperations) {
auto inner_stream = std::make_unique<ByteArrayInputStream>("abcdefghij", /*length=*/10);
ASSERT_OK_AND_ASSIGN(auto offset_stream, OffsetInputStream::Create(std::move(inner_stream),
/*length=*/6, /*offset=*/2));
// Test sequential read
std::string buffer(4, '\0');
ASSERT_OK_AND_ASSIGN(auto bytes_read, offset_stream->Read(buffer.data(), /*size=*/4));
ASSERT_EQ(4, bytes_read);
ASSERT_EQ("cdef", buffer);
ASSERT_OK_AND_ASSIGN(auto pos, offset_stream->GetPos());
ASSERT_EQ(4, pos);
// Test read with offset
std::string buffer2(2, '\0');
ASSERT_OK_AND_ASSIGN(bytes_read, offset_stream->Read(buffer2.data(), /*size=*/2, /*offset=*/1));
ASSERT_EQ(2, bytes_read);
ASSERT_EQ("de", buffer2);
// Position should not change after offset read
ASSERT_OK_AND_ASSIGN(pos, offset_stream->GetPos());
ASSERT_EQ(4, pos);
// Test close
ASSERT_OK(offset_stream->Close());
}
TEST(OffsetInputStreamTest, TestReadAsync) {
auto inner_stream = std::make_unique<ByteArrayInputStream>("abcdefghij", /*length=*/10);
ASSERT_OK_AND_ASSIGN(auto offset_stream, OffsetInputStream::Create(std::move(inner_stream),
/*length=*/6, /*offset=*/2));
std::string buffer1(3, '\0'), buffer2(2, '\0');
bool callback_called1 = false, callback_called2 = false;
Status callback_status1, callback_status2;
auto callback1 = [&](Status status) {
callback_called1 = true;
callback_status1 = status;
};
auto callback2 = [&](Status status) {
callback_called2 = true;
callback_status2 = status;
};
offset_stream->ReadAsync(buffer1.data(), 3, 1, std::move(callback1));
offset_stream->ReadAsync(buffer2.data(), 2, 3, std::move(callback2));
ASSERT_TRUE(callback_called1);
ASSERT_OK(callback_status1);
ASSERT_EQ("def", buffer1);
ASSERT_TRUE(callback_called2);
ASSERT_OK(callback_status2);
ASSERT_EQ("fg", buffer2);
// Position should not change after offset read
ASSERT_OK_AND_ASSIGN(auto pos, offset_stream->GetPos());
ASSERT_EQ(0, pos);
}
TEST(OffsetInputStreamTest, TestBoundaryValidation) {
auto inner_stream = std::make_unique<ByteArrayInputStream>("abcdefghij", /*length=*/10);
ASSERT_OK_AND_ASSIGN(auto offset_stream, OffsetInputStream::Create(std::move(inner_stream),
/*length=*/6, /*offset=*/2));
// Test read beyond boundary
std::string buffer(10, '\0');
ASSERT_NOK_WITH_MSG(offset_stream->Read(buffer.data(), /*size=*/10),
"assert boundary failed: inner pos 10 exceed length 6");
// Test offset read beyond boundary
ASSERT_NOK_WITH_MSG(offset_stream->Read(buffer.data(), /*size=*/4, /*offset=*/5),
"assert boundary failed: inner pos 9 exceed length 6");
}
TEST(OffsetInputStreamTest, TestReadWithUnspecifiedLength) {
auto inner_stream = std::make_unique<ByteArrayInputStream>("abcdefghij", /*length=*/10);
// Use -1 for length to test dynamic length calculation
ASSERT_OK_AND_ASSIGN(
auto offset_stream,
OffsetInputStream::Create(std::move(inner_stream), /*length=*/-1, /*offset=*/2));
// Test that length is calculated correctly
ASSERT_OK_AND_ASSIGN(auto length, offset_stream->Length());
// Should be total length (10) minus offset (2) = 8
ASSERT_EQ(8, length);
// Test sequential read within the calculated bounds
std::string buffer(4, '\0');
ASSERT_OK_AND_ASSIGN(auto bytes_read, offset_stream->Read(buffer.data(), /*size=*/4));
ASSERT_EQ(4, bytes_read);
ASSERT_EQ("cdef", buffer);
ASSERT_OK_AND_ASSIGN(auto pos, offset_stream->GetPos());
ASSERT_EQ(4, pos);
// Test read with offset within the calculated bounds
std::string buffer2(3, '\0');
ASSERT_OK_AND_ASSIGN(bytes_read, offset_stream->Read(buffer2.data(), /*size=*/3, /*offset=*/5));
ASSERT_EQ(3, bytes_read);
ASSERT_EQ("hij", buffer2);
// Position should not change after offset read
ASSERT_OK_AND_ASSIGN(pos, offset_stream->GetPos());
ASSERT_EQ(4, pos);
// Test boundary validation with dynamic length
std::string buffer3(10, '\0');
ASSERT_NOK_WITH_MSG(offset_stream->Read(buffer3.data(), /*size=*/10),
"assert boundary failed: inner pos 14 exceed length 8");
}
TEST(OffsetInputStreamTest, TestInvalidParameters) {
// Test null wrapped stream
ASSERT_NOK_WITH_MSG(OffsetInputStream::Create(nullptr, /*length=*/6, /*offset=*/2),
"input stream is null pointer");
// Test negative offset
auto inner_stream = std::make_unique<ByteArrayInputStream>("abcdefghij", /*length=*/10);
ASSERT_NOK_WITH_MSG(
OffsetInputStream::Create(std::move(inner_stream), /*length=*/6, /*offset=*/-1),
"offset -1 is less than 0");
// Test length less than -1
inner_stream = std::make_unique<ByteArrayInputStream>("abcdefghij", /*length=*/10);
ASSERT_NOK_WITH_MSG(
OffsetInputStream::Create(std::move(inner_stream), /*length=*/-2, /*offset=*/2),
"length -2 is less than -1");
// Test length + offset beyond wrapped stream length
inner_stream = std::make_unique<ByteArrayInputStream>("abcdefghij", /*length=*/10);
ASSERT_NOK_WITH_MSG(
OffsetInputStream::Create(std::move(inner_stream), /*length=*/8, /*offset=*/7),
"offset 7 + length 8 exceed total length 10");
// Test dynamic length with offset beyond wrapped stream length
inner_stream = std::make_unique<ByteArrayInputStream>("abcdefghij", /*length=*/10);
ASSERT_NOK_WITH_MSG(
OffsetInputStream::Create(std::move(inner_stream), /*length=*/-1, /*offset=*/15),
"offset 15 exceed total length 10");
}
} // namespace paimon::test