[Fuchsia] Really running cfv2 test

Though it's still failing, the test-script integration has pretty much
finished.

```=== 298 tests passed, 40 failed ===```

Errors are mainly

```
Exhausted heap space, trying to allocate 32 bytes.
../../runtime/vm/object.cc: 2854: error: Out of memory.
```

This change requires https://crrev.com/c/4913284 and
`"fuchsia_sdk_version": "version:15.20231007.2.1",`
in DEPS.

Bug: #38752
Change-Id: I92a387f4289fce7d05d84e483560729301541c1b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/330025
Commit-Queue: Zijie He <zijiehe@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Thomas <athom@google.com>
diff --git a/pkg/test_runner/lib/src/fuchsia.dart b/pkg/test_runner/lib/src/fuchsia.dart
index 192987e..288848c 100644
--- a/pkg/test_runner/lib/src/fuchsia.dart
+++ b/pkg/test_runner/lib/src/fuchsia.dart
@@ -17,7 +17,8 @@
   void stop();
   // Returns a command to execute a set of tests against the running Fuchsia
   // environment.
-  VMCommand getTestCommand(String mode, String arch, List<String> arguments);
+  VMCommand getTestCommand(
+      String buildDir, String mode, String arch, List<String> arguments);
 
   static final FuchsiaEmulator _instance = _create();
 
diff --git a/pkg/test_runner/lib/src/fuchsia_cfv1.dart b/pkg/test_runner/lib/src/fuchsia_cfv1.dart
index c7b2de6..a120e44 100644
--- a/pkg/test_runner/lib/src/fuchsia_cfv1.dart
+++ b/pkg/test_runner/lib/src/fuchsia_cfv1.dart
@@ -74,7 +74,8 @@
   }
 
   @override
