text: // Copyright 2018 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/async-testutils/dispatcher_stub.h>
  #include <lib/async/cpp/trap.h>
  #include <unittest/unittest.h>
  
  namespace {
  
  const zx_handle_t dummy_guest = static_cast<zx_handle_t>(1);
  const zx_vaddr_t dummy_addr = 0x1000;
  const size_t dummy_length = 0x1000;
  const zx_packet_guest_bell_t dummy_bell{
      .addr = dummy_addr,
      .reserved0 = 0u,
      .reserved1 = 0u,
      .reserved2 = 0u,
  };
  
  class MockDispatcher : public async::DispatcherStub {
  public:
      zx_status_t SetGuestBellTrap(async_guest_bell_trap_t* trap,
                                   const zx::guest& guest,
                                   zx_vaddr_t addr, size_t length) override {
          last_trap = trap;
          last_guest = guest.get();
          last_addr = addr;
          last_length = length;
          return ZX_OK;
      }
  
      async_guest_bell_trap_t* last_trap = nullptr;
      zx_handle_t last_guest = ZX_HANDLE_INVALID;
      zx_vaddr_t last_addr = 0u;
      size_t last_length = 0u;
  };
  
  class Harness {
  public:
      void Handler(async_dispatcher_t* dispatcher,
                   async::GuestBellTrapBase* trap,
                   zx_status_t status,
                   const zx_packet_guest_bell_t* bell) {
          handler_ran = true;
          last_trap = trap;
          last_status = status;
          last_bell = bell;
      }
  
      virtual async::GuestBellTrapBase& trap() = 0;
  
      bool handler_ran = false;
      async::GuestBellTrapBase* last_trap = nullptr;
      zx_status_t last_status = ZX_ERR_INTERNAL;
      const zx_packet_guest_bell_t* last_bell = nullptr;
  };
  
  class LambdaHarness : public Harness {
  public:
      async::GuestBellTrapBase& trap() override { return trap_; }
  
  private:
      async::GuestBellTrap trap_{[this](async_dispatcher_t* dispatcher, async::GuestBellTrap* trap,
                                        zx_status_t status, const zx_packet_guest_bell_t* bell) {
          Handler(dispatcher, trap, status, bell);
      }};
  };
  
  class MethodHarness : public Harness {
  public:
      async::GuestBellTrapBase& trap() override { return trap_; }
  
  private:
      async::GuestBellTrapMethod<Harness, &Harness::Handler> trap_{this};
  };
  
  bool guest_bell_trap_set_handler_test() {
      BEGIN_TEST;
  
      {
          async::GuestBellTrap trap;
          EXPECT_FALSE(trap.has_handler());
  
          trap.set_handler([](async_dispatcher_t* dispatcher, async::GuestBellTrap* trap,
                              zx_status_t status, const zx_packet_guest_bell_t* bell) {});
          EXPECT_TRUE(trap.has_handler());
      }
  
      {
          async::GuestBellTrap trap([](async_dispatcher_t* dispatcher, async::GuestBellTrap* trap,
                                       zx_status_t status, const zx_packet_guest_bell_t* bell) {});
          EXPECT_TRUE(trap.has_handler());
      }
  
      END_TEST;
  }
  
  template <typename Harness>
  bool guest_bell_trap_test() {
      BEGIN_TEST;
  
      MockDispatcher dispatcher;
      Harness harness;
  
      EXPECT_EQ(ZX_OK, harness.trap().SetTrap(
                    &dispatcher, *zx::unowned_guest(dummy_guest), dummy_addr, dummy_length));
      EXPECT_EQ(dummy_guest, dispatcher.last_guest);
      EXPECT_EQ(dummy_addr, dispatcher.last_addr);
      EXPECT_EQ(dummy_length, dispatcher.last_length);
  
      dispatcher.last_trap->handler(&dispatcher, dispatcher.last_trap, ZX_OK, &dummy_bell);
      EXPECT_TRUE(harness.handler_ran);
      EXPECT_EQ(&harness.trap(), harness.last_trap);
      EXPECT_EQ(ZX_OK, harness.last_status);
      EXPECT_EQ(&dummy_bell, harness.last_bell);
  
      END_TEST;
  }
  
  } // namespace
  
  BEGIN_TEST_CASE(trap_tests)
  RUN_TEST(guest_bell_trap_set_handler_test)
  RUN_TEST((guest_bell_trap_test<LambdaHarness>))
  RUN_TEST((guest_bell_trap_test<MethodHarness>))
  END_TEST_CASE(trap_tests)
  