[cfe] Run `fasta testing` directly

This avoids the precompile step when running

package:testing/src/run_tests.dart
Change-Id: Idb7569bb865b077440eb750333cfec5b6c48cd91
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/345280
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
diff --git a/pkg/front_end/test/dartdoctest_suite.dart b/pkg/front_end/test/dartdoctest_suite.dart
index d71cd57..5c34345 100644
--- a/pkg/front_end/test/dartdoctest_suite.dart
+++ b/pkg/front_end/test/dartdoctest_suite.dart
@@ -10,9 +10,8 @@
 void main([List<String> arguments = const []]) =>
     runMe(arguments, createContext, configurationPath: "../testing.json");
 
-Future<Context> createContext(
-    Chain suite, Map<String, String> environment) async {
-  return new Context(suite.name);
+Future<Context> createContext(Chain suite, Map<String, String> environment) {
+  return new Future.value(new Context(suite.name));
 }
 
 class Context extends ChainContext {
diff --git a/pkg/front_end/test/fasta/suite_utils.dart b/pkg/front_end/test/fasta/suite_utils.dart
index 790c76c..8f76f1d 100644
--- a/pkg/front_end/test/fasta/suite_utils.dart
+++ b/pkg/front_end/test/fasta/suite_utils.dart
@@ -12,7 +12,10 @@
 import 'testing/suite.dart';
 
 Future<void> internalMain(CreateContext createContext,
-    {List<String> arguments = const [], int shards = 1, int shard = 0}) async {
+    {List<String> arguments = const [],
+    int shards = 1,
+    int shard = 0,
+    String? configurationPath}) async {
   Logger logger = const StdoutLogger();
   if (arguments.contains("--traceStepTiming")) {
     logger = new TracingLogger();
@@ -21,7 +24,7 @@
   await runMe(
     arguments,
     createContext,
-    configurationPath: "../../testing.json",
+    configurationPath: configurationPath ?? "../../testing.json",
     shards: shards,
     shard: shard,
     logger: logger,
diff --git a/pkg/front_end/test/incremental_bulk_compiler_full.dart b/pkg/front_end/test/incremental_bulk_compiler_full.dart
index 0856796..e348744 100644
--- a/pkg/front_end/test/incremental_bulk_compiler_full.dart
+++ b/pkg/front_end/test/incremental_bulk_compiler_full.dart
@@ -26,9 +26,8 @@
 void main([List<String> arguments = const []]) =>
     runMe(arguments, createContext, configurationPath: "../testing.json");
 
-Future<Context> createContext(
-    Chain suite, Map<String, String> environment) async {
-  return new Context();
+Future<Context> createContext(Chain suite, Map<String, String> environment) {
+  return new Future.value(new Context());
 }
 
 class Context extends ChainContext {
diff --git a/pkg/front_end/test/incremental_bulk_compiler_smoke_suite.dart b/pkg/front_end/test/incremental_bulk_compiler_smoke_suite.dart
index b60f41c..d204053 100644
--- a/pkg/front_end/test/incremental_bulk_compiler_smoke_suite.dart
+++ b/pkg/front_end/test/incremental_bulk_compiler_smoke_suite.dart
@@ -9,7 +9,6 @@
 void main([List<String> arguments = const []]) =>
     runMe(arguments, createContext, configurationPath: "../testing.json");
 
-Future<Context> createContext(
-    Chain suite, Map<String, String> environment) async {
-  return new Context();
+Future<Context> createContext(Chain suite, Map<String, String> environment) {
+  return new Future.value(new Context());
 }
diff --git a/pkg/front_end/test/lint_suite.dart b/pkg/front_end/test/lint_suite.dart
index fd2023f..f04c5d9 100644
--- a/pkg/front_end/test/lint_suite.dart
+++ b/pkg/front_end/test/lint_suite.dart
@@ -36,13 +36,12 @@
 void main([List<String> arguments = const []]) =>
     runMe(arguments, createContext, configurationPath: "../testing.json");
 
-Future<Context> createContext(
-    Chain suite, Map<String, String> environment) async {
+Future<Context> createContext(Chain suite, Map<String, String> environment) {
   const Set<String> knownEnvironmentKeys = {"onlyInGit"};
   checkEnvironment(environment, knownEnvironmentKeys);
 
   bool onlyInGit = environment["onlyInGit"] != "false";
-  return new Context(onlyInGit: onlyInGit);
+  return new Future.value(new Context(onlyInGit: onlyInGit));
 }
 
 class LintTestDescription extends TestDescription {
diff --git a/pkg/front_end/test/outline_extractor_suite.dart b/pkg/front_end/test/outline_extractor_suite.dart
index 9f392bb..ff24b5b 100644
--- a/pkg/front_end/test/outline_extractor_suite.dart
+++ b/pkg/front_end/test/outline_extractor_suite.dart
@@ -43,8 +43,7 @@
 void main([List<String> arguments = const []]) =>
     runMe(arguments, createContext, configurationPath: "../testing.json");
 
-Future<Context> createContext(
-    Chain suite, Map<String, String> environment) async {
+Future<Context> createContext(Chain suite, Map<String, String> environment) {
   const Set<String> knownEnvironmentKeys = {
     UPDATE_EXPECTATIONS,
   };
@@ -52,7 +51,7 @@
 
   bool updateExpectations = environment[UPDATE_EXPECTATIONS] == "true";
 
-  return new Context(suite.name, updateExpectations);
+  return new Future.value(new Context(suite.name, updateExpectations));
 }
 
 class Context extends ChainContext with MatchContext {
diff --git a/pkg/front_end/test/parser_all_suite.dart b/pkg/front_end/test/parser_all_suite.dart
index bef7873..0549446 100644
--- a/pkg/front_end/test/parser_all_suite.dart
+++ b/pkg/front_end/test/parser_all_suite.dart
@@ -10,6 +10,6 @@
     runMe(arguments, createContext, configurationPath: "../testing.json");
 
 Future<ChainContext> createContext(
-    Chain suite, Map<String, String> environment) async {
-  return new ContextChecksOnly(suite.name);
+    Chain suite, Map<String, String> environment) {
+  return new Future.value(new ContextChecksOnly(suite.name));
 }
diff --git a/pkg/front_end/test/parser_equivalence_suite.dart b/pkg/front_end/test/parser_equivalence_suite.dart
index 712e512..829fb67 100644
--- a/pkg/front_end/test/parser_equivalence_suite.dart
+++ b/pkg/front_end/test/parser_equivalence_suite.dart
@@ -20,12 +20,11 @@
       configurationPath: "../testing.json",
     );
 
-Future<Context> createContext(
-    Chain suite, Map<String, String> environment) async {
+Future<Context> createContext(Chain suite, Map<String, String> environment) {
   const Set<String> knownEnvironmentKeys = {};
   checkEnvironment(environment, knownEnvironmentKeys);
 
-  return new Context(suite.name);
+  return new Future.value(new Context(suite.name));
 }
 
 class Context extends ChainContext {
diff --git a/pkg/front_end/test/parser_suite.dart b/pkg/front_end/test/parser_suite.dart
index 84fdd73..3a9e567 100644
--- a/pkg/front_end/test/parser_suite.dart
+++ b/pkg/front_end/test/parser_suite.dart
@@ -59,8 +59,7 @@
 void main([List<String> arguments = const []]) =>
     runMe(arguments, createContext, configurationPath: "../testing.json");
 
-Future<Context> createContext(
-    Chain suite, Map<String, String> environment) async {
+Future<Context> createContext(Chain suite, Map<String, String> environment) {
   const Set<String> knownEnvironmentKeys = {
     UPDATE_EXPECTATIONS,
     "trace",
@@ -72,7 +71,8 @@
   bool trace = environment["trace"] == "true";
   bool annotateLines = environment["annotateLines"] == "true";
 
-  return new Context(suite.name, updateExpectations, trace, annotateLines);
+  return new Future.value(
+      new Context(suite.name, updateExpectations, trace, annotateLines));
 }
 
 ScannerConfiguration scannerConfiguration = new ScannerConfiguration(
diff --git a/pkg/front_end/test/spelling_test_base.dart b/pkg/front_end/test/spelling_test_base.dart
index e6d43dc..40a9714 100644
--- a/pkg/front_end/test/spelling_test_base.dart
+++ b/pkg/front_end/test/spelling_test_base.dart
@@ -85,7 +85,7 @@
 
   @override
   Future<Result<TestDescription>> run(
-      TestDescription description, SpellContext context) async {
+      TestDescription description, SpellContext context) {
     File f = new File.fromUri(description.uri);
     List<int> rawBytes = f.readAsBytesSync();
 
@@ -136,7 +136,7 @@
     while (token != null) {
       if (token is ErrorToken) {
         // For now just accept that.
-        return pass(description);
+        return new Future.value(pass(description));
       }
       if (token.precedingComments != null) {
         Token? comment = token.precedingComments;
@@ -187,9 +187,9 @@
     }
 
     if (errors == null) {
-      return pass(description);
+      return new Future.value(pass(description));
     } else {
-      return fail(description, errors!.join("\n\n"));
+      return new Future.value(fail(description, errors!.join("\n\n")));
     }
   }
 }
diff --git a/pkg/front_end/test/spelling_test_not_src_suite.dart b/pkg/front_end/test/spelling_test_not_src_suite.dart
index 285e8fc..51f10fe 100644
--- a/pkg/front_end/test/spelling_test_not_src_suite.dart
+++ b/pkg/front_end/test/spelling_test_not_src_suite.dart
@@ -14,13 +14,14 @@
     runMe(arguments, createContext, configurationPath: "../testing.json");
 
 Future<SpellContext> createContext(
-    Chain suite, Map<String, String> environment) async {
+    Chain suite, Map<String, String> environment) {
   const Set<String> knownEnvironmentKeys = {"interactive", "onlyInGit"};
   checkEnvironment(environment, knownEnvironmentKeys);
 
   bool interactive = environment["interactive"] == "true";
   bool onlyInGit = environment["onlyInGit"] != "false";
-  return new SpellContextTest(interactive: interactive, onlyInGit: onlyInGit);
+  return new Future.value(
+      new SpellContextTest(interactive: interactive, onlyInGit: onlyInGit));
 }
 
 class SpellContextTest extends SpellContext {
diff --git a/pkg/front_end/test/spelling_test_src_suite.dart b/pkg/front_end/test/spelling_test_src_suite.dart
index 066475a..8d54dfc 100644
--- a/pkg/front_end/test/spelling_test_src_suite.dart
+++ b/pkg/front_end/test/spelling_test_src_suite.dart
@@ -14,13 +14,14 @@
     runMe(arguments, createContext, configurationPath: "../testing.json");
 
 Future<SpellContext> createContext(
-    Chain suite, Map<String, String> environment) async {
+    Chain suite, Map<String, String> environment) {
   const Set<String> knownEnvironmentKeys = {"interactive", "onlyInGit"};
   checkEnvironment(environment, knownEnvironmentKeys);
 
   bool interactive = environment["interactive"] == "true";
   bool onlyInGit = environment["onlyInGit"] != "false";
-  return new SpellContextSource(interactive: interactive, onlyInGit: onlyInGit);
+  return new Future.value(
+      new SpellContextSource(interactive: interactive, onlyInGit: onlyInGit));
 }
 
 class SpellContextSource extends SpellContext {
diff --git a/pkg/front_end/test/unit_test_suites.dart b/pkg/front_end/test/unit_test_suites.dart
index 3928f31..9f35834 100644
--- a/pkg/front_end/test/unit_test_suites.dart
+++ b/pkg/front_end/test/unit_test_suites.dart
@@ -353,77 +353,77 @@
   });
 }
 
-const List<Suite> suites = [
-  const Suite(
+const Map<String, Suite> suites = {
+  'dartdoctest': const Suite(
     "dartdoctest",
     dartdoctest.createContext,
     "../testing.json",
     shardCount: 1,
   ),
-  const Suite(
+  'expression': const Suite(
     "fasta/expression",
     expression.createContext,
     "../../testing.json",
     shardCount: 1,
   ),
-  const Suite(
+  'outline': const Suite(
     "fasta/outline",
     outline.createContext,
     "../../testing.json",
     shardCount: 2,
   ),
-  const Suite(
+  'incremental_dartino': const Suite(
     "fasta/incremental_dartino",
     incremental_dartino.createContext,
     "../../testing.json",
     shardCount: 1,
   ),
-  const Suite(
+  'messages': const Suite(
     "fasta/messages",
     messages.createContext,
     "../../testing.json",
     shardCount: 1,
     requiresGit: true,
   ),
-  const Suite(
+  'strong': const Suite(
     "fasta/strong",
     strong.createContext,
     "../../testing.json",
     path: "fasta/strong_suite.dart",
     shardCount: 2,
   ),
-  const Suite(
+  'incremental_bulk_compiler_smoke': const Suite(
     "incremental_bulk_compiler_smoke",
     incremental_bulk_compiler.createContext,
     "../testing.json",
     shardCount: 1,
   ),
-  const Suite(
+  'incremental': const Suite(
     "incremental",
     incremental.createContext,
     "../testing.json",
     shardCount: 2,
   ),
-  const Suite(
+  'lint': const Suite(
     "lint",
     lint.createContext,
     "../testing.json",
     shardCount: 1,
     requiresGit: true,
   ),
-  const Suite(
+  'parser': const Suite(
     "parser",
     parser.createContext,
     "../testing.json",
     shardCount: 1,
   ),
-  const Suite(
+  'parser_equivalence': const Suite(
     "parser_equivalence",
     parserEquivalence.createContext,
     "../testing.json",
     shardCount: 1,
   ),
-  const Suite(
+  'parser_all': const Suite(
     "parser_all",
     parserAll.createContext,
     "../testing.json",
@@ -433,47 +433,47 @@
          than in test_matrix.json file set */
     ,
   ),
-  const Suite(
+  'spelling_test_not_src': const Suite(
     "spelling_test_not_src",
     spelling_not_src.createContext,
     "../testing.json",
     shardCount: 1,
     requiresGit: true,
   ),
-  const Suite(
+  'spelling_test_src': const Suite(
     "spelling_test_src",
     spelling_src.createContext,
     "../testing.json",
     shardCount: 1,
     requiresGit: true,
   ),
-  const Suite(
+  'modular': const Suite(
     "fasta/modular",
     modular.createContext,
     "../../testing.json",
     path: "fasta/modular_suite.dart",
     shardCount: 4,
   ),
-  const Suite(
+  'weak': const Suite(
     "fasta/weak",
     weak.createContext,
     "../../testing.json",
     path: "fasta/weak_suite.dart",
     shardCount: 10,
   ),
-  const Suite(
+  'textual_outline': const Suite(
     "fasta/textual_outline",
     textual_outline.createContext,
     "../../testing.json",
     shardCount: 1,
   ),
-  const Suite(
+  'outline_extractor': const Suite(
     "outline_extractor",
     outline_extractor.createContext,
     "../testing.json",
     shardCount: 1,
   ),
-];
+};
 
 const Duration timeoutDuration = Duration(minutes: 30);
 
@@ -557,7 +557,7 @@
   int numberOfFreeWorkers = options.numberOfWorkers;
   // Run test suites and record the results and possible failure logs.
   int chunkNum = 0;
-  for (Suite suite in suites) {
+  for (Suite suite in suites.values) {
     if (options.onlyTestsThatRequireGit && !suite.requiresGit) continue;
     if (options.skipTestsThatRequireGit && suite.requiresGit) continue;
     String prefix = suite.prefix;
diff --git a/pkg/front_end/tool/all_suites.dart b/pkg/front_end/tool/all_suites.dart
new file mode 100644
index 0000000..8129cca
--- /dev/null
+++ b/pkg/front_end/tool/all_suites.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2024, the Dart project authors.  Please see the AUTHORS file
+// for details. 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' show Future;
+
+import 'package:testing/src/chain.dart' show CreateContext;
+import 'package:testing/src/run.dart' show runSuites;
+import 'package:testing/src/run_tests.dart' show CommandLine;
+import 'package:testing/src/test_root.dart' show TestRoot;
+import 'package:testing/testing.dart';
+
+import '../test/unit_test_suites.dart';
+
+Future<Null> main(List<String> arguments) async {
+  CommandLine cl = CommandLine.parse(arguments);
+  Map<String, CreateContext> suiteMap = {};
+  for (MapEntry<String, Suite> entry in suites.entries) {
+    suiteMap[entry.key] = entry.value.createContext;
+  }
+  TestRoot testRoot =
+      await TestRoot.fromUri(Uri.base.resolve('pkg/front_end/testing.json'));
+  await runSuites(cl, testRoot, suiteMap);
+}
diff --git a/pkg/front_end/tool/fasta.dart b/pkg/front_end/tool/fasta.dart
index 20ed126..b7e6ee3 100644
--- a/pkg/front_end/tool/fasta.dart
+++ b/pkg/front_end/tool/fasta.dart
@@ -4,7 +4,6 @@
 
 import 'dart:io';
 
-import "package:testing/src/run_tests.dart" as run_tests;
 import 'package:kernel/src/tool/dump.dart' as dump;
 import '../test/utils/io_utils.dart' show computeRepoDir;
 import '_fasta/abcompile.dart' as abcompile;
@@ -17,6 +16,7 @@
 import '_fasta/outline.dart' as outline;
 import '_fasta/parser.dart' as parser;
 import '_fasta/scanner.dart' as scanner;
+import 'all_suites.dart' as all_suites;
 
 final String repoDir = computeRepoDir();
 
@@ -90,8 +90,8 @@
       }
       break;
     case 'testing':
-      mainFunction = run_tests.main;
-      script = '${repoDir}/pkg/testing/bin/testing.dart';
+      mainFunction = all_suites.main;
+      script = '${repoDir}/all_suites.dart';
       scriptArguments.add('--config=${repoDir}/pkg/front_end/testing.json');
       break;
     case 'generate-messages':
diff --git a/pkg/testing/lib/src/run.dart b/pkg/testing/lib/src/run.dart
index ef0301a..1ec262f 100644
--- a/pkg/testing/lib/src/run.dart
+++ b/pkg/testing/lib/src/run.dart
@@ -70,6 +70,27 @@
   }, logger: logger);
 }
 
+Future<void> runSuites(
+    CommandLine cl, TestRoot testRoot, Map<String, CreateContext> suites,
+    {int shards = 1,
+    int shard = 0,
+    Logger logger = const StdoutLogger()}) async {
+  return withErrorHandling(() async {
+    if (cl.verbose) enableVerboseOutput();
+    Set<String> selectedSuites = cl.selectedSuites;
+    for (MapEntry<String, CreateContext> entry in suites.entries) {
+      String suiteName = entry.key;
+      if (selectedSuites.contains(entry.key)) {
+        Chain suite = testRoot.getChain(suiteName)!;
+        CreateContext createContext = entry.value;
+        ChainContext context = await createContext(suite, {...cl.environment});
+        await context.run(suite, Set<String>.from(cl.selectors),
+            shards: shards, shard: shard, logger: logger);
+      }
+    }
+  }, logger: logger);
+}
+
 /// This is called from a `_test.dart` file, and helps integration in other
 /// test runner frameworks.
 ///
diff --git a/pkg/testing/lib/src/test_root.dart b/pkg/testing/lib/src/test_root.dart
index fad874c..c293c82 100644
--- a/pkg/testing/lib/src/test_root.dart
+++ b/pkg/testing/lib/src/test_root.dart
@@ -55,6 +55,15 @@
 
   List<RegExp> get excludedFromAnalysis => analyze.exclude;
 
+  Chain? getChain(String name) {
+    for (Suite suite in suites) {
+      if (suite is Chain && suite.name == name) {
+        return suite;
+      }
+    }
+    return null;
+  }
+
   Iterable<Dart> get dartSuites {
     return List<Dart>.from(suites.whereType<Dart>());
   }