// Copyright 2021 The Pigweed Authors // // Licensed 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 // // https://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 "pw_stream/stream.h" #include #include "gtest/gtest.h" #include "pw_stream/null_stream.h" namespace pw::stream { namespace { static_assert(sizeof(Stream) <= 2 * sizeof(void*), "Stream should be no larger than two pointers (vtable pointer & " "packed members)"); class TestNonSeekableReader : public NonSeekableReader { private: StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); } }; class TestRelativeSeekableReader : public RelativeSeekableReader { private: StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); } Status DoSeek(ptrdiff_t, Whence) override { return Status(); } }; class TestSeekableReader : public SeekableReader { private: StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); } Status DoSeek(ptrdiff_t, Whence) override { return Status(); } }; class TestNonSeekableWriter : public NonSeekableWriter { private: Status DoWrite(ConstByteSpan) override { return OkStatus(); } }; class TestRelativeSeekableWriter : public RelativeSeekableWriter { private: Status DoWrite(ConstByteSpan) override { return OkStatus(); } Status DoSeek(ptrdiff_t, Whence) override { return OkStatus(); } }; class TestSeekableWriter : public SeekableWriter { private: Status DoWrite(ConstByteSpan) override { return OkStatus(); } Status DoSeek(ptrdiff_t, Whence) override { return OkStatus(); } }; class TestNonSeekableReaderWriter : public NonSeekableReaderWriter { private: StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); } Status DoWrite(ConstByteSpan) override { return OkStatus(); } }; class TestRelativeSeekableReaderWriter : public RelativeSeekableReaderWriter { private: StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); } Status DoWrite(ConstByteSpan) override { return OkStatus(); } Status DoSeek(ptrdiff_t, Whence) override { return OkStatus(); } }; class TestSeekableReaderWriter : public SeekableReaderWriter { private: StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); } Status DoWrite(ConstByteSpan) override { return OkStatus(); } Status DoSeek(ptrdiff_t, Whence) override { return OkStatus(); } }; // Test ReaderWriter conversions to Reader/Writer. // clang-format off static_assert(std::is_convertible()); static_assert(std::is_convertible()); static_assert(!std::is_convertible()); static_assert(!std::is_convertible()); static_assert(!std::is_convertible()); static_assert(!std::is_convertible()); static_assert(std::is_convertible()); static_assert(std::is_convertible()); static_assert(std::is_convertible()); static_assert(std::is_convertible()); static_assert(!std::is_convertible()); static_assert(!std::is_convertible()); static_assert(std::is_convertible()); static_assert(std::is_convertible()); static_assert(std::is_convertible()); static_assert(std::is_convertible()); static_assert(std::is_convertible()); static_assert(std::is_convertible()); // clang-format on constexpr uint8_t kSeekable = Stream::kBeginning | Stream::kCurrent | Stream::kEnd; constexpr uint8_t kRelativeSeekable = Stream::kCurrent; constexpr uint8_t kNonSeekable = 0; enum Readable : bool { kNonReadable = false, kReadable = true }; enum Writable : bool { kNonWritable = false, kWritable = true }; template void TestStreamImpl() { T derived_stream; Stream& stream = derived_stream; // Check stream properties ASSERT_EQ(writable, stream.writable()); ASSERT_EQ(readable, stream.readable()); ASSERT_EQ((seekable & Stream::kBeginning) != 0, stream.seekable(Stream::kBeginning)); ASSERT_EQ((seekable & Stream::kCurrent) != 0, stream.seekable(Stream::kCurrent)); ASSERT_EQ((seekable & Stream::kEnd) != 0, stream.seekable(Stream::kEnd)); ASSERT_EQ(seekable != kNonSeekable, stream.seekable()); // Check Read()/Write()/Seek() ASSERT_EQ(readable ? OkStatus() : Status::Unimplemented(), stream.Read({}).status()); ASSERT_EQ(writable ? OkStatus() : Status::Unimplemented(), stream.Write({})); ASSERT_EQ(seekable ? OkStatus() : Status::Unimplemented(), stream.Seek(0)); // Check ConservativeLimits() ASSERT_EQ(readable ? Stream::kUnlimited : 0, stream.ConservativeReadLimit()); ASSERT_EQ(writable ? Stream::kUnlimited : 0, stream.ConservativeWriteLimit()); } TEST(Stream, NonSeekableReader) { TestStreamImpl(); } TEST(Stream, RelativeSeekableReader) { TestStreamImpl(); } TEST(Stream, SeekableReader) { TestStreamImpl(); } TEST(Stream, NonSeekableWriter) { TestStreamImpl(); } TEST(Stream, RelativeSeekableWriter) { TestStreamImpl(); } TEST(Stream, SeekableWriter) { TestStreamImpl(); } TEST(Stream, NonSeekableReaderWriter) { TestStreamImpl(); } TEST(Stream, RelativeSeekableReaderWriter) { TestStreamImpl(); } TEST(Stream, SeekableReaderWriter) { TestStreamImpl(); } TEST(NullStream, DefaultConservativeWriteLimit) { NullStream stream; EXPECT_EQ(stream.ConservativeWriteLimit(), Stream::kUnlimited); } TEST(NullStream, DefaultConservativeReadLimit) { NullStream stream; EXPECT_EQ(stream.ConservativeReadLimit(), Stream::kUnlimited); } TEST(NullStream, DefaultConservativeReadWriteLimit) { NullStream stream; EXPECT_EQ(stream.ConservativeWriteLimit(), Stream::kUnlimited); EXPECT_EQ(stream.ConservativeReadLimit(), Stream::kUnlimited); } TEST(NullStream, DefaultTell) { NullStream stream; EXPECT_EQ(stream.Tell(), Stream::kUnknownPosition); } } // namespace } // namespace pw::stream