text: // Copyright 2017 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 <lib/zx/fifo.h>
  
  #include <zxtest/zxtest.h>
  
  namespace {
  
  using ElementType = uint64_t;
  constexpr size_t kElementSize = sizeof(ElementType);
  
  zx_signals_t GetSignals(const zx::fifo& fifo) {
    zx_signals_t pending;
    zx_status_t status = fifo.wait_one(0xFFFFFFFF, zx::time(), &pending);
    if ((status != ZX_OK) && (status != ZX_ERR_TIMED_OUT)) {
      return 0xFFFFFFFF;
    }
    return pending;
  }
  
  #define EXPECT_SIGNALS(h, s) EXPECT_EQ(GetSignals(h), s)
  
  TEST(FifoTest, InvalidParametersReturnOutOfRange) {
    zx::fifo fifo_a, fifo_b;
  
    // ensure parameter validation works
    EXPECT_EQ(zx::fifo::create(0, 0, 0, &fifo_a, &fifo_b),
              ZX_ERR_OUT_OF_RANGE);  // too small
    EXPECT_EQ(zx::fifo::create(128, 33, 0, &fifo_a, &fifo_b),
              ZX_ERR_OUT_OF_RANGE);  // too large
    EXPECT_EQ(zx::fifo::create(0, 0, 1, &fifo_a, &fifo_b),
              ZX_ERR_OUT_OF_RANGE);  // invalid options
  }
  
  TEST(FifoTest, EndpointsAreRelated) {
    zx::fifo fifo_a, fifo_b;
  
    // simple 8 x 8 fifo
    ASSERT_OK(zx::fifo::create(8, kElementSize, 0, &fifo_a, &fifo_b));
    EXPECT_SIGNALS(fifo_a, ZX_FIFO_WRITABLE);
    EXPECT_SIGNALS(fifo_b, ZX_FIFO_WRITABLE);
  
    // Check that koids line up.
    zx_info_handle_basic_t info_a = {}, info_b = {};
    ASSERT_OK(fifo_a.get_info(ZX_INFO_HANDLE_BASIC, &info_a, sizeof(info_a), nullptr, nullptr));
  
    ASSERT_OK(fifo_b.get_info(ZX_INFO_HANDLE_BASIC, &info_b, sizeof(info_b), nullptr, nullptr));
    ASSERT_NE(info_a.koid, 0u, "zero koid!");
    ASSERT_NE(info_a.related_koid, 0u, "zero peer koid!");
    ASSERT_NE(info_b.koid, 0u, "zero koid!");
    ASSERT_NE(info_b.related_koid, 0u, "zero peer koid!");
    ASSERT_EQ(info_a.koid, info_b.related_koid, "mismatched koids!");
    ASSERT_EQ(info_b.koid, info_a.related_koid, "mismatched koids!");
  }
  
  TEST(FifoTest, EmptyQueueReturnsErrShouldWait) {
    zx::fifo fifo_a, fifo_b;
    ElementType actual_elements[8] = {};
  
    // simple 8 x 8 fifo
    ASSERT_OK(zx::fifo::create(8, kElementSize, 0, &fifo_a, &fifo_b));
  
    // should not be able to read any entries from an empty fifo
    size_t actual_count;
    EXPECT_EQ(fifo_a.read(kElementSize, actual_elements, 8, &actual_count), ZX_ERR_SHOULD_WAIT);
  }
  
  TEST(FifoTest, ReadAndWriteValidatesSizeAndElementCount) {
    zx::fifo fifo_a, fifo_b;
    ElementType expected_elements[] = {1, 2, 3, 4, 5, 6, 7, 8};
    ElementType actual_elements[8] = {};
    size_t actual_count;
  
    // simple 8 x 8 fifo
    ASSERT_OK(zx::fifo::create(8, kElementSize, 0, &fifo_a, &fifo_b));
  
    // not allowed to read or write zero elements
    EXPECT_EQ(fifo_a.read(kElementSize, actual_elements, 0, &actual_count), ZX_ERR_OUT_OF_RANGE);
    EXPECT_EQ(fifo_a.write(kElementSize, expected_elements, 0, &actual_count), ZX_ERR_OUT_OF_RANGE);
  
    // element size must match
    EXPECT_EQ(fifo_a.read(kElementSize + 1, actual_elements, 8, &actual_count), ZX_ERR_OUT_OF_RANGE);
    EXPECT_EQ(fifo_a.write(kElementSize + 1, expected_elements, 8, &actual_count),
              ZX_ERR_OUT_OF_RANGE);
  }
  
  TEST(FifoTest, DequeueSignalsWriteable) {
    zx::fifo fifo_a, fifo_b;
    ElementType expected_elements[] = {1, 2, 3, 4, 5, 6, 7, 8};
    ElementType actual_elements[8] = {};
    // simple 8 x 8 fifo
    ASSERT_OK(zx::fifo::create(8, kElementSize, 0, &fifo_a, &fifo_b));
  
    EXPECT_SIGNALS(fifo_a, ZX_FIFO_WRITABLE);
    EXPECT_SIGNALS(fifo_b, ZX_FIFO_WRITABLE);
  
    size_t actual_count;
    // should be able to write all entries into empty fifo
    ASSERT_OK(fifo_a.write(kElementSize, expected_elements, 8, &actual_count));
    ASSERT_EQ(actual_count, 8u);
    EXPECT_SIGNALS(fifo_b, ZX_FIFO_READABLE | ZX_FIFO_WRITABLE);
  
    // should be able to write no entries into a full fifo
    ASSERT_EQ(fifo_a.write(kElementSize, expected_elements, 8, &actual_count), ZX_ERR_SHOULD_WAIT);
    EXPECT_SIGNALS(fifo_a, 0u);
  
    // read half the entries, make sure they're what we expect
    ASSERT_OK(fifo_b.read(kElementSize, actual_elements, 4, &actual_count));
    ASSERT_EQ(actual_count, 4u);
    ASSERT_EQ(actual_elements[0], 1u);
    ASSERT_EQ(actual_elements[1], 2u);
    ASSERT_EQ(actual_elements[2], 3u);
    ASSERT_EQ(actual_elements[3], 4u);
    ASSERT_EQ(actual_elements[4], 0u);
  
    // should be writable again now
    EXPECT_SIGNALS(fifo_a, ZX_FIFO_WRITABLE);
  
    ASSERT_OK(fifo_b.read(kElementSize, actual_elements, 4, &actual_count));
    ASSERT_EQ(actual_elements[0], 5u);
    ASSERT_EQ(actual_elements[1], 6u);
    ASSERT_EQ(actual_elements[2], 7u);
    ASSERT_EQ(actual_elements[3], 8u);
    ASSERT_EQ(actual_elements[4], 0u);
  
    // should no longer be readable
    EXPECT_SIGNALS(fifo_b, ZX_FIFO_WRITABLE);
  }
  
  TEST(FifoTest, FifoOrderIsPreserved) {
    zx::fifo fifo_a, fifo_b;
    ElementType expected_elements[] = {1, 2, 3, 4, 5, 6, 7, 8};
    ElementType actual_elements[8] = {};
  
    // simple 8 x 8 fifo
    ASSERT_OK(zx::fifo::create(8, kElementSize, 0, &fifo_a, &fifo_b));
  
    size_t actual_count;
    // should be able to write all entries into empty fifo
    ASSERT_OK(fifo_a.write(kElementSize, expected_elements, 8, &actual_count));
  
    // read half the entries, make sure they're what we expect
    ASSERT_OK(fifo_b.read(kElementSize, actual_elements, 4, &actual_count));
  
    // write some more, wrapping to the front again
    expected_elements[0] = 9u;
    expected_elements[1] = 10u;
    ASSERT_OK(fifo_a.write(kElementSize, expected_elements, 2, &actual_count));
    ASSERT_EQ(actual_count, 2u);
  
    // read across the wrap, test partial read
    ASSERT_OK(fifo_b.read(kElementSize, actual_elements, 8, &actual_count));
    ASSERT_EQ(actual_count, 6u);
    ASSERT_EQ(actual_elements[0], 5u);
    ASSERT_EQ(actual_elements[1], 6u);
    ASSERT_EQ(actual_elements[2], 7u);
    ASSERT_EQ(actual_elements[3], 8u);
    ASSERT_EQ(actual_elements[4], 9u);
    ASSERT_EQ(actual_elements[5], 10u);
  
    // write across the wrap
    expected_elements[0] = 11u;
    expected_elements[1] = 12u;
    expected_elements[2] = 13u;
    expected_elements[3] = 14u;
    expected_elements[4] = 15u;
    ASSERT_OK(fifo_a.write(kElementSize, expected_elements, 5, &actual_count));
    ASSERT_EQ(actual_count, 5u);
  }
  
  TEST(FifoTest, PartialWriteQueuesElementsThatFit) {
    zx::fifo fifo_a, fifo_b;
    ElementType expected_elements[] = {1, 2, 3, 4, 5, 6, 7, 8};
  
    // simple 8 x 8 fifo
    ASSERT_OK(zx::fifo::create(8, kElementSize, 0, &fifo_a, &fifo_b));
  
    size_t actual_count;
    // Fill it up for 5 elements.
    ASSERT_OK(fifo_a.write(kElementSize, expected_elements, 5, &actual_count));
  
    // partial write test
    expected_elements[0] = 16u;
    expected_elements[1] = 17u;
    expected_elements[2] = 18u;
    ASSERT_OK(fifo_a.write(kElementSize, expected_elements, 5, &actual_count));
    ASSERT_EQ(actual_count, 3u);
  }
  
  TEST(FifoTest, IndividualReadsPreserveOrder) {
    zx::fifo fifo_a, fifo_b;
    ElementType expected_elements[] = {1, 2, 3, 4, 5, 6, 7, 8};
  
    // simple 8 x 8 fifo
    ASSERT_OK(zx::fifo::create(8, kElementSize, 0, &fifo_a, &fifo_b));
  
    size_t actual_count;
    // Fill it up
    ASSERT_OK(fifo_a.write(kElementSize, expected_elements, 8, &actual_count));
  
    ElementType actual_element;
    // small reads
    for (unsigned i = 0; i < 8; i++) {
      ASSERT_OK(fifo_b.read(kElementSize, &actual_element, 1, &actual_count));
      ASSERT_EQ(actual_count, 1u);
      ASSERT_EQ(actual_element, expected_elements[i]);
    }
  }
  
  TEST(FifoTest, EndpointCloseSignalsPeerClosed) {
    zx::fifo fifo_b;
    ElementType expected_element = 19u;
    ElementType actual_elements[8] = {};
  
    size_t actual_count;
  
    {
      zx::fifo fifo_a;
      ASSERT_OK(zx::fifo::create(8, kElementSize, 0, &fifo_a, &fifo_b));
  
      // write and then close, verify we can read written entries before
      // receiving ZX_ERR_PEER_CLOSED.
      ASSERT_OK(fifo_a.write(kElementSize, &expected_element, 1, &actual_count));
      ASSERT_EQ(actual_count, 1u);
      // end of scope for fifo_b so it's closed.
    }
  
    EXPECT_SIGNALS(fifo_b, ZX_FIFO_READABLE | ZX_FIFO_PEER_CLOSED);
    ASSERT_OK(fifo_b.read(kElementSize, actual_elements, 8, &actual_count));
    ASSERT_EQ(actual_count, 1u);
    EXPECT_SIGNALS(fifo_b, ZX_FIFO_PEER_CLOSED);
    ASSERT_EQ(fifo_b.read(kElementSize, actual_elements, 8, &actual_count), ZX_ERR_PEER_CLOSED);
    ASSERT_EQ(fifo_b.signal_peer(0u, ZX_USER_SIGNAL_0), ZX_ERR_PEER_CLOSED);
  }
  
  TEST(FifoTest, NonPowerOfTwoCountSupported) {
    zx::fifo fifo_a, fifo_b;
    ASSERT_OK(zx::fifo::create(10, kElementSize, 0, &fifo_a, &fifo_b));
  
    ElementType expected_elements[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    ElementType actual_elements[9] = {};
    size_t actual_count;
  
    // Write to, then drain, the FIFO.
    // Intentionally write one element less than the FIFO can hold, so the next write will wrap.
    ASSERT_OK(
        fifo_a.write(kElementSize, &expected_elements, countof(expected_elements), &actual_count));
    ASSERT_EQ(actual_count, 9u);
    ASSERT_OK(fifo_b.read(kElementSize, actual_elements, countof(actual_elements), &actual_count));
    ASSERT_EQ(actual_count, 9u);
  
    // Repeat the process. This write spans the buffer wrap.
    ASSERT_OK(
        fifo_a.write(kElementSize, &expected_elements, countof(expected_elements), &actual_count));
    ASSERT_EQ(actual_count, 9u);
    ASSERT_OK(fifo_b.read(kElementSize, actual_elements, countof(actual_elements), &actual_count));
    ASSERT_EQ(actual_count, 9u);
  }
  
  }  // namespace
  