[fdf][tests] Create non-bindable test

Write a test that checks the compat shim successfully
handles a NON_BINDABLE parent creating their own local
child device.

Run this test 200 times locally to make sure there's no
flakes.

Multiply: nonbindable-test
Fixed: 98842

Change-Id: I30098e5bbeb32f7b93da6b0417e5af12b875f48c
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/693162
Commit-Queue: David Gilhooley <dgilhooley@google.com>
Reviewed-by: Sarah Chan <spqchan@google.com>
diff --git a/build/drivers/all_drivers_list.txt b/build/drivers/all_drivers_list.txt
index b70a868..9e35041 100644
--- a/build/drivers/all_drivers_list.txt
+++ b/build/drivers/all_drivers_list.txt
@@ -223,6 +223,7 @@
 //src/devices/tests/fidl-protocol:parent-driver
 //src/devices/tests/isolateddevmgr:isolateddevmgr-test-driver-driver
 //src/devices/tests/mock-device:mock-device-driver
+//src/devices/tests/nonbindable:driver
 //src/devices/tests/string-bind-test:child-driver-driver
 //src/devices/tests/string-bind-test:parent-driver-driver
 //src/devices/tests/sysdev:sysdev-driver
@@ -337,4 +338,4 @@
 //src/ui/light/drivers/aml-light:aml-light-driver
 //src/ui/light/drivers/gpio-light:gpio-light-driver
 //tools/create/goldens/my-driver-cpp:driver
-//zircon/third_party/dev/ethernet/e1000:e1000-driver
+//zircon/third_party/dev/ethernet/e1000:e1000-driver
\ No newline at end of file
diff --git a/src/devices/tests/BUILD.gn b/src/devices/tests/BUILD.gn
index 495cdd0..1de9e21 100644
--- a/src/devices/tests/BUILD.gn
+++ b/src/devices/tests/BUILD.gn
@@ -32,6 +32,7 @@
     "fx-logger:tests",
     "isolateddevmgr:tests",
     "libdriver-integration-test",
+    "nonbindable:tests",
     "string-bind-test",
     "v2:tests",
   ]
@@ -70,6 +71,7 @@
     "fidl-protocol:parent",
     "isolateddevmgr:isolateddevmgr-test-driver",
     "mock-device:mock-device",
+    "nonbindable:drivers",
     "string-bind-test:child-driver",
     "string-bind-test:parent-driver",
     "sysdev:sysdev",
