[utest][core][interrupt] Convert to C++

Convert interrupt-test from C to C++, so that we can exercise ulib/zx.
In addition, it also simplifies the test and reduces the code.

Test: interrupt-test
Change-Id: Iccc18b0860cd3cb2743381703924e5540366e763
diff --git a/system/utest/core/interrupt/interrupt-test.c b/system/utest/core/interrupt/interrupt-test.c
deleted file mode 100644
index 7802f9f..0000000
--- a/system/utest/core/interrupt/interrupt-test.c
+++ /dev/null
@@ -1,342 +0,0 @@
-// 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 <unittest/unittest.h>
-#include <zircon/syscalls.h>
-#include <zircon/process.h>
-#include <zircon/syscalls/port.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <threads.h>
-
-extern zx_handle_t get_root_resource(void);
-
-static bool get_thread_info(zx_handle_t thread, zx_info_thread_t* info) {
-    return zx_object_get_info(thread, ZX_INFO_THREAD, info, sizeof(*info), NULL, NULL) == ZX_OK;
-}
-
-static bool wait_thread(zx_handle_t thread, uint32_t reason) {
-    while (true) {
-        zx_info_thread_t info;
-        ASSERT_TRUE(get_thread_info(thread, &info), "");
-        if (info.state == reason)
-            break;
-        zx_nanosleep(zx_deadline_after(ZX_MSEC(1)));
-    }
-    return true;
-}
-
-static void thread_entry(uintptr_t arg1, uintptr_t arg2) {
-    zx_handle_t vinth = *(zx_handle_t*)arg1;
-    while(1) {
-        zx_interrupt_wait(vinth, NULL);
-    }
-    zx_thread_exit();
-}
-
-// Tests to bind interrupt to a non-bindable port
-static bool interrupt_port_non_bindable_test(void) {
-    BEGIN_TEST;
-
-    zx_handle_t port_handle;
-    zx_handle_t virt_interrupt_port_handle;
-    zx_handle_t rsrc = get_root_resource();
-    uint32_t key = 789;
-
-    ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL,
-                                  &virt_interrupt_port_handle), ZX_OK, "");
-    ASSERT_EQ(zx_port_create(0, &port_handle), ZX_OK, "");
-
-    ASSERT_EQ(zx_interrupt_bind(virt_interrupt_port_handle,
-                                port_handle, key, 0), ZX_ERR_WRONG_TYPE, "");
-
-    ASSERT_EQ(zx_handle_close(port_handle), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(virt_interrupt_port_handle), ZX_OK, "");
-
-    END_TEST;
-}
-
-// Tests Interrupts bound to a port
-static bool interrupt_port_bound_test(void) {
-    BEGIN_TEST;
-
-    zx_handle_t virt_interrupt_port_handle;
-    zx_handle_t port_handle_bind;
-    zx_time_t signaled_timestamp_1 = 12345;
-    zx_time_t signaled_timestamp_2 = 67890;
-    uint32_t key = 789;
-    zx_port_packet_t out;
-    zx_handle_t rsrc = get_root_resource();
-
-    ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL,
-                                  &virt_interrupt_port_handle), ZX_OK, "");
-    ASSERT_EQ(zx_port_create(ZX_PORT_BIND_TO_INTERRUPT, &port_handle_bind), ZX_OK, "");
-
-    // Test port binding
-    ASSERT_EQ(zx_interrupt_bind(virt_interrupt_port_handle, port_handle_bind, key, 0), ZX_OK, "");
-    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_port_handle, 0, signaled_timestamp_1), ZX_OK, "");
-    ASSERT_EQ(zx_port_wait(port_handle_bind, ZX_TIME_INFINITE, &out), ZX_OK, "");
-    ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1, "");
-
-    // Triggering 2nd time, ACKing it causes port packet to be delivered
-    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_port_handle, 0, signaled_timestamp_1), ZX_OK, "");
-    ASSERT_EQ(zx_interrupt_ack(virt_interrupt_port_handle), ZX_OK, "");
-    ASSERT_EQ(zx_port_wait(port_handle_bind, ZX_TIME_INFINITE, &out), ZX_OK, "");
-    ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1, "");
-    ASSERT_EQ(out.key, key, "");
-    ASSERT_EQ(out.type, ZX_PKT_TYPE_INTERRUPT, "");
-    ASSERT_EQ(out.status, ZX_OK, "");
-    ASSERT_EQ(zx_interrupt_ack(virt_interrupt_port_handle), ZX_OK, "");
-
-    // Triggering it twice
-    // the 2nd timestamp is recorded and upon ACK another packet is queued
-    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_port_handle, 0, signaled_timestamp_1), ZX_OK, "");
-    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_port_handle, 0, signaled_timestamp_2), ZX_OK, "");
-    ASSERT_EQ(zx_port_wait(port_handle_bind, ZX_TIME_INFINITE, &out), ZX_OK, "");
-    ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1, "");
-    ASSERT_EQ(zx_interrupt_ack(virt_interrupt_port_handle), ZX_OK, "");
-    ASSERT_EQ(zx_port_wait(port_handle_bind, ZX_TIME_INFINITE, &out), ZX_OK, "");
-    ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_2, "");
-
-    // Try to destroy now, expecting to return error telling packet
-    // has been read but the interrupt has not been re-armed
-    ASSERT_EQ(zx_interrupt_destroy(virt_interrupt_port_handle), ZX_ERR_NOT_FOUND,"");
-    ASSERT_EQ(zx_interrupt_ack(virt_interrupt_port_handle), ZX_ERR_CANCELED, "");
-    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_port_handle, 0,
-                                   signaled_timestamp_1), ZX_ERR_CANCELED, "");
-
-    ASSERT_EQ(zx_handle_close(virt_interrupt_port_handle), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(port_handle_bind), ZX_OK, "");
-
-    END_TEST;
-}
-
-// Tests support for virtual interrupts
-static bool interrupt_test(void) {
-    BEGIN_TEST;
-
-    zx_handle_t virt_interrupt_handle;
-    zx_handle_t virt_interrupt_handle_cancelled;
-    zx_time_t timestamp;
-    zx_time_t signaled_timestamp = 12345;
-    zx_handle_t rsrc = get_root_resource();
-
-    ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL,
-                                  &virt_interrupt_handle), ZX_OK, "");
-    ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL,
-                                  &virt_interrupt_handle_cancelled), ZX_OK, "");
-    ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_SLOT_USER,
-                                  &virt_interrupt_handle), ZX_ERR_INVALID_ARGS, "");
-
-
-    ASSERT_EQ(zx_interrupt_destroy(virt_interrupt_handle_cancelled), ZX_OK, "");
-    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_handle_cancelled,
-                                   0, signaled_timestamp), ZX_ERR_CANCELED, "");
-
-    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_handle, 0, signaled_timestamp), ZX_OK, "");
-
-    ASSERT_EQ(zx_interrupt_wait(virt_interrupt_handle_cancelled, &timestamp), ZX_ERR_CANCELED, "");
-    ASSERT_EQ(zx_interrupt_wait(virt_interrupt_handle, &timestamp), ZX_OK, "");
-    ASSERT_EQ(timestamp, signaled_timestamp, "");
-
-    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_handle, 0, signaled_timestamp), ZX_OK, "");
-    ASSERT_EQ(zx_interrupt_wait(virt_interrupt_handle, NULL), ZX_OK, "");
-
-    ASSERT_EQ(zx_handle_close(virt_interrupt_handle), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(virt_interrupt_handle_cancelled), ZX_OK, "");
-    ASSERT_EQ(zx_interrupt_trigger(virt_interrupt_handle,
-                                   0, signaled_timestamp), ZX_ERR_BAD_HANDLE, "");
-
-    END_TEST;
-}
-
-// Tests interrupt thread after suspend/resume
-static bool interrupt_suspend_test(void) {
-    BEGIN_TEST;
-
-    zx_handle_t thread_h;
-    const char* thread_name = "interrupt_test_thread";
-    // preallocated stack to satisfy the thread we create
-    static uint8_t stack[1024] __ALIGNED(16);
-    zx_handle_t rsrc = get_root_resource();
-    zx_handle_t vinth;
-    ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL, &vinth), ZX_OK, "");
-
-    // Create and start a thread which waits for an IRQ
-    ASSERT_EQ(zx_thread_create(zx_process_self(), thread_name, strlen(thread_name),
-                               0, &thread_h), ZX_OK, "");
-
-    ASSERT_EQ(zx_thread_start(thread_h, (uintptr_t)thread_entry,
-                             (uintptr_t)stack + sizeof(stack),
-                             (uintptr_t)&vinth, 0), ZX_OK, "");
-
-    // Wait till the thread is in blocked state
-    ASSERT_TRUE(wait_thread(thread_h, ZX_THREAD_STATE_BLOCKED_INTERRUPT), "");
-
-    // Suspend the thread, wait till it is suspended
-    zx_handle_t suspend_token = ZX_HANDLE_INVALID;
-    ASSERT_EQ(zx_task_suspend_token(thread_h, &suspend_token), ZX_OK, "");
-    ASSERT_TRUE(wait_thread(thread_h, ZX_THREAD_STATE_SUSPENDED), "");
-
-    // Resume the thread, wait till it is back to being in blocked state
-    ASSERT_EQ(zx_handle_close(suspend_token), ZX_OK, "");
-    ASSERT_TRUE(wait_thread(thread_h, ZX_THREAD_STATE_BLOCKED_INTERRUPT), "");
-
-    END_TEST;
-}
-
-// Tests binding an interrupt to multiple VCPUs
-static bool interrupt_bind_vcpu_test(void) {
-    BEGIN_TEST;
-
-    zx_handle_t rsrc = get_root_resource();
-    zx_handle_t interrupt;
-    zx_handle_t guest;
-    zx_handle_t vmar;
-    zx_handle_t vcpu1;
-    zx_handle_t vcpu2;
-
-    zx_status_t status = zx_guest_create(rsrc, 0, &guest, &vmar);
-    if (status == ZX_ERR_NOT_SUPPORTED) {
-        fprintf(stderr, "Guest creation not supported\n");
-        return true;
-    }
-    ASSERT_EQ(status, ZX_OK, "");
-
-    ASSERT_EQ(zx_interrupt_create(rsrc, 0, 0, &interrupt), ZX_OK, "");
-    ASSERT_EQ(zx_vcpu_create(guest, 0, 0, &vcpu1), ZX_OK, "");
-    ASSERT_EQ(zx_vcpu_create(guest, 0, 0, &vcpu2), ZX_OK, "");
-
-    ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt, vcpu1, 0), ZX_OK, "");
-    ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt, vcpu2, 0), ZX_OK, "");
-
-    ASSERT_EQ(zx_handle_close(vcpu1), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(vcpu2), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(vmar), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(guest), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(interrupt), ZX_OK, "");
-
-    END_TEST;
-}
-
-// Tests binding a virtual interrupt to a VCPU
-static bool interrupt_bind_vcpu_not_supported_test(void) {
-    BEGIN_TEST;
-
-    zx_handle_t rsrc = get_root_resource();
-    zx_handle_t interrupt;
-    zx_handle_t port;
-    zx_handle_t guest;
-    zx_handle_t vmar;
-    zx_handle_t vcpu;
-
-    zx_status_t status = zx_guest_create(rsrc, 0, &guest, &vmar);
-    if (status == ZX_ERR_NOT_SUPPORTED) {
-        fprintf(stderr, "Guest creation not supported\n");
-        return true;
-    }
-    ASSERT_EQ(status, ZX_OK, "");
-
-    ASSERT_EQ(zx_interrupt_create(rsrc, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK, "");
-    ASSERT_EQ(zx_port_create(ZX_PORT_BIND_TO_INTERRUPT, &port), ZX_OK, "");
-    ASSERT_EQ(zx_vcpu_create(guest, 0, 0, &vcpu), ZX_OK, "");
-
-    ASSERT_EQ(zx_interrupt_bind(interrupt, port, 0, 0), ZX_OK, "");
-    ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt, vcpu, 0), ZX_ERR_NOT_SUPPORTED, "");
-
-    ASSERT_EQ(zx_handle_close(vcpu), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(vmar), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(guest), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(port), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(interrupt), ZX_OK, "");
-
-    END_TEST;
-}
-
-// Tests binding an interrupt to a VCPU, after binding it to a port
-static bool interrupt_bind_vcpu_already_bound_test(void) {
-    BEGIN_TEST;
-
-    zx_handle_t rsrc = get_root_resource();
-    zx_handle_t interrupt;
-    zx_handle_t port;
-    zx_handle_t guest;
-    zx_handle_t vmar;
-    zx_handle_t vcpu;
-
-    zx_status_t status = zx_guest_create(rsrc, 0, &guest, &vmar);
-    if (status == ZX_ERR_NOT_SUPPORTED) {
-        fprintf(stderr, "Guest creation not supported\n");
-        return true;
-    }
-    ASSERT_EQ(status, ZX_OK, "");
-
-    ASSERT_EQ(zx_interrupt_create(rsrc, 0, 0, &interrupt), ZX_OK, "");
-    ASSERT_EQ(zx_port_create(ZX_PORT_BIND_TO_INTERRUPT, &port), ZX_OK, "");
-    ASSERT_EQ(zx_vcpu_create(guest, 0, 0, &vcpu), ZX_OK, "");
-
-    ASSERT_EQ(zx_interrupt_bind(interrupt, port, 0, 0), ZX_OK, "");
-    ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt, vcpu, 0), ZX_ERR_ALREADY_BOUND, "");
-
-    ASSERT_EQ(zx_handle_close(vcpu), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(vmar), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(guest), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(port), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(interrupt), ZX_OK, "");
-
-    END_TEST;
-}
-
-// Tests binding an interrupt to VCPUs from different guests
-static bool interrupt_bind_vcpu_multiple_guests_test(void) {
-    BEGIN_TEST;
-
-    zx_handle_t rsrc = get_root_resource();
-    zx_handle_t interrupt;
-    zx_handle_t guest1;
-    zx_handle_t guest2;
-    zx_handle_t vmar1;
-    zx_handle_t vmar2;
-    zx_handle_t vcpu1;
-    zx_handle_t vcpu2;
-
-    zx_status_t status = zx_guest_create(rsrc, 0, &guest1, &vmar1);
-    if (status == ZX_ERR_NOT_SUPPORTED) {
-        fprintf(stderr, "Guest creation not supported\n");
-        return true;
-    }
-    ASSERT_EQ(status, ZX_OK, "");
-
-    ASSERT_EQ(zx_interrupt_create(rsrc, 0, 0, &interrupt), ZX_OK, "");
-    ASSERT_EQ(zx_vcpu_create(guest1, 0, 0, &vcpu1), ZX_OK, "");
-    ASSERT_EQ(zx_guest_create(rsrc, 0, &guest2, &vmar2), ZX_OK, "");
-    ASSERT_EQ(zx_vcpu_create(guest2, 0, 0, &vcpu2), ZX_OK, "");
-
-    ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt, vcpu1, 0), ZX_OK, "");
-    ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt, vcpu2, 0), ZX_ERR_INVALID_ARGS, "");
-
-    ASSERT_EQ(zx_handle_close(vcpu1), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(vcpu2), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(vmar1), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(vmar2), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(guest1), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(guest2), ZX_OK, "");
-    ASSERT_EQ(zx_handle_close(interrupt), ZX_OK, "");
-
-    END_TEST;
-}
-
-BEGIN_TEST_CASE(interrupt_tests)
-RUN_TEST(interrupt_test)
-RUN_TEST(interrupt_port_bound_test)
-RUN_TEST(interrupt_port_non_bindable_test)
-RUN_TEST(interrupt_suspend_test)
-RUN_TEST(interrupt_bind_vcpu_test)
-RUN_TEST(interrupt_bind_vcpu_not_supported_test)
-RUN_TEST(interrupt_bind_vcpu_already_bound_test)
-RUN_TEST(interrupt_bind_vcpu_multiple_guests_test)
-END_TEST_CASE(interrupt_tests)
diff --git a/system/utest/core/interrupt/interrupt-test.cpp b/system/utest/core/interrupt/interrupt-test.cpp
new file mode 100644
index 0000000..255dccb
--- /dev/null
+++ b/system/utest/core/interrupt/interrupt-test.cpp
@@ -0,0 +1,294 @@
+// 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/guest.h>
+#include <lib/zx/interrupt.h>
+#include <lib/zx/port.h>
+#include <lib/zx/process.h>
+#include <lib/zx/thread.h>
+#include <lib/zx/vcpu.h>
+#include <unittest/unittest.h>
+
+extern "C" zx_handle_t get_root_resource(void);
+
+static bool get_thread_info(zx_handle_t thread, zx_info_thread_t* info) {
+    return zx_object_get_info(thread, ZX_INFO_THREAD, info, sizeof(*info), NULL, NULL) == ZX_OK;
+}
+
+static bool wait_thread(const zx::thread& thread, uint32_t reason) {
+    while (true) {
+        zx_info_thread_t info;
+        ASSERT_EQ(thread.get_info(ZX_INFO_THREAD, &info, sizeof(info), nullptr, nullptr), ZX_OK);
+        if (info.state == reason) {
+            return true;
+        }
+        zx::nanosleep(zx::deadline_after(zx::msec(1)));
+    }
+}
+
+static void thread_entry(uintptr_t arg1, uintptr_t arg2) {
+    zx_handle_t interrupt = static_cast<zx_handle_t>(arg1);
+    while (zx_interrupt_wait(interrupt, nullptr) == ZX_OK) {}
+}
+
+// Tests to bind interrupt to a non-bindable port
+static bool interrupt_port_non_bindable_test() {
+    BEGIN_TEST;
+
+    zx::unowned_resource resource(get_root_resource());
+    zx::interrupt interrupt;
+    zx::port port;
+    const uint32_t key = 789;
+
+    ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK);
+    ASSERT_EQ(zx::port::create(0, &port), ZX_OK);
+
+    ASSERT_EQ(interrupt.bind(port.get(), key, 0), ZX_ERR_WRONG_TYPE);
+
+    END_TEST;
+}
+
+// Tests Interrupts bound to a port
+static bool interrupt_port_bound_test() {
+    BEGIN_TEST;
+
+    zx::unowned_resource resource(get_root_resource());
+    zx::interrupt interrupt;
+    zx::port port;
+    const zx::time signaled_timestamp_1(12345);
+    const zx::time signaled_timestamp_2(67890);
+    const uint32_t key = 789;
+    zx_port_packet_t out;
+
+    ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK);
+    ASSERT_EQ(zx::port::create(ZX_PORT_BIND_TO_INTERRUPT, &port), ZX_OK);
+
+    // Test port binding
+    ASSERT_EQ(interrupt.bind(port.get(), key, 0), ZX_OK);
+    ASSERT_EQ(interrupt.trigger(0, signaled_timestamp_1), ZX_OK);
+    ASSERT_EQ(port.wait(zx::time::infinite(), &out), ZX_OK);
+    ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1.get());
+
+    // Triggering 2nd time, ACKing it causes port packet to be delivered
+    ASSERT_EQ(interrupt.trigger(0, signaled_timestamp_1), ZX_OK);
+    ASSERT_EQ(interrupt.ack(), ZX_OK);
+    ASSERT_EQ(port.wait(zx::time::infinite(), &out), ZX_OK);
+    ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1.get());
+    ASSERT_EQ(out.key, key);
+    ASSERT_EQ(out.type, ZX_PKT_TYPE_INTERRUPT);
+    ASSERT_EQ(out.status, ZX_OK);
+    ASSERT_EQ(interrupt.ack(), ZX_OK);
+
+    // Triggering it twice
+    // the 2nd timestamp is recorded and upon ACK another packet is queued
+    ASSERT_EQ(interrupt.trigger(0, signaled_timestamp_1), ZX_OK);
+    ASSERT_EQ(interrupt.trigger(0, signaled_timestamp_2), ZX_OK);
+    ASSERT_EQ(port.wait(zx::time::infinite(), &out), ZX_OK);
+    ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_1.get());
+    ASSERT_EQ(interrupt.ack(), ZX_OK);
+    ASSERT_EQ(port.wait(zx::time::infinite(), &out), ZX_OK);
+    ASSERT_EQ(out.interrupt.timestamp, signaled_timestamp_2.get());
+
+    // Try to destroy now, expecting to return error telling packet
+    // has been read but the interrupt has not been re-armed
+    ASSERT_EQ(interrupt.destroy(), ZX_ERR_NOT_FOUND,"");
+    ASSERT_EQ(interrupt.ack(), ZX_ERR_CANCELED);
+    ASSERT_EQ(interrupt.trigger(0, signaled_timestamp_1), ZX_ERR_CANCELED);
+
+    END_TEST;
+}
+
+// Tests support for virtual interrupts
+static bool interrupt_test() {
+    BEGIN_TEST;
+
+    zx::unowned_resource resource(get_root_resource());
+    zx::interrupt interrupt;
+    zx::interrupt interrupt_cancelled;
+    const zx::time signaled_timestamp(12345);
+    zx::time timestamp;
+
+    ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_SLOT_USER, &interrupt),
+              ZX_ERR_INVALID_ARGS);
+    ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK);
+    ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt_cancelled),
+              ZX_OK);
+
+    ASSERT_EQ(interrupt_cancelled.destroy(), ZX_OK);
+    ASSERT_EQ(interrupt_cancelled.trigger(0, signaled_timestamp), ZX_ERR_CANCELED);
+
+    ASSERT_EQ(interrupt.trigger(0, signaled_timestamp), ZX_OK);
+
+    ASSERT_EQ(interrupt_cancelled.wait(&timestamp), ZX_ERR_CANCELED);
+    ASSERT_EQ(interrupt.wait(&timestamp), ZX_OK);
+    ASSERT_EQ(timestamp.get(), signaled_timestamp.get());
+
+    ASSERT_EQ(interrupt.trigger(0, signaled_timestamp), ZX_OK);
+    ASSERT_EQ(interrupt.wait(&timestamp), ZX_OK);
+
+    END_TEST;
+}
+
+// Tests interrupt thread after suspend/resume
+static bool interrupt_suspend_test() {
+    BEGIN_TEST;
+
+    zx::unowned_resource resource(get_root_resource());
+    zx::interrupt interrupt;
+    zx::thread thread;
+    const char name[] = "interrupt_test_thread";
+    // preallocated stack to satisfy the thread we create
+    static uint8_t stack[1024] __ALIGNED(16);
+
+    ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK);
+
+    // Create and start a thread which waits for an IRQ
+    ASSERT_EQ(zx::thread::create(*zx::process::self(), name, sizeof(name), 0, &thread), ZX_OK);
+
+    ASSERT_EQ(thread.start(reinterpret_cast<uintptr_t>(thread_entry),
+                           reinterpret_cast<uintptr_t>(stack) + sizeof(stack),
+                           static_cast<uintptr_t>(interrupt.get()), 0),
+              ZX_OK);
+
+    // Wait till the thread is in blocked state
+    ASSERT_TRUE(wait_thread(thread, ZX_THREAD_STATE_BLOCKED_INTERRUPT));
+
+    // Suspend the thread, wait till it is suspended
+    zx::suspend_token suspend_token;
+    ASSERT_EQ(thread.suspend(&suspend_token), ZX_OK);
+    ASSERT_TRUE(wait_thread(thread, ZX_THREAD_STATE_SUSPENDED));
+
+    // Resume the thread, wait till it is back to being in blocked state
+    suspend_token.reset();
+    ASSERT_TRUE(wait_thread(thread, ZX_THREAD_STATE_BLOCKED_INTERRUPT));
+    thread.kill();
+
+    END_TEST;
+}
+
+// Tests binding an interrupt to multiple VCPUs
+static bool interrupt_bind_vcpu_test() {
+    BEGIN_TEST;
+
+    zx::unowned_resource resource(get_root_resource());
+    zx::interrupt interrupt;
+    zx::guest guest;
+    zx::vmar vmar;
+    zx::vcpu vcpu1;
+    zx::vcpu vcpu2;
+
+    zx_status_t status = zx::guest::create(*resource, 0, &guest, &vmar);
+    if (status == ZX_ERR_NOT_SUPPORTED) {
+        fprintf(stderr, "Guest creation not supported\n");
+        return true;
+    }
+    ASSERT_EQ(status, ZX_OK);
+
+    ASSERT_EQ(zx::interrupt::create(*resource, 0, 0, &interrupt), ZX_OK);
+    ASSERT_EQ(zx::vcpu::create(guest, 0, 0, &vcpu1), ZX_OK);
+    ASSERT_EQ(zx::vcpu::create(guest, 0, 0, &vcpu2), ZX_OK);
+
+    ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt.get(), vcpu1.get(), 0), ZX_OK);
+    ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt.get(), vcpu2.get(), 0), ZX_OK);
+
+    END_TEST;
+}
+
+// Tests binding a virtual interrupt to a VCPU
+static bool interrupt_bind_vcpu_not_supported_test() {
+    BEGIN_TEST;
+
+    zx::unowned_resource resource(get_root_resource());
+    zx::interrupt interrupt;
+    zx::port port;
+    zx::guest guest;
+    zx::vmar vmar;
+    zx::vcpu vcpu;
+
+    zx_status_t status = zx::guest::create(*resource, 0, &guest, &vmar);
+    if (status == ZX_ERR_NOT_SUPPORTED) {
+        fprintf(stderr, "Guest creation not supported\n");
+        return true;
+    }
+    ASSERT_EQ(status, ZX_OK);
+
+    ASSERT_EQ(zx::interrupt::create(*resource, 0, ZX_INTERRUPT_VIRTUAL, &interrupt), ZX_OK);
+    ASSERT_EQ(zx::port::create(ZX_PORT_BIND_TO_INTERRUPT, &port), ZX_OK);
+    ASSERT_EQ(zx::vcpu::create(guest, 0, 0, &vcpu), ZX_OK);
+
+    ASSERT_EQ(interrupt.bind(port.get(), 0, 0), ZX_OK);
+    ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt.get(), vcpu.get(), 0), ZX_ERR_NOT_SUPPORTED);
+
+    END_TEST;
+}
+
+// Tests binding an interrupt to a VCPU, after binding it to a port
+static bool interrupt_bind_vcpu_already_bound_test() {
+    BEGIN_TEST;
+
+    zx::unowned_resource resource(get_root_resource());
+    zx::interrupt interrupt;
+    zx::port port;
+    zx::guest guest;
+    zx::vmar vmar;
+    zx::vcpu vcpu;
+
+    zx_status_t status = zx::guest::create(*resource, 0, &guest, &vmar);
+    if (status == ZX_ERR_NOT_SUPPORTED) {
+        fprintf(stderr, "Guest creation not supported\n");
+        return true;
+    }
+    ASSERT_EQ(status, ZX_OK);
+
+    ASSERT_EQ(zx::interrupt::create(*resource, 0, 0, &interrupt), ZX_OK);
+    ASSERT_EQ(zx::port::create(ZX_PORT_BIND_TO_INTERRUPT, &port), ZX_OK);
+    ASSERT_EQ(zx::vcpu::create(guest, 0, 0, &vcpu), ZX_OK);
+
+    ASSERT_EQ(interrupt.bind(port.get(), 0, 0), ZX_OK);
+    ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt.get(), vcpu.get(), 0), ZX_ERR_ALREADY_BOUND);
+
+    END_TEST;
+}
+
+// Tests binding an interrupt to VCPUs from different guests
+static bool interrupt_bind_vcpu_multiple_guests_test() {
+    BEGIN_TEST;
+
+    zx::unowned_resource resource(get_root_resource());
+    zx::interrupt interrupt;
+    zx::guest guest1;
+    zx::guest guest2;
+    zx::vmar vmar1;
+    zx::vmar vmar2;
+    zx::vcpu vcpu1;
+    zx::vcpu vcpu2;
+
+    zx_status_t status = zx::guest::create(*resource, 0, &guest1, &vmar1);
+    if (status == ZX_ERR_NOT_SUPPORTED) {
+        fprintf(stderr, "Guest creation not supported\n");
+        return true;
+    }
+    ASSERT_EQ(status, ZX_OK);
+
+    ASSERT_EQ(zx::interrupt::create(*resource, 0, 0, &interrupt), ZX_OK);
+    ASSERT_EQ(zx::vcpu::create(guest1, 0, 0, &vcpu1), ZX_OK);
+    ASSERT_EQ(zx::guest::create(*resource, 0, &guest2, &vmar2), ZX_OK);
+    ASSERT_EQ(zx::vcpu::create(guest2, 0, 0, &vcpu2), ZX_OK);
+
+    ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt.get(), vcpu1.get(), 0), ZX_OK);
+    ASSERT_EQ(zx_interrupt_bind_vcpu(interrupt.get(), vcpu2.get(), 0), ZX_ERR_INVALID_ARGS);
+
+    END_TEST;
+}
+
+BEGIN_TEST_CASE(interrupt_tests)
+RUN_TEST(interrupt_test)
+RUN_TEST(interrupt_port_bound_test)
+RUN_TEST(interrupt_port_non_bindable_test)
+RUN_TEST(interrupt_suspend_test)
+RUN_TEST(interrupt_bind_vcpu_test)
+RUN_TEST(interrupt_bind_vcpu_not_supported_test)
+RUN_TEST(interrupt_bind_vcpu_already_bound_test)
+RUN_TEST(interrupt_bind_vcpu_multiple_guests_test)
+END_TEST_CASE(interrupt_tests)