| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "buffer.h" |
| |
| #include <lib/fzl/vmo-mapper.h> |
| |
| #include <gtest/gtest.h> |
| |
| namespace network { |
| namespace tun { |
| namespace testing { |
| |
| constexpr uint64_t kVmoSize = ZX_PAGE_SIZE; |
| constexpr uint8_t kVmoId = 0x06; |
| |
| class BufferTest : public ::testing::Test { |
| void SetUp() override { |
| zx::vmo vmo; |
| ASSERT_EQ(zx::vmo::create(kVmoSize, 0, &vmo), ZX_OK); |
| ASSERT_EQ(vmos_.RegisterVmo(kVmoId, std::move(vmo)), ZX_OK); |
| } |
| |
| public: |
| void MintVmo(size_t offset, size_t len) { |
| uint8_t val = 0; |
| while (len--) { |
| ASSERT_EQ(vmos_.Write(kVmoId, offset, 1, &val), ZX_OK); |
| offset++; |
| val++; |
| } |
| } |
| |
| void MintVmo(const buffer_region_t& region) { MintVmo(region.offset, region.length); } |
| |
| std::vector<uint8_t> ReadVmo(const buffer_region_t& region) { |
| std::vector<uint8_t> ret; |
| ret.reserve(region.length); |
| EXPECT_EQ(vmos_.Read(kVmoId, region.offset, region.length, std::back_inserter(ret)), ZX_OK); |
| return ret; |
| } |
| |
| protected: |
| VmoStore vmos_; |
| }; |
| |
| TEST_F(BufferTest, TestBufferBuildTx) { |
| buffer_region_t regions[2]; |
| regions[0].offset = 10; |
| regions[0].length = 5; |
| regions[1].offset = 100; |
| regions[1].length = 3; |
| MintVmo(regions[0]); |
| MintVmo(regions[1]); |
| tx_buffer_t tx; |
| tx.id = 1; |
| tx.data.vmo_id = kVmoId; |
| tx.data.parts_count = 2; |
| tx.data.parts_list = regions; |
| tx.head_length = 0; |
| tx.tail_length = 0; |
| tx.meta.frame_type = static_cast<uint8_t>(fuchsia::hardware::network::FrameType::ETHERNET); |
| tx.meta.info_type = static_cast<uint32_t>(fuchsia::hardware::network::InfoType::NO_INFO); |
| tx.meta.flags = static_cast<uint32_t>(fuchsia::hardware::network::TxFlags::TX_ACCEL_0); |
| auto b = vmos_.MakeTxBuffer(&tx, true); |
| EXPECT_EQ(b.id(), tx.id); |
| EXPECT_EQ(b.frame_type(), fuchsia::hardware::network::FrameType::ETHERNET); |
| auto meta = b.TakeMetadata(); |
| EXPECT_EQ(meta->info_type, fuchsia::hardware::network::InfoType::NO_INFO); |
| EXPECT_TRUE(meta->info.empty()); |
| EXPECT_EQ(meta->flags, static_cast<uint32_t>(fuchsia::hardware::network::TxFlags::TX_ACCEL_0)); |
| std::vector<uint8_t> data; |
| ASSERT_EQ(b.Read(&data), ZX_OK); |
| EXPECT_EQ(data, std::vector<uint8_t>({0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, 0x02})); |
| } |
| |
| TEST_F(BufferTest, TestBufferBuildRx) { |
| buffer_region_t parts[2]; |
| rx_space_buffer_t space; |
| space.id = 1; |
| space.data.vmo_id = kVmoId; |
| space.data.parts_count = 2; |
| space.data.parts_list = parts; |
| |
| parts[0].offset = 10; |
| parts[0].length = 5; |
| parts[1].offset = 100; |
| parts[1].length = 3; |
| |
| auto b = vmos_.MakeRxSpaceBuffer(&space); |
| |
| EXPECT_EQ(b.id(), space.id); |
| std::vector<uint8_t> wr_data({0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x00, 0x01, 0x02}); |
| ASSERT_EQ(b.Write(wr_data), ZX_OK); |
| EXPECT_EQ(ReadVmo(parts[0]), std::vector<uint8_t>({0xAA, 0xBB, 0xCC, 0xDD, 0xEE})); |
| EXPECT_EQ(ReadVmo(parts[1]), std::vector<uint8_t>({0x00, 0x01, 0x02})); |
| } |
| |
| TEST_F(BufferTest, CopyBuffer) { |
| buffer_region_t tx_parts[3]; |
| tx_parts[0].offset = 0; |
| tx_parts[0].length = 5; |
| tx_parts[1].offset = 10; |
| tx_parts[1].length = 3; |
| tx_parts[2].offset = 20; |
| tx_parts[2].length = 2; |
| MintVmo(tx_parts[0]); |
| MintVmo(tx_parts[1]); |
| MintVmo(tx_parts[0]); |
| MintVmo(tx_parts[2]); |
| tx_buffer_t tx; |
| tx.id = 1; |
| tx.data.vmo_id = kVmoId; |
| tx.data.parts_count = 3; |
| tx.data.parts_list = tx_parts; |
| |
| auto b_tx = vmos_.MakeTxBuffer(&tx, false); |
| |
| buffer_region_t rx_parts[3]; |
| rx_space_buffer_t space; |
| space.id = 1; |
| space.data.vmo_id = kVmoId; |
| space.data.parts_count = 3; |
| space.data.parts_list = rx_parts; |
| |
| rx_parts[0].offset = 100; |
| rx_parts[0].length = 3; |
| rx_parts[1].offset = 110; |
| rx_parts[1].length = 5; |
| rx_parts[2].offset = 120; |
| rx_parts[2].length = 100; |
| |
| auto b_rx = vmos_.MakeRxSpaceBuffer(&space); |
| |
| size_t total; |
| ASSERT_EQ(b_rx.CopyFrom(&b_tx, &total), ZX_OK); |
| EXPECT_EQ(total, 10ul); |
| |
| EXPECT_EQ(ReadVmo(rx_parts[0]), std::vector<uint8_t>({0x00, 0x01, 0x02})); |
| EXPECT_EQ(ReadVmo(rx_parts[1]), std::vector<uint8_t>({0x03, 0x04, 0x00, 0x01, 0x02})); |
| EXPECT_EQ(ReadVmo(buffer_region_t{.offset = rx_parts[2].offset, .length = 2}), |
| std::vector<uint8_t>({0x00, 0x01})); |
| } |
| |
| TEST_F(BufferTest, WriteFailure) { |
| buffer_region_t parts; |
| rx_space_buffer_t space; |
| space.id = 1; |
| space.data.vmo_id = kVmoId; |
| space.data.parts_count = 1; |
| space.data.parts_list = &parts; |
| |
| parts.offset = 10; |
| parts.length = 3; |
| |
| { |
| // Write more than buffer's length is invalid. |
| auto b = vmos_.MakeRxSpaceBuffer(&space); |
| ASSERT_EQ(b.Write({0x01, 0x02, 0x03, 0x04}), ZX_ERR_OUT_OF_RANGE); |
| } |
| { |
| // A buffer that doesn't fit its VMO is invalid. |
| parts.offset = kVmoSize; |
| auto b = vmos_.MakeRxSpaceBuffer(&space); |
| ASSERT_EQ(b.Write({0x01}), ZX_ERR_OUT_OF_RANGE); |
| } |
| { |
| // A buffer with an invalid vmo_id is invalid. |
| space.data.vmo_id = kVmoId + 1; |
| auto b = vmos_.MakeRxSpaceBuffer(&space); |
| ASSERT_EQ(b.Write({0x01}), ZX_ERR_NOT_FOUND); |
| } |
| } |
| |
| TEST_F(BufferTest, ReadFailure) { |
| buffer_region_t parts; |
| tx_buffer_t tx_buffer; |
| tx_buffer.id = 1; |
| tx_buffer.data.vmo_id = kVmoId; |
| tx_buffer.data.parts_count = 1; |
| tx_buffer.data.parts_list = &parts; |
| |
| parts.length = 3; |
| std::vector<uint8_t> data; |
| |
| { |
| // A buffer that doesn't fit its VMO is invalid. |
| parts.offset = kVmoSize; |
| auto b = vmos_.MakeTxBuffer(&tx_buffer, false); |
| ASSERT_EQ(b.Read(&data), ZX_ERR_OUT_OF_RANGE); |
| } |
| { |
| // A buffer with an invalid vmo_id is invalid. |
| tx_buffer.data.vmo_id = kVmoId + 1; |
| auto b = vmos_.MakeTxBuffer(&tx_buffer, false); |
| ASSERT_EQ(b.Read(&data), ZX_ERR_NOT_FOUND); |
| } |
| } |
| |
| TEST_F(BufferTest, CopyFailure) { |
| // Source region is out of range. |
| ASSERT_EQ(VmoStore::Copy(&vmos_, kVmoId, kVmoSize, &vmos_, kVmoId, 0, 10), ZX_ERR_OUT_OF_RANGE); |
| // Destination region is out of range, |
| ASSERT_EQ(VmoStore::Copy(&vmos_, kVmoId, 0, &vmos_, kVmoId, kVmoSize, 10), ZX_ERR_OUT_OF_RANGE); |
| // Source region is has bad id. |
| ASSERT_EQ(VmoStore::Copy(&vmos_, kVmoId + 1, 0, &vmos_, kVmoId, 0, 10), ZX_ERR_NOT_FOUND); |
| // Destination region is has bad id. |
| ASSERT_EQ(VmoStore::Copy(&vmos_, kVmoId, 0, &vmos_, kVmoId + 1, 0, 10), ZX_ERR_NOT_FOUND); |
| } |
| |
| } // namespace testing |
| } // namespace tun |
| } // namespace network |