diff --git a/src/devices/tests/nonbindable/BUILD.gn b/src/devices/tests/nonbindable/BUILD.gn
new file mode 100644
index 0000000..7b1d5d8
--- /dev/null
+++ b/src/devices/tests/nonbindable/BUILD.gn
@@ -0,0 +1,67 @@
+# Copyright 2022 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.
+
+import("//build/bind/bind.gni")
+import("//build/components.gni")
+import("//build/drivers.gni")
+import("//build/test.gni")
+
+group("tests") {
+  testonly = true
+  deps = [ ":pkg" ]
+}
+
+group("drivers") {
+  testonly = true
+  deps = [ ":component" ]
+}
+
+driver_bind_rules("bind") {
+  rules = "nonbindable.bind"
+  header_output = "nonbindable-bind.h"
+  bind_output = "nonbindable.bindbc"
+  deps = [
+    "//src/devices/bind/fuchsia.pci",
+    "//src/devices/bind/fuchsia.test",
+  ]
+}
+
+fuchsia_driver("driver") {
+  output_name = "test-nonbindable"
+  sources = [ "nonbindable.cc" ]
+  deps = [
+    ":bind",
+    "//src/devices/lib/driver",
+    "//src/lib/ddktl",
+    "//zircon/system/ulib/fbl",
+    "//zircon/system/ulib/zx",
+  ]
+}
+
+fuchsia_driver_component("component") {
+  component_name = "test-nonbindable"
+  deps = [ ":driver" ]
+  info = "component-info.json"
+  colocate = true
+}
+
+test("nonbindable-test") {
+  sources = [ "test.cc" ]
+  deps = [
+    ":component",
+    "//sdk/fidl/fuchsia.driver.test:fuchsia.driver.test_llcpp",
+    "//sdk/fidl/fuchsia.io:fuchsia.io_llcpp",
+    "//sdk/lib/device-watcher/cpp",
+    "//sdk/lib/driver_test_realm",
+    "//src/devices/internal/drivers/fragment",
+    "//src/devices/misc/drivers/test-parent",
+    "//src/lib/fxl/test:gtest_main",
+    "//zircon/system/ulib/service:service-llcpp",
+  ]
+}
+
+fuchsia_unittest_package("pkg") {
+  package_name = "nonbindable-test"
+  deps = [ ":nonbindable-test" ]
+}
diff --git a/src/devices/tests/nonbindable/component-info.json b/src/devices/tests/nonbindable/component-info.json
new file mode 100644
index 0000000..01b7fad
--- /dev/null
+++ b/src/devices/tests/nonbindable/component-info.json
@@ -0,0 +1,10 @@
+{
+    "short_description": "Driver Framework nonbindable test driver",
+    "manufacturer": "",
+    "families": [],
+    "models": [],
+    "areas": [
+        "DriverFramework",
+        "Test"
+    ]
+}
diff --git a/src/devices/tests/nonbindable/nonbindable.bind b/src/devices/tests/nonbindable/nonbindable.bind
new file mode 100644
index 0000000..cf99322
--- /dev/null
+++ b/src/devices/tests/nonbindable/nonbindable.bind
@@ -0,0 +1,7 @@
+// Copyright 2022 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.
+
+using fuchsia.test;
+
+fuchsia.BIND_PROTOCOL == fuchsia.test.BIND_PROTOCOL.PARENT;
diff --git a/src/devices/tests/nonbindable/nonbindable.cc b/src/devices/tests/nonbindable/nonbindable.cc
new file mode 100644
index 0000000..732a6f2
--- /dev/null
+++ b/src/devices/tests/nonbindable/nonbindable.cc
@@ -0,0 +1,61 @@
+// Copyright 2022 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 "src/devices/tests/nonbindable/nonbindable.h"
+
+#include "src/devices/tests/nonbindable/nonbindable-bind.h"
+
+namespace auto_bind {
+
+class Child;
+using ChildDeviceType = ddk::Device<Child>;
+class Child : public ChildDeviceType {
+ public:
+  explicit Child(zx_device_t* parent) : ChildDeviceType(parent) {}
+  virtual ~Child() = default;
+
+  zx_status_t Bind() { return DdkAdd(ddk::DeviceAddArgs("child")); }
+
+  void DdkRelease() { delete this; }
+};
+
+zx_status_t Nonbindable::Bind(void* ctx, zx_device_t* dev) {
+  auto device = std::make_unique<Nonbindable>(dev);
+  zx_status_t status = device->Bind();
+  if (status != ZX_OK) {
+    return status;
+  }
+  __UNUSED auto ptr = device.release();
+  return ZX_OK;
+}
+
+zx_status_t Nonbindable::Bind() {
+  constexpr uint32_t flags = DEVICE_ADD_NON_BINDABLE;
+  return DdkAdd(ddk::DeviceAddArgs("nonbindable").set_flags(flags));
+}
+
+void Nonbindable::DdkInit(ddk::InitTxn txn) {
+  auto child = std::make_unique<Child>(zxdev());
+  zx_status_t status = child->Bind();
+  if (status != ZX_OK) {
+    txn.Reply(status);
+  }
+
+  __UNUSED auto ptr = child.release();
+
+  txn.Reply(ZX_OK);
+}
+
+void Nonbindable::DdkRelease() { delete this; }
+
+static zx_driver_ops_t auto_bind_driver_ops = []() -> zx_driver_ops_t {
+  zx_driver_ops_t ops = {};
+  ops.version = DRIVER_OPS_VERSION;
+  ops.bind = Nonbindable::Bind;
+  return ops;
+}();
+
+}  // namespace auto_bind
+
+ZIRCON_DRIVER(Nonbindable, auto_bind::auto_bind_driver_ops, "zircon", "0.1");
diff --git a/src/devices/tests/nonbindable/nonbindable.h b/src/devices/tests/nonbindable/nonbindable.h
new file mode 100644
index 0000000..762cd99
--- /dev/null
+++ b/src/devices/tests/nonbindable/nonbindable.h
@@ -0,0 +1,27 @@
+// Copyright 2022 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.
+
+#ifndef SRC_DEVICES_TESTS_NONBINDABLE_NONBINDABLE_H_
+#define SRC_DEVICES_TESTS_NONBINDABLE_NONBINDABLE_H_
+
+#include <ddktl/device.h>
+
+namespace auto_bind {
+
+class Nonbindable;
+using DeviceType = ddk::Device<Nonbindable, ddk::Initializable>;
+class Nonbindable : public DeviceType {
+ public:
+  explicit Nonbindable(zx_device_t* parent) : DeviceType(parent) {}
+  virtual ~Nonbindable() = default;
+
+  static zx_status_t Bind(void* ctx, zx_device_t* dev);
+  zx_status_t Bind();
+  void DdkInit(ddk::InitTxn txn);
+  void DdkRelease();
+};
+
+}  // namespace auto_bind
+
+#endif  // SRC_DEVICES_TESTS_NONBINDABLE_NONBINDABLE_H_
diff --git a/src/devices/tests/nonbindable/test.cc b/src/devices/tests/nonbindable/test.cc
new file mode 100644
index 0000000..23a5ba5
--- /dev/null
+++ b/src/devices/tests/nonbindable/test.cc
@@ -0,0 +1,31 @@
+// Copyright 2022 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 <fidl/fuchsia.driver.test/cpp/wire.h>
+#include <fidl/fuchsia.io/cpp/wire.h>
+#include <lib/fdio/directory.h>
+#include <lib/service/llcpp/service.h>
+
+#include <gtest/gtest.h>
+#include <sdk/lib/device-watcher/cpp/device-watcher.h>
+
+TEST(NonbindableTest, DriversExist) {
+  {
+    // Connect to DriverTestRealm.
+    auto client_end = service::Connect<fuchsia_driver_test::Realm>();
+    ASSERT_EQ(client_end.status_value(), ZX_OK);
+    auto client = fidl::BindSyncClient(std::move(*client_end));
+
+    // Start the DriverTestRealm with correct arguments.
+    fidl::Arena arena;
+    auto args = fuchsia_driver_test::wire::RealmArgs::Builder(arena);
+    args.use_driver_framework_v2(true);
+    args.root_driver("fuchsia-boot:///#meta/test-parent-sys.cm");
+    auto wire_result = client->Start(args.Build());
+    ASSERT_EQ(wire_result.status(), ZX_OK);
+  }
+
+  fbl::unique_fd out;
+  ASSERT_EQ(ZX_OK, device_watcher::RecursiveWaitForFile("/dev/sys/test/nonbindable/child", &out));
+}