[test] Add mock-hidbus-ifc library

Test: I3387e885bdd77bd0185a2e16b01b72566b47d7cb

Change-Id: I9bb6da7086ebca9687c504dc20c9702efb2d9f76
diff --git a/system/dev/lib/mock-hidbus-ifc/include/lib/mock-hidbus-ifc/mock-hidbus-ifc.h b/system/dev/lib/mock-hidbus-ifc/include/lib/mock-hidbus-ifc/mock-hidbus-ifc.h
new file mode 100644
index 0000000..805e0bf
--- /dev/null
+++ b/system/dev/lib/mock-hidbus-ifc/include/lib/mock-hidbus-ifc/mock-hidbus-ifc.h
@@ -0,0 +1,88 @@
+// Copyright 2019 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.
+
+#pragma once
+
+#include <ddktl/protocol/hidbus.h>
+#include <fbl/auto_lock.h>
+#include <fbl/mutex.h>
+#include <fbl/vector.h>
+#include <lib/sync/completion.h>
+#include <unittest/unittest.h>
+#include <zircon/thread_annotations.h>
+
+namespace mock_hidbus_ifc {
+
+// This class provides a mock a hidbus_ifc_t that can be passed to the HidbusStart method of HID
+// drivers. The template parameter is used to interpret and save reports. See the following example
+// test:
+//
+// mock_hidbus_ifc::MockHidbusIfc<some_report_struct_t> mock_ifc;
+// SomeDriver dut;
+//
+// ASSERT_EQ(ZX_OK, dut.HidbusStart(mock_ifc.proto()));
+// ASSERT_EQ(ZX_OK, mock_ifc.WaitForReports(5));
+//
+// for (const some_report_struct_t& report : mock_ifc.Reports()) {
+//     // Do something
+// }
+
+template <typename T>
+class MockHidbusIfc : public ddk::HidbusIfc<MockHidbusIfc<T>> {
+public:
+    MockHidbusIfc() : ifc_{&this->hidbus_ifc_ops_, this} {}
+
+    const hidbus_ifc_t* proto() const { return &ifc_; }
+
+    // Waits for count reports to be received by IoQueue.
+    zx_status_t WaitForReports(size_t count) {
+        for (;;) {
+            {
+                fbl::AutoLock lock(&reports_lock_);
+                if (reports_.size() == count) {
+                    break;
+                }
+            }
+
+            zx_status_t status = sync_completion_wait(&signal_, ZX_TIME_INFINITE);
+            sync_completion_reset(&signal_);
+
+            if (status != ZX_OK) {
+                return status;
+            }
+        }
+
+        return ZX_OK;
+    }
+
+    // Returns a vector containing the received reports.
+    const fbl::Vector<T>& reports() { return reports_; }
+
+    void HidbusIfcIoQueue(const void* buffer, size_t buf_size) {
+        HidbusIfcIoQueueHelper(buffer, buf_size);
+    }
+
+private:
+    bool HidbusIfcIoQueueHelper(const void* buffer, size_t buf_size) {
+        BEGIN_HELPER;
+
+        ASSERT_EQ(sizeof(T), buf_size);
+
+        {
+            fbl::AutoLock lock(&reports_lock_);
+            reports_.push_back(*reinterpret_cast<const T*>(buffer));
+        }
+
+        sync_completion_signal(&signal_);
+
+        END_HELPER;
+    }
+
+    const hidbus_ifc_t ifc_;
+    fbl::Vector<T> reports_ TA_GUARDED(reports_lock_);
+    sync_completion_t signal_;
+    fbl::Mutex reports_lock_;
+};
+
+}  // namespace mock_hidbus_ifc
diff --git a/system/dev/lib/mock-hidbus-ifc/rules.mk b/system/dev/lib/mock-hidbus-ifc/rules.mk
new file mode 100644
index 0000000..ee69689
--- /dev/null
+++ b/system/dev/lib/mock-hidbus-ifc/rules.mk
@@ -0,0 +1,21 @@
+# Copyright 2019 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.
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_TYPE := userlib
+
+MODULE_STATIC_LIBS := \
+    system/ulib/ddk \
+    system/ulib/ddktl \
+    system/ulib/fbl \
+    system/ulib/sync \
+    system/ulib/unittest \
+
+MODULE_BANJO_LIBS := \
+    system/banjo/ddk-protocol-hidbus \
+
+include make/module.mk