[inspect][validator] Dart Puppet

This is the Dart Puppet program for the Inspect Validator system. This program
receives FIDL commands from the Validator program and dispatches them to the
Dart Inspect library. The Validator will ensure that the commands are
reflected properly in the Inspect VMO.

Test: README.md

CF-911

Change-Id: Ic9c0e4a92fe97c3cda6f83338d458d7d526dfdf9
diff --git a/public/dart/fuchsia_inspect/lib/src/inspect/inspect.dart b/public/dart/fuchsia_inspect/lib/src/inspect/inspect.dart
index e998d50..85c86f0 100644
--- a/public/dart/fuchsia_inspect/lib/src/inspect/inspect.dart
+++ b/public/dart/fuchsia_inspect/lib/src/inspect/inspect.dart
@@ -9,6 +9,7 @@
 import 'package:fuchsia_services/services.dart';
 import 'package:fuchsia_vfs/vfs.dart';
 import 'package:meta/meta.dart';
+import 'package:zircon/zircon.dart';
 
 import '../vmo/vmo_writer.dart';
 import 'internal/_inspect_impl.dart';
@@ -46,6 +47,12 @@
   /// similarly named inspectors.
   static Map<String, int> nameToInstanceCount;
 
+  /// For use in testing only. There's probably no way to put @visibleForTesting
+  /// because this needs to be used by the Validator Puppet, outside the current
+  /// library.
+  /// @nodoc
+  Handle get vmoHandleForExportTestOnly;
+
   /// Returns a singleton [Inspect] instance at root.inspect
   factory Inspect() {
     if (_singleton == null) {
diff --git a/public/dart/fuchsia_inspect/lib/src/inspect/internal/_inspect_impl.dart b/public/dart/fuchsia_inspect/lib/src/inspect/internal/_inspect_impl.dart
index 92a4e78..e102a66 100644
--- a/public/dart/fuchsia_inspect/lib/src/inspect/internal/_inspect_impl.dart
+++ b/public/dart/fuchsia_inspect/lib/src/inspect/internal/_inspect_impl.dart
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import 'package:fuchsia_vfs/vfs.dart' as vfs;
+import 'package:zircon/zircon.dart';
 
 import '../../vmo/vmo_writer.dart';
 import '../inspect.dart';
@@ -13,14 +14,24 @@
 /// should be used by the [Inspect] factory constructor.
 class InspectImpl implements Inspect {
   Node _root;
+  Vmo _vmo;
 
   /// The default constructor for this instance.
   InspectImpl(vfs.PseudoDir directory, String fileName, VmoWriter writer) {
     directory.addNode(fileName, writer.vmoNode);
 
     _root = RootNode(writer);
+    _vmo = writer.vmo;
   }
 
   @override
   Node get root => _root;
+
+  /// For use in testing only. There's probably no way to put @visibleForTesting
+  /// because this needs to be used by the Validator Puppet, outside the current
+  /// library.
+  /// @nodoc
+  @override
+  Handle get vmoHandleForExportTestOnly =>
+      _vmo.duplicate(ZX.RIGHT_READ | ZX.RIGHTS_BASIC | ZX.RIGHT_MAP).handle;
 }
diff --git a/public/dart/fuchsia_inspect/test/validator_puppet/BUILD.gn b/public/dart/fuchsia_inspect/test/validator_puppet/BUILD.gn
new file mode 100644
index 0000000..8ab12b5
--- /dev/null
+++ b/public/dart/fuchsia_inspect/test/validator_puppet/BUILD.gn
@@ -0,0 +1,63 @@
+#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.
+
+import("//build/test/test_package.gni")
+import("//build/testing/environments.gni")
+import("//topaz/runtime/dart_runner/dart_app.gni")
+
+dart_app("dart_inspect_validator_puppet") {
+  components = [
+    {
+      component_name = "dart_inspect_validator_puppet"
+      component_type = "dart"
+      package_root = "dart_inspect_validator_puppet"
+      main_dart = "lib/main.dart"
+      sources = []
+      deps = [
+        "//sdk/fidl/fuchsia.sys",
+        "//src/diagnostics/inspect_validator/fidl:validate",
+        "//topaz/public/dart/fidl",
+        "//topaz/public/dart/fuchsia_inspect",
+        "//topaz/public/dart/fuchsia_logger",
+        "//topaz/public/dart/fuchsia_services",
+      ]
+    },
+  ]
+
+  meta = [
+    {
+      path = rebase_path("meta/dart_inspect_validator_puppet.cmx")
+      dest = "dart_inspect_validator_puppet.cmx"
+    },
+  ]
+}
+
+test_package("inspect_validator_test_dart") {
+  public_deps = [
+    ":dart_inspect_validator_puppet",
+    "//src/diagnostics/inspect_validator:validator_bin",
+  ]
+
+  meta = [
+    {
+      path = rebase_path("meta/inspect_validator_test_dart.cmx")
+      dest = "inspect_validator_test_dart.cmx"
+    },
+  ]
+
+  tests = [
+    {
+      name = "validator"
+      environments = basic_envs
+    },
+  ]
+}
+
+group("tests") {
+  testonly = true
+  deps = [
+    ":dart_inspect_validator_puppet",
+    ":inspect_validator_test_dart",
+  ]
+}
diff --git a/public/dart/fuchsia_inspect/test/validator_puppet/README.md b/public/dart/fuchsia_inspect/test/validator_puppet/README.md
new file mode 100644
index 0000000..56bed62
--- /dev/null
+++ b/public/dart/fuchsia_inspect/test/validator_puppet/README.md
@@ -0,0 +1,41 @@
+# Inspect Validator Dart Puppet
+
+Reviewed on: 2019-09-19
+
+Inspect Validator exercises Inspect libraries and evaluates
+the resulting VMOs for correctness and (soon) memory efficiency.
+
+To do this, Validator controls "puppet" programs via FIDL, making them
+do the actual VMO manipulations using the Inspect library in the target
+languages.
+
+This directory contains the Dart Puppet program. BUILD.gn's `:tests` target
+invokes the Validator on the Dart Puppet, and is integrated in CQ/CI.
+
+## Building
+
+This project can be added to builds by including
+`--with //topaz/public/dart/fuchsia_inspect/test/validator_puppet:tests`
+to the `fx set` invocation.
+
+For example:
+
+```
+fx set core.x64 --with '//topaz/bundles:buildbot' --with //src/diagnostics/inspect_validator:tests
+```
+
+## Running
+
+```
+fx build && fx run-test inspect_validator_test_dart
+```
+
+## Testing
+
+See Running. Since the Validator puppet integration tests completely
+exercise the code in this Puppet, the Puppet does not include unit tests.
+
+## Source layout
+
+lib/main.rs contains the entry point main() which sets up a FIDL service and
+dispatches initialization commands and actions to the Dart Inspect library.
diff --git a/public/dart/fuchsia_inspect/test/validator_puppet/analysis_options.yaml b/public/dart/fuchsia_inspect/test/validator_puppet/analysis_options.yaml
new file mode 100644
index 0000000..76b5c99
--- /dev/null
+++ b/public/dart/fuchsia_inspect/test/validator_puppet/analysis_options.yaml
@@ -0,0 +1,5 @@
+# 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: ../../../../../tools/analysis_options.yaml
diff --git a/public/dart/fuchsia_inspect/test/validator_puppet/dart_inspect_validator_puppet/analysis_options.yaml b/public/dart/fuchsia_inspect/test/validator_puppet/dart_inspect_validator_puppet/analysis_options.yaml
new file mode 100644
index 0000000..737f3f3
--- /dev/null
+++ b/public/dart/fuchsia_inspect/test/validator_puppet/dart_inspect_validator_puppet/analysis_options.yaml
@@ -0,0 +1,5 @@
+# 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: ../../../../../../tools/analysis_options.yaml
diff --git a/public/dart/fuchsia_inspect/test/validator_puppet/dart_inspect_validator_puppet/pubspec.yaml b/public/dart/fuchsia_inspect/test/validator_puppet/dart_inspect_validator_puppet/pubspec.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/public/dart/fuchsia_inspect/test/validator_puppet/dart_inspect_validator_puppet/pubspec.yaml
diff --git a/public/dart/fuchsia_inspect/test/validator_puppet/lib/main.dart b/public/dart/fuchsia_inspect/test/validator_puppet/lib/main.dart
new file mode 100644
index 0000000..65d092c
--- /dev/null
+++ b/public/dart/fuchsia_inspect/test/validator_puppet/lib/main.dart
@@ -0,0 +1,142 @@
+// 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.
+
+import 'dart:async';
+
+import 'package:fidl/fidl.dart';
+import 'package:fidl_test_inspect_validate/fidl_async.dart' as fidl_validate;
+import 'package:fuchsia_inspect/src/inspect/inspect.dart';
+import 'package:fuchsia_logger/logger.dart';
+import 'package:fuchsia_services/services.dart';
+//import 'package:fuchsia/fuchsia.dart' as fuchsia; // for fuchsia.exit()
+
+class _ValidateImpl extends fidl_validate.Validate {
+  final _binding = fidl_validate.ValidateBinding();
+  Inspect _inspect;
+  final _nodes = <int, Node>{};
+  final _properties = <int, Property>{};
+
+  void bind(InterfaceRequest<fidl_validate.Validate> request) {
+    _binding.bind(this, request);
+  }
+
+  @override
+  Future<fidl_validate.Validate$Initialize$Response> initialize(
+      fidl_validate.InitializationParams params) async {
+    _inspect = Inspect();
+
+    var handle = _inspect.vmoHandleForExportTestOnly;
+    return fidl_validate.Validate$Initialize$Response(
+        handle, fidl_validate.TestResult.ok);
+  }
+
+  @override
+  Future<fidl_validate.TestResult> act(fidl_validate.Action action) async {
+    if (_inspect == null) {
+      return fidl_validate.TestResult.illegal;
+    }
+    switch (action.$tag) {
+      case fidl_validate.ActionTag.createNode:
+        _nodes[action.createNode.id] =
+            lookupNode(action.createNode.parent).child(action.createNode.name);
+        break;
+      case fidl_validate.ActionTag.deleteNode:
+        _nodes.remove(action.deleteNode.id).delete();
+        break;
+      case fidl_validate.ActionTag.createNumericProperty:
+        switch (action.createNumericProperty.value.$tag) {
+          case fidl_validate.NumberTag.intT:
+            final property = lookupNode(action.createNumericProperty.parent)
+                .intProperty(action.createNumericProperty.name)
+                  ..setValue(action.createNumericProperty.value.intT);
+            _properties[action.createNumericProperty.id] = property;
+            break;
+          case fidl_validate.NumberTag.doubleT:
+            final property = lookupNode(action.createNumericProperty.parent)
+                .doubleProperty(action.createNumericProperty.name)
+                  ..setValue(action.createNumericProperty.value.doubleT);
+            _properties[action.createNumericProperty.id] = property;
+            break;
+          default:
+            return fidl_validate.TestResult.unimplemented;
+        }
+        break;
+      case fidl_validate.ActionTag.createStringProperty:
+        final property = lookupNode(action.createStringProperty.parent)
+            .stringProperty(action.createStringProperty.name)
+              ..setValue(action.createStringProperty.value);
+        _properties[action.createStringProperty.id] = property;
+        break;
+      case fidl_validate.ActionTag.deleteProperty:
+        _properties.remove(action.deleteProperty.id).delete();
+        break;
+      case fidl_validate.ActionTag.addNumber:
+        switch (action.addNumber.value.$tag) {
+          case fidl_validate.NumberTag.intT:
+            IntProperty p = _properties[action.addNumber.id];
+            p.add(action.addNumber.value.intT);
+            break;
+          case fidl_validate.NumberTag.doubleT:
+            DoubleProperty p = _properties[action.addNumber.id];
+            p.add(action.addNumber.value.doubleT);
+            break;
+          default:
+            return fidl_validate.TestResult.unimplemented;
+        }
+        break;
+      case fidl_validate.ActionTag.subtractNumber:
+        switch (action.subtractNumber.value.$tag) {
+          case fidl_validate.NumberTag.intT:
+            IntProperty p = _properties[action.subtractNumber.id];
+            p.subtract(action.subtractNumber.value.intT);
+            break;
+          case fidl_validate.NumberTag.doubleT:
+            DoubleProperty p = _properties[action.subtractNumber.id];
+            p.subtract(action.subtractNumber.value.doubleT);
+            break;
+          default:
+            return fidl_validate.TestResult.unimplemented;
+        }
+        break;
+      case fidl_validate.ActionTag.setNumber:
+        switch (action.setNumber.value.$tag) {
+          case fidl_validate.NumberTag.intT:
+            IntProperty p = _properties[action.setNumber.id];
+            p.setValue(action.setNumber.value.intT);
+            break;
+          case fidl_validate.NumberTag.doubleT:
+            DoubleProperty p = _properties[action.setNumber.id];
+            p.setValue(action.setNumber.value.doubleT);
+            break;
+          default:
+            return fidl_validate.TestResult.unimplemented;
+        }
+        break;
+      case fidl_validate.ActionTag.setBytes:
+        BytesProperty p = _properties[action.addNumber.id];
+        p.setValue(action.setBytes.value);
+        break;
+      case fidl_validate.ActionTag.setString:
+        StringProperty p = _properties[action.setString.id];
+        p.setValue(action.setString.value);
+        break;
+      default:
+        return fidl_validate.TestResult.unimplemented;
+    }
+    return fidl_validate.TestResult.ok;
+  }
+
+  Node lookupNode(int id) {
+    return (id == 0) ? _inspect.root : _nodes[id];
+  }
+}
+
+void main(List<String> args) {
+  setupLogger();
+  final context = StartupContext.fromStartupInfo();
+  final validate = _ValidateImpl();
+
+  context.outgoing.addPublicService<fidl_validate.Validate>(
+      validate.bind, fidl_validate.Validate.$serviceName);
+}
diff --git a/public/dart/fuchsia_inspect/test/validator_puppet/meta/dart_inspect_validator_puppet.cmx b/public/dart/fuchsia_inspect/test/validator_puppet/meta/dart_inspect_validator_puppet.cmx
new file mode 100644
index 0000000..cf22a0c
--- /dev/null
+++ b/public/dart/fuchsia_inspect/test/validator_puppet/meta/dart_inspect_validator_puppet.cmx
@@ -0,0 +1,11 @@
+{
+    "program": {
+        "binary": "bin/dart_inspect_validator_puppet"
+    },
+    "sandbox": {
+        "services": [
+            "fuchsia.sys.Environment",
+            "fuchsia.logger.LogSink"
+        ]
+    }
+}
diff --git a/public/dart/fuchsia_inspect/test/validator_puppet/meta/inspect_validator_test_dart.cmx b/public/dart/fuchsia_inspect/test/validator_puppet/meta/inspect_validator_test_dart.cmx
new file mode 100644
index 0000000..863d435
--- /dev/null
+++ b/public/dart/fuchsia_inspect/test/validator_puppet/meta/inspect_validator_test_dart.cmx
@@ -0,0 +1,13 @@
+{
+    "program": {
+        "data": "bin/dart_inspect_validator_puppet"
+    },
+    "sandbox": {
+        "services": [
+            "fuchsia.sys.Environment",
+            "fuchsia.sys.Launcher",
+            "fuchsia.sys.Loader",
+            "fuchsia.logger.LogSink"
+        ]
+    }
+}
diff --git a/public/dart/fuchsia_inspect/test/validator_puppet/meta/validator.cmx b/public/dart/fuchsia_inspect/test/validator_puppet/meta/validator.cmx
new file mode 100644
index 0000000..a226812
--- /dev/null
+++ b/public/dart/fuchsia_inspect/test/validator_puppet/meta/validator.cmx
@@ -0,0 +1,19 @@
+{
+    "program": {
+        "args": [
+            "--url",
+            "fuchsia-pkg://fuchsia.com/dart_inspect_validator_puppet#meta/dart_inspect_validator_puppet.cmx",
+            "--output",
+            "text"
+        ],
+        "binary": "test/validator"
+    },
+    "sandbox": {
+        "services": [
+            "fuchsia.sys.Environment",
+            "fuchsia.sys.Launcher",
+            "fuchsia.sys.Loader",
+            "fuchsia.logger.LogSink"
+        ]
+    }
+}
diff --git a/public/dart/fuchsia_inspect/test/validator_puppet/pubspec.yaml b/public/dart/fuchsia_inspect/test/validator_puppet/pubspec.yaml
new file mode 100644
index 0000000..64e8f21
--- /dev/null
+++ b/public/dart/fuchsia_inspect/test/validator_puppet/pubspec.yaml
@@ -0,0 +1,3 @@
+# 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.