//===-- smoke tests for RPC -----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/__support/RPC/rpc.h"

#include "test/UnitTest/Test.h"

namespace {
enum { lane_size = 8, port_count = 4 };

using ProcAType = LIBC_NAMESPACE::rpc::Process<false>;
using ProcBType = LIBC_NAMESPACE::rpc::Process<true>;

static_assert(ProcAType::inbox_offset(port_count) ==
              ProcBType::outbox_offset(port_count));

static_assert(ProcAType::outbox_offset(port_count) ==
              ProcBType::inbox_offset(port_count));

enum { alloc_size = ProcAType::allocation_size(port_count, 1) };

alignas(64) char buffer[alloc_size] = {0};
} // namespace

TEST(LlvmLibcRPCSmoke, SanityCheck) {

  ProcAType ProcA(port_count, buffer);
  ProcBType ProcB(port_count, buffer);

  uint64_t index = 0; // any < port_count
  uint64_t lane_mask = 1;

  // Each process has its own local lock for index
  EXPECT_TRUE(ProcA.try_lock(lane_mask, index));
  EXPECT_TRUE(ProcB.try_lock(lane_mask, index));

  // All zero to begin with
  EXPECT_EQ(ProcA.load_inbox(lane_mask, index), 0u);
  EXPECT_EQ(ProcB.load_inbox(lane_mask, index), 0u);
  EXPECT_EQ(ProcA.load_outbox(lane_mask, index), 0u);
  EXPECT_EQ(ProcB.load_outbox(lane_mask, index), 0u);

  // Available for ProcA and not for ProcB
  EXPECT_FALSE(ProcA.buffer_unavailable(ProcA.load_inbox(lane_mask, index),
                                        ProcA.load_outbox(lane_mask, index)));
  EXPECT_TRUE(ProcB.buffer_unavailable(ProcB.load_inbox(lane_mask, index),
                                       ProcB.load_outbox(lane_mask, index)));

  // ProcA write to outbox
  uint32_t ProcAOutbox = ProcA.load_outbox(lane_mask, index);
  EXPECT_EQ(ProcAOutbox, 0u);
  ProcAOutbox = ProcA.invert_outbox(index, ProcAOutbox);
  EXPECT_EQ(ProcAOutbox, 1u);

  // No longer available for ProcA
  EXPECT_TRUE(ProcA.buffer_unavailable(ProcA.load_inbox(lane_mask, index),
                                       ProcAOutbox));

  // Outbox is still zero, hasn't been written to
  EXPECT_EQ(ProcB.load_outbox(lane_mask, index), 0u);

  // Wait for ownership will terminate because load_inbox returns 1
  EXPECT_EQ(ProcB.load_inbox(lane_mask, index), 1u);
  ProcB.wait_for_ownership(lane_mask, index, 0u, 0u);

  // and B now has the buffer available
  EXPECT_FALSE(ProcB.buffer_unavailable(ProcB.load_inbox(lane_mask, index),
                                        ProcB.load_outbox(lane_mask, index)));

  // Enough checks for one test, close the locks
  ProcA.unlock(lane_mask, index);
  ProcB.unlock(lane_mask, index);
}
