| // Copyright 2018 The Fuchsia Authors | |
| // | |
| // Use of this source code is governed by a MIT-style | |
| // license that can be found in the LICENSE file or at | |
| // https://opensource.org/licenses/MIT | |
| #include <lib/unittest/unittest.h> | |
| #include <lib/unittest/user_memory.h> | |
| #include <lib/user_copy/user_ptr.h> | |
| #include <stdio.h> | |
| #include "object/buffer_chain.h" | |
| namespace { | |
| using testing::UserMemory; | |
| static bool alloc_free_basic() { | |
| BEGIN_TEST; | |
| // An empty chain requires one buffer | |
| BufferChain* bc = BufferChain::Alloc(0); | |
| ASSERT_NE(bc, nullptr); | |
| ASSERT_FALSE(bc->buffers()->is_empty()); | |
| ASSERT_EQ(bc->buffers()->size_slow(), 1u); | |
| BufferChain::Free(bc); | |
| // One Buffer is enough to hold one byte. | |
| bc = BufferChain::Alloc(1); | |
| ASSERT_FALSE(bc->buffers()->is_empty()); | |
| ASSERT_EQ(bc->buffers()->size_slow(), 1u); | |
| ASSERT_NE(bc, nullptr); | |
| BufferChain::Free(bc); | |
| // One Buffer is still enough. | |
| bc = BufferChain::Alloc(BufferChain::kContig); | |
| ASSERT_FALSE(bc->buffers()->is_empty()); | |
| ASSERT_EQ(bc->buffers()->size_slow(), 1u); | |
| ASSERT_NE(bc, nullptr); | |
| BufferChain::Free(bc); | |
| // Two Buffers required. | |
| bc = BufferChain::Alloc(BufferChain::kContig + 1); | |
| ASSERT_FALSE(bc->buffers()->is_empty()); | |
| ASSERT_EQ(bc->buffers()->size_slow(), 2u); | |
| ASSERT_NE(bc, nullptr); | |
| BufferChain::Free(bc); | |
| // Many Buffers required. | |
| bc = BufferChain::Alloc(10000 * BufferChain::kRawDataSize); | |
| ASSERT_FALSE(bc->buffers()->is_empty()); | |
| ASSERT_EQ(bc->buffers()->size_slow(), 1u + 10000u); | |
| ASSERT_NE(bc, nullptr); | |
| BufferChain::Free(bc); | |
| END_TEST; | |
| } | |
| static bool copy_in_copy_out() { | |
| BEGIN_TEST; | |
| constexpr size_t kSize = BufferChain::kContig + 2 * BufferChain::kRawDataSize; | |
| fbl::AllocChecker ac; | |
| auto buf = ktl::unique_ptr<char[]>(new (&ac) char[kSize]); | |
| ASSERT_TRUE(ac.check()); | |
| ktl::unique_ptr<UserMemory> mem = UserMemory::Create(kSize); | |
| auto mem_in = mem->user_in<char>(); | |
| auto mem_out = mem->user_out<char>(); | |
| BufferChain* bc = BufferChain::Alloc(kSize); | |
| ASSERT_NE(nullptr, bc); | |
| ASSERT_FALSE(bc->buffers()->is_empty()); | |
| // Fill the chain with 'A'. | |
| memset(buf.get(), 'A', kSize); | |
| ASSERT_EQ(ZX_OK, mem_out.copy_array_to_user(buf.get(), kSize)); | |
| ASSERT_EQ(ZX_OK, bc->CopyIn(mem_in, 0, kSize)); | |
| // Verify it. | |
| ASSERT_EQ(3u, bc->buffers()->size_slow()); | |
| for (auto& b : *bc->buffers()) { | |
| char* data = b.data(); | |
| for (size_t i = 0; i < b.size(); ++i) { | |
| ASSERT_EQ('A', data[i]); | |
| } | |
| } | |
| // Write a chunk of 'B' straddling all three buffers. | |
| memset(buf.get(), 'B', kSize); | |
| ASSERT_EQ(ZX_OK, mem_out.copy_array_to_user(buf.get(), kSize)); | |
| size_t offset = BufferChain::kContig - 1; | |
| size_t size = BufferChain::kRawDataSize + 2; | |
| ASSERT_EQ(ZX_OK, bc->CopyIn(mem_in, offset, size)); | |
| // Verify it. | |
| auto iter = bc->buffers()->begin(); | |
| for (size_t i = 0; i < offset; ++i) { | |
| char* data = iter->data(); | |
| ASSERT_EQ('A', data[i]); | |
| } | |
| ASSERT_EQ('B', *(iter->data() + offset)); | |
| ++iter; | |
| for (size_t i = 0; i < BufferChain::kRawDataSize; ++i) { | |
| char* data = iter->data(); | |
| ASSERT_EQ('B', data[i]); | |
| } | |
| ++iter; | |
| ASSERT_EQ('B', *iter->data()); | |
| for (size_t i = 1; i < BufferChain::kRawDataSize; ++i) { | |
| char* data = iter->data(); | |
| EXPECT_EQ('A', data[i]); | |
| } | |
| ASSERT_TRUE(++iter == bc->buffers()->end()); | |
| // Copy it all out. | |
| memset(buf.get(), 0, kSize); | |
| ASSERT_EQ(ZX_OK, mem_out.copy_array_to_user(buf.get(), kSize)); | |
| ASSERT_EQ(ZX_OK, bc->CopyOut(mem_out, 0, kSize)); | |
| // Verify it. | |
| memset(buf.get(), 0, kSize); | |
| ASSERT_EQ(ZX_OK, mem_in.copy_array_from_user(buf.get(), kSize)); | |
| size_t index = 0; | |
| for (size_t i = 0; i < offset; ++i) { | |
| ASSERT_EQ('A', buf[index++]); | |
| } | |
| EXPECT_EQ('B', buf[index++]); | |
| for (size_t i = 0; i < BufferChain::kRawDataSize; ++i) { | |
| ASSERT_EQ('B', buf[index++]); | |
| } | |
| ASSERT_EQ('B', buf[index++]); | |
| for (size_t i = 1; i < BufferChain::kRawDataSize; ++i) { | |
| EXPECT_EQ('A', buf[index++]); | |
| } | |
| BufferChain::Free(bc); | |
| END_TEST; | |
| } | |
| } // namespace | |
| UNITTEST_START_TESTCASE(buffer_chain_tests) | |
| UNITTEST("alloc_free_basic", alloc_free_basic) | |
| UNITTEST("copy_in_copy_out", copy_in_copy_out) | |
| UNITTEST_END_TESTCASE(buffer_chain_tests, "buffer_chain", "BufferChain tests") |