-  VMCommand getTestCommand(String mode, String arch, List<String> arguments) {
+  VMCommand getTestCommand(
+      String buildDir, String mode, String arch, List<String> arguments) {
     arguments = arguments
         .map((arg) => arg.replaceAll(Repository.uri.toFilePath(), '/pkg/data/'))
         .toList();
diff --git a/pkg/test_runner/lib/src/fuchsia_cfv2.dart b/pkg/test_runner/lib/src/fuchsia_cfv2.dart
index a4e4e6c..5766416 100644
--- a/pkg/test_runner/lib/src/fuchsia_cfv2.dart
+++ b/pkg/test_runner/lib/src/fuchsia_cfv2.dart
@@ -16,64 +16,136 @@
   static const String ffx = "./third_party/fuchsia/sdk/linux/tools/x64/ffx";
   static const String testScriptRoot =
       "./third_party/fuchsia/test_scripts/test/";
+  static const String withEnv = "./build/fuchsia/with_envs.py";
+  static const String tmpRoot = "/tmp/dart_ffi_test/";
+  static const String cmName = "fuchsia_ffi_test_component.cm";
 
+  final Map<String, String> envs = <String, String>{};
   Process? daemonProc;
   Process? emuProc;
   String? emuName;
+  Process? repoProc;
 
   @override
   Future<void> publishPackage(String buildDir, String mode, String arch) async {
-    housekeeping();
-
+    try {
+      Directory(tmpRoot).deleteSync(recursive: true);
+    } catch (_) {}
+    // The /tmp/ should always be present, recursive creation is not expected.
+    Directory(tmpRoot).createSync();
     assert(daemonProc == null);
-    daemonProc = await runWithOutput("isolate_daemon.py", []);
+    daemonProc = await _run("isolate_daemon.py", []);
+    var isolateDir = await _captureStdout(daemonProc!);
+    print("+ ffx daemon running on $isolateDir should be ready now.");
+    envs["FFX_ISOLATE_DIR"] = isolateDir;
     assert(emuProc == null);
-    emuProc = await run(
-        "start_emulator.py", ["--disable-graphics", "--target-id-only"]);
-    emuName = await emuProc!.stdout.transform(utf8.decoder).first;
+    emuProc = await _run("start_emulator.py", [
+      "--disable-graphics",
+      "--target-id-only",
+      "--device-spec",
+      "virtual_device_large"
+    ]);
+    emuName = await _captureStdout(emuProc!);
     print("+ Targeting emu name $emuName");
+    await _assertRun("test_connection.py", [emuName!]);
+    await _assertRun("publish_package.py", [
+      "--packages",
+      _testPackagePath(buildDir, mode),
+      "--purge-repo",
+      "--repo",
+      _tempDirectoryOf("repo")
+    ]);
+    repoProc = await _run("serve_repo.py", [
+      "run",
+      "--serve-repo",
+      _tempDirectoryOf("repo"),
+      "--repo-name",
+      "dart-ffi-test-repo",
+      "--target-id",
+      emuName!
+    ]);
+    print("+ Fuchsia repo ${await _captureStdout(repoProc!)} is running "
+        "at ${_tempDirectoryOf('repo')}");
+    await _assertRun("pkg_resolve.py", [emuName!, cmName]);
   }
 
   @override
-  void stop() {
-    // isolate_daemon.py should respect the sigterm and gracefully stop the
-    // daemon process.
+  Future<void> stop() async {
+    assert(repoProc != null);
+    repoProc!.kill();
+    await repoProc!.exitCode;
+    assert(emuProc != null);
+    emuProc!.kill();
+    await emuProc!.exitCode;
     assert(daemonProc != null);
     daemonProc!.kill();
-    emuProc!.kill();
-
-    // In case anything goes wrong, ensure everything is cleaned up.
-    housekeeping();
+    await daemonProc!.exitCode;
   }
 
   @override
-  VMCommand getTestCommand(String mode, String arch, List<String> arguments) {
-    return VMCommand("echo", arguments, <String, String>{});
+  VMCommand getTestCommand(
+      String buildDir, String mode, String arch, List<String> arguments) {
+    return VMCommand(
+        withEnv,
+        _runArgs("run_executable_test.py", [
+          "--target-id",
+          emuName!,
+          "--out-dir",
+          _tempDirectoryOf("out"),
+          "--test-name",
+          "fuchsia-pkg://fuchsia.com/${_testPackageName(mode)}#meta/$cmName",
+          "--logs-dir",
+          _tempDirectoryOf("logs"),
+          "--package-deps",
+          _testPackagePath(buildDir, mode),
+          ...arguments
+        ]),
+        envs);
   }
 
-  static void housekeeping() {
-    Process.runSync(ffx, ["emu", "stop", "--all"]);
-    Process.runSync(ffx, ["repository", "server", "stop"]);
-    Process.runSync(ffx, ["daemon", "stop", "-t", "10000"]);
+  static String _testPackageName(String mode) {
+    return "dart_ffi_test_cfv2_$mode";
   }
 
-  // Same as run, but capture the stdout and stderr.
-  static Future<Process> runWithOutput(String script, List<String> args) async {
-    return run(script, args).then((proc) {
-      proc.stdout.transform(utf8.decoder).forEach((x) {
-        print("++ [$script] stdout: $x");
-      });
-      proc.stderr.transform(utf8.decoder).forEach((x) {
-        print("++ [$script] stderr: $x");
-      });
-      return proc;
-    });
+  static String _testPackagePath(String buildDir, String mode) {
+    var farName = _testPackageName(mode);
+    return "$buildDir/gen/$farName/$farName.far";
   }
 
-  // Executes a test script inside of third_party/fuchsia/test_scripts/test/
-  // with the required environment setup and the arguments.
-  static Future<Process> run(String script, List<String> args) async {
-    return Process.start("./build/fuchsia/with_envs.py",
-        [Uri.directory(testScriptRoot).resolve(script).toString(), ...args]);
+  static String _tempDirectoryOf(String name) {
+    return tmpRoot + name;
+  }
+
+  List<String> _runArgs(String script, List<String> args) {
+    return [testScriptRoot + script, ...args];
+  }
+
+  /// Executes a test script inside of third_party/fuchsia/test_scripts/test/
+  /// with the required environment setup and the arguments.
+  Future<Process> _run(String script, List<String> args) async {
+    var newArgs = _runArgs(script, args);
+    print("+ Start $withEnv with $newArgs with environment $envs.");
+    return Process.start(withEnv, newArgs, environment: envs);
+  }
+
+  /// Executes a test script and asserts its return code is 0; see _run and
+  /// _assert.
+  Future<void> _assertRun(String script, List<String> args) async {
+    _assert((await (await _run(script, args)).exitCode) == 0);
+  }
+
+  /// Captures the first line of output in utf8.
+  Future<String> _captureStdout(Process proc) async {
+    // The stderr needs to be fully consumed as well.
+    proc.stderr.transform(utf8.decoder).forEach((x) => stderr.write(x));
+    return (await proc.stdout.transform(utf8.decoder).first).trim();
+  }
+
+  /// Unlike assert keyword, always evaluates the input function and throws
+  /// exception when the evaluated result is false.
+  void _assert(bool condition) {
+    if (!condition) {
+      throw AssertionError();
+    }
   }
 }
diff --git a/pkg/test_runner/lib/src/runtime_configuration.dart b/pkg/test_runner/lib/src/runtime_configuration.dart
index 22e2c24..e364b5a 100644
--- a/pkg/test_runner/lib/src/runtime_configuration.dart
+++ b/pkg/test_runner/lib/src/runtime_configuration.dart
@@ -476,12 +476,15 @@
       arguments.insert(0, '--suppress-core-dump');
     }
     var command = FuchsiaEmulator.instance().getTestCommand(
-        _configuration.mode.name, _configuration.architecture.name, arguments);
+        _configuration.buildDirectory,
+        _configuration.mode.name,
+        _configuration.architecture.name,
+        arguments);
     command.arguments
         .insert(command.arguments.length - 1, '--disable-dart-dev');
-    return [
-      VMCommand(command.executable, command.arguments, environmentOverrides)
-    ];
+    command.environmentOverrides.addAll(environmentOverrides);
+    print("+ About to run command $command to test against fuchsia vm");
+    return [command];
   }
 }