blob: a6241769991160fba903742dae06988b9b64ffc8 [file] [log] [blame]
// 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 <gtest/gtest.h>
#include "src/lib/testing/predicates/status.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_OK(zx::vmo::create(kVmoSize, 0, &vmo));
ASSERT_OK(vmos_.RegisterVmo(kVmoId, std::move(vmo)));
}
public:
void MintVmo(size_t offset, size_t len) {
uint8_t val = 0;
while (len--) {
ASSERT_OK(vmos_.Write(kVmoId, offset, 1, &val));
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(region.vmo, kVmoId);
EXPECT_OK(vmos_.Read(kVmoId, region.offset, region.length, std::back_inserter(ret)));
return ret;
}
protected:
VmoStore vmos_;
};
TEST_F(BufferTest, TestBufferBuildTx) {
buffer_region_t regions[] = {
{.vmo = kVmoId, .offset = 10, .length = 5},
{.vmo = kVmoId, .offset = 100, .length = 3},
};
for (const buffer_region_t& region : regions) {
MintVmo(region);
}
tx_buffer_t tx = {
.id = 1,
.data_list = regions,
.data_count = std::size(regions),
.meta =
{
.info_type = static_cast<uint32_t>(fuchsia_hardware_network::wire::InfoType::kNoInfo),
.flags = static_cast<uint32_t>(fuchsia_hardware_network::wire::TxFlags::kTxAccel0),
.frame_type =
static_cast<uint8_t>(fuchsia_hardware_network::wire::FrameType::kEthernet),
},
};
TxBuffer b = vmos_.MakeTxBuffer(tx, true);
EXPECT_EQ(b.id(), tx.id);
EXPECT_EQ(b.frame_type(), fuchsia_hardware_network::wire::FrameType::kEthernet);
auto meta = b.TakeMetadata();
EXPECT_EQ(meta->info_type, fuchsia_hardware_network::wire::InfoType::kNoInfo);
EXPECT_TRUE(meta->info.empty());
EXPECT_EQ(meta->flags, static_cast<uint32_t>(fuchsia_hardware_network::wire::TxFlags::kTxAccel0));
std::vector<uint8_t> data;
ASSERT_OK(b.Read(data));
EXPECT_EQ(data, std::vector<uint8_t>({0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, 0x02}));
}
TEST_F(BufferTest, TestBufferBuildRx) {
const rx_space_buffer_t space_1 = {
.id = 1,
.region =
{
.vmo = kVmoId,
.offset = 10,
.length = 5,
},
};
const rx_space_buffer_t space_2 = {
.id = 2,
.region =
{
.vmo = kVmoId,
.offset = 100,
.length = 3,
},
};
RxBuffer b = vmos_.MakeRxSpaceBuffer(space_1);
b.PushRxSpace(space_2);
std::vector<uint8_t> wr_data({0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x00, 0x01, 0x02});
ASSERT_OK(b.Write(wr_data));
EXPECT_EQ(ReadVmo(space_1.region), std::vector<uint8_t>({0xAA, 0xBB, 0xCC, 0xDD, 0xEE}));
EXPECT_EQ(ReadVmo(space_2.region), std::vector<uint8_t>({0x00, 0x01, 0x02}));
}
TEST_F(BufferTest, CopyBuffer) {
buffer_region_t tx_parts[3] = {
{.vmo = kVmoId, .offset = 0, .length = 5},
{.vmo = kVmoId, .offset = 10, .length = 3},
{.vmo = kVmoId, .offset = 20, .length = 2},
};
for (const buffer_region_t& region : tx_parts) {
MintVmo(region);
}
tx_buffer_t tx = {
.id = 1,
.data_list = tx_parts,
.data_count = std::size(tx_parts),
};
TxBuffer b_tx = vmos_.MakeTxBuffer(tx, false);
rx_space_buffer_t rx_space[3] = {
{.id = 2, .region = {.vmo = kVmoId, .offset = 100, .length = 3}},
{.id = 3, .region = {.vmo = kVmoId, .offset = 110, .length = 5}},
{.id = 4, .region = {.vmo = kVmoId, .offset = 120, .length = 100}},
};
RxBuffer b_rx = vmos_.MakeEmptyRxBuffer();
for (const rx_space_buffer_t& space : rx_space) {
b_rx.PushRxSpace(space);
}
zx::result status = b_rx.CopyFrom(b_tx);
ASSERT_OK(status.status_value());
EXPECT_EQ(status.value(), 10ul);
EXPECT_EQ(ReadVmo(rx_space[0].region), std::vector<uint8_t>({0x00, 0x01, 0x02}));
EXPECT_EQ(ReadVmo(rx_space[1].region), std::vector<uint8_t>({0x03, 0x04, 0x00, 0x01, 0x02}));
EXPECT_EQ(ReadVmo(buffer_region_t{
.vmo = kVmoId,
.offset = rx_space[2].region.offset,
.length = 2,
}),
std::vector<uint8_t>({0x00, 0x01}));
}
TEST_F(BufferTest, WriteFailure) {
{
// Write more than buffer's length is invalid.
RxBuffer b = vmos_.MakeRxSpaceBuffer(rx_space_buffer_t{
.id = 1,
.region =
{
.vmo = kVmoId,
.offset = 10,
.length = 3,
},
});
ASSERT_EQ(b.Write({0x01, 0x02, 0x03, 0x04}), ZX_ERR_OUT_OF_RANGE);
}
{
// A buffer that doesn't fit its VMO is invalid.
RxBuffer b = vmos_.MakeRxSpaceBuffer(rx_space_buffer_t{
.id = 1,
.region =
{
.vmo = kVmoId,
.offset = kVmoSize,
.length = 3,
},
});
ASSERT_EQ(b.Write({0x01}), ZX_ERR_OUT_OF_RANGE);
}
{
// A buffer with an invalid vmo_id is invalid.
RxBuffer b = vmos_.MakeRxSpaceBuffer(rx_space_buffer_t{
.id = 1,
.region =
{
.vmo = kVmoId + 1,
.offset = 10,
.length = 3,
},
});
ASSERT_EQ(b.Write({0x01}), ZX_ERR_NOT_FOUND);
}
}
TEST_F(BufferTest, ReadFailure) {
std::vector<uint8_t> data;
{
// A buffer that doesn't fit its VMO is invalid.
buffer_region_t part = {.vmo = kVmoId, .offset = kVmoSize, .length = 10};
TxBuffer b = vmos_.MakeTxBuffer(
tx_buffer_t{
.id = 1,
.data_list = &part,
.data_count = 1,
},
false);
ASSERT_EQ(b.Read(data), ZX_ERR_OUT_OF_RANGE);
}
{
// A buffer with an invalid vmo_id is invalid.
buffer_region_t part = {.vmo = kVmoId + 1, .length = 10};
TxBuffer b = vmos_.MakeTxBuffer(
tx_buffer_t{
.id = 1,
.data_list = &part,
.data_count = 1,
},
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