Version 2.18.0-99.0.dev

Merge commit '98e565a7f7693ec14687a7ebe1dc45f1ef1090d0' into 'dev'
diff --git a/pkg/dds/test/dap/integration/debug_breakpoints_test.dart b/pkg/dds/test/dap/integration/debug_breakpoints_test.dart
index 03475cb..ecfb94b 100644
--- a/pkg/dds/test/dap/integration/debug_breakpoints_test.dart
+++ b/pkg/dds/test/dap/integration/debug_breakpoints_test.dart
@@ -467,8 +467,8 @@
     test('print messages with Dart interpolation', () async {
       await _testLogPoint(
         dap,
-        r'This is a test message in ${DateTime.now().year}',
-        'This is a test message in ${DateTime.now().year}',
+        r'This is a test message in ${DateTime(2000, 1, 1).year}',
+        'This is a test message in ${DateTime(2000, 1, 1).year}',
       );
     });
 
@@ -478,8 +478,8 @@
         // The DAP spec says "Expressions within {} are interpolated" so in the DA
         // we just prefix them with $ and treat them like other Dart interpolation
         // expressions.
-        r'This is a test message in {DateTime.now().year}',
-        'This is a test message in ${DateTime.now().year}',
+        r'This is a test message in {DateTime(2000, 1, 1).year}',
+        'This is a test message in ${DateTime(2000, 1, 1).year}',
       );
     });
 
diff --git a/pkg/dds/test/devtools_server/devtools_server_path_strategy_dds_test.dart b/pkg/dds/test/devtools_server/devtools_server_path_strategy_dds_test.dart
index 78baa0c..81046da 100644
--- a/pkg/dds/test/devtools_server/devtools_server_path_strategy_dds_test.dart
+++ b/pkg/dds/test/devtools_server/devtools_server_path_strategy_dds_test.dart
@@ -71,7 +71,5 @@
     } finally {
       proc.kill();
     }
-    // TODO(dantup): Unskip this test once DevTools has rolled into
-    //   the SDK so that contains the (newly-added) base href tag.
-  }, timeout: const Timeout.factor(10), skip: true);
+  }, timeout: const Timeout.factor(10));
 }
diff --git a/pkg/dds/test/devtools_server/devtools_server_path_strategy_test.dart b/pkg/dds/test/devtools_server/devtools_server_path_strategy_test.dart
index 9185c15..021b311 100644
--- a/pkg/dds/test/devtools_server/devtools_server_path_strategy_test.dart
+++ b/pkg/dds/test/devtools_server/devtools_server_path_strategy_test.dart
@@ -44,9 +44,7 @@
       httpClient.close();
       server.kill();
     }
-    // TODO(dantup): Unskip this test once DevTools has rolled into
-    //   the SDK so that contains the (newly-added) base href tag.
-  }, timeout: const Timeout.factor(10), skip: true);
+  }, timeout: const Timeout.factor(10));
 
   test('serves 404 contents for requests that are not pages', () async {
     final server = await DevToolsServerDriver.create();
diff --git a/pkg/front_end/test/fasta/messages_suite.dart b/pkg/front_end/test/fasta/messages_suite.dart
index 6b0ae9a..3a90827 100644
--- a/pkg/front_end/test/fasta/messages_suite.dart
+++ b/pkg/front_end/test/fasta/messages_suite.dart
@@ -19,7 +19,7 @@
 import "package:kernel/target/targets.dart" show TargetFlags;
 
 import "package:testing/testing.dart"
-    show Chain, ChainContext, Expectation, Result, Step, TestDescription, runMe;
+    show Chain, ChainContext, Expectation, Result, Step, TestDescription;
 
 import "package:vm/target/vm.dart" show VmTarget;
 
@@ -53,6 +53,8 @@
 
 import '../spell_checking_utils.dart' as spell;
 
+import 'suite_utils.dart' show internalMain;
+
 class MessageTestDescription extends TestDescription {
   @override
   final Uri uri;
@@ -893,5 +895,6 @@
   }
 }
 
-void main([List<String> arguments = const []]) =>
-    runMe(arguments, createContext, configurationPath: "../../testing.json");
+Future<void> main(List<String> arguments) async {
+  await internalMain(createContext, arguments: arguments);
+}
diff --git a/pkg/front_end/test/fasta/modular_suite.dart b/pkg/front_end/test/fasta/modular_suite.dart
index 8d7441e..a2a9684 100644
--- a/pkg/front_end/test/fasta/modular_suite.dart
+++ b/pkg/front_end/test/fasta/modular_suite.dart
@@ -2,8 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE.md file.
 
-library fasta.test.weak_test;
+library fasta.test.modular_suite;
 
+import 'suite_utils.dart' show internalMain;
 import 'testing/suite.dart';
 
 Future<FastaContext> createContext(
@@ -12,11 +13,6 @@
   return FastaContext.create(suite, environment);
 }
 
-void main(List<String> arguments) {
-  internalMain(arguments: arguments);
+Future<void> main(List<String> arguments) async {
+  await internalMain(createContext, arguments: arguments);
 }
-
-void internalMain(
-        {List<String> arguments = const [], int shards = 1, int shard = 0}) =>
-    runMe(arguments, createContext,
-        configurationPath: "../../testing.json", shard: shard, shards: shards);
diff --git a/pkg/front_end/test/fasta/outline_suite.dart b/pkg/front_end/test/fasta/outline_suite.dart
index b8cf207..80a743c 100644
--- a/pkg/front_end/test/fasta/outline_suite.dart
+++ b/pkg/front_end/test/fasta/outline_suite.dart
@@ -2,8 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE.md file.
 
-library fasta.test.outline_test;
+library fasta.test.outline_suite;
 
+import 'suite_utils.dart' show internalMain;
 import 'testing/suite.dart';
 
 Future<FastaContext> createContext(
@@ -11,5 +12,6 @@
   return FastaContext.create(suite, environment);
 }
 
-void main([List<String> arguments = const []]) =>
-    runMe(arguments, createContext, configurationPath: "../../testing.json");
+Future<void> main(List<String> arguments) async {
+  await internalMain(createContext, arguments: arguments);
+}
diff --git a/pkg/front_end/test/fasta/strong_suite.dart b/pkg/front_end/test/fasta/strong_suite.dart
index 8bb8640..3f57d8e 100644
--- a/pkg/front_end/test/fasta/strong_suite.dart
+++ b/pkg/front_end/test/fasta/strong_suite.dart
@@ -2,8 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE.md file.
 
-library fasta.test.strong_test;
+library fasta.test.strong_suite;
 
+import 'suite_utils.dart' show internalMain;
 import 'testing/suite.dart';
 
 Future<FastaContext> createContext(
@@ -13,12 +14,6 @@
   return FastaContext.create(suite, environment);
 }
 
-void main(List<String> arguments) {
-  internalMain(arguments: arguments);
-}
-
-void internalMain(
-    {List<String> arguments = const [], int shards = 1, int shard = 0}) {
-  runMe(arguments, createContext,
-      configurationPath: "../../testing.json", shards: shards, shard: shard);
+Future<void> main(List<String> arguments) async {
+  await internalMain(createContext, arguments: arguments);
 }
diff --git a/pkg/front_end/test/fasta/suite_utils.dart b/pkg/front_end/test/fasta/suite_utils.dart
new file mode 100644
index 0000000..4b4964f
--- /dev/null
+++ b/pkg/front_end/test/fasta/suite_utils.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2022, 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.md file.
+
+library fasta.test.suite_utils;
+
+import 'package:testing/testing.dart' show Step, TestDescription;
+import 'package:testing/src/chain.dart' show CreateContext;
+import 'package:testing/src/log.dart' show Logger, StdoutLogger;
+import 'package:testing/src/suite.dart' as testing show Suite;
+
+import 'testing/suite.dart';
+
+Future<void> internalMain(CreateContext createContext,
+    {List<String> arguments = const [], int shards = 1, int shard = 0}) async {
+  Logger logger = const StdoutLogger();
+  if (arguments.contains("--traceStepTiming")) {
+    logger = new TracingLogger();
+    arguments = arguments.toList()..remove("--traceStepTiming");
+  }
+  await runMe(
+    arguments,
+    createContext,
+    configurationPath: "../../testing.json",
+    shards: shards,
+    shard: shard,
+    logger: logger,
+  );
+}
+
+class TracingLogger extends StdoutLogger {
+  Map<TestDescription, Map<Step, Stopwatch>> stopwatches = {};
+
+  @override
+  void logStepStart(int completed, int failed, int total, testing.Suite? suite,
+      TestDescription description, Step step) {
+    Map<Step, Stopwatch> map = stopwatches[description] ??= {};
+    Stopwatch stopwatch = map[step] ??= new Stopwatch();
+    if (stopwatch.isRunning) throw "unexpectedly already running @ $step";
+    stopwatch.start();
+    super.logStepStart(completed, failed, total, suite, description, step);
+  }
+
+  @override
+  void logStepComplete(int completed, int failed, int total,
+      testing.Suite? suite, TestDescription description, Step step) {
+    Map<Step, Stopwatch> map = stopwatches[description]!;
+    Stopwatch stopwatch = map[step] = map[step]!;
+    if (!stopwatch.isRunning) throw "unexpectedly not running";
+    stopwatch.stop();
+    super.logStepComplete(completed, failed, total, suite, description, step);
+  }
+
+  @override
+  void logSuiteComplete(testing.Suite suite) {
+    Map<String, Duration> totalRuntimesForSteps = {};
+    // Make sure not to overwrite existing text about number of tests run,
+    // failures etc.
+    print("");
+    print("");
+
+    for (MapEntry<TestDescription, Map<Step, Stopwatch>> entryMap
+        in stopwatches.entries) {
+      for (MapEntry<Step, Stopwatch> entry in entryMap.value.entries) {
+        if (entry.value.isRunning) throw "unexpectedly already running";
+        entry.value.elapsed + entry.value.elapsed;
+        totalRuntimesForSteps[entry.key.name] =
+            (totalRuntimesForSteps[entry.key.name] ?? new Duration()) +
+                entry.value.elapsed;
+      }
+    }
+    List<MapEntry<String, Duration>> entries =
+        totalRuntimesForSteps.entries.toList();
+    entries.sort((a, b) => a.value.compareTo(b.value));
+    for (MapEntry<String, Duration> entry in entries) {
+      const int maxLength = 25;
+      String key = (entry.key.length > maxLength)
+          ? entry.key.substring(0, maxLength)
+          : entry.key.padLeft(maxLength);
+      print("$key: ${entry.value} ms");
+    }
+    super.logSuiteComplete(suite);
+  }
+}
diff --git a/pkg/front_end/test/fasta/text_serialization_suite.dart b/pkg/front_end/test/fasta/text_serialization_suite.dart
index eda34ee..caf682e 100644
--- a/pkg/front_end/test/fasta/text_serialization_suite.dart
+++ b/pkg/front_end/test/fasta/text_serialization_suite.dart
@@ -2,8 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE.md file.
 
-library fasta.test.text_serialization_test;
+library fasta.test.text_serialization_suite;
 
+import 'suite_utils.dart' show internalMain;
 import 'testing/suite.dart';
 
 Future<FastaContext> createContext(
@@ -13,12 +14,6 @@
   return FastaContext.create(suite, environment);
 }
 
-void main(List<String> arguments) {
-  internalMain(arguments: arguments);
-}
-
-void internalMain(
-    {List<String> arguments = const [], int shards = 1, int shard = 0}) {
-  runMe(arguments, createContext,
-      configurationPath: "../../testing.json", shards: shards, shard: shard);
+Future<void> main(List<String> arguments) async {
+  await internalMain(createContext, arguments: arguments);
 }
diff --git a/pkg/front_end/test/fasta/weak_suite.dart b/pkg/front_end/test/fasta/weak_suite.dart
index 52fd4d8..20cb0c5 100644
--- a/pkg/front_end/test/fasta/weak_suite.dart
+++ b/pkg/front_end/test/fasta/weak_suite.dart
@@ -2,8 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE.md file.
 
-library fasta.test.weak_test;
+library fasta.test.weak_suite;
 
+import 'suite_utils.dart' show internalMain;
 import 'testing/suite.dart';
 
 Future<FastaContext> createContext(
@@ -12,11 +13,6 @@
   return FastaContext.create(suite, environment);
 }
 
-void main(List<String> arguments) {
-  internalMain(arguments: arguments);
+Future<void> main(List<String> arguments) async {
+  await internalMain(createContext, arguments: arguments);
 }
-
-void internalMain(
-        {List<String> arguments = const [], int shards = 1, int shard = 0}) =>
-    runMe(arguments, createContext,
-        configurationPath: "../../testing.json", shard: shard, shards: shards);
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 7bfe8f0..83bb81c 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -607,6 +607,7 @@
 stats
 stay
 std
+stopwatches
 stress
 stringy
 strip
diff --git a/pkg/test_runner/lib/src/configuration.dart b/pkg/test_runner/lib/src/configuration.dart
index cf71c99..92cf4a9 100644
--- a/pkg/test_runner/lib/src/configuration.dart
+++ b/pkg/test_runner/lib/src/configuration.dart
@@ -172,7 +172,8 @@
 
   String get packages {
     // If the .packages file path wasn't given, find it.
-    _packages ??= Repository.uri.resolve('.packages').toFilePath();
+    _packages ??=
+        Repository.uri.resolve('.dart_tool/package_config.json').toFilePath();
 
     return _packages;
   }
@@ -304,7 +305,7 @@
     const locations = {
       Runtime.firefox: {
         System.win: 'C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe',
-        System.linux: 'firefox',
+        System.linux: '/usr/bin/firefox',
         System.mac: '/Applications/Firefox.app/Contents/MacOS/firefox'
       },
       Runtime.chrome: {
@@ -312,7 +313,7 @@
             'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
         System.mac:
             '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
-        System.linux: 'google-chrome'
+        System.linux: '/usr/bin/google-chrome'
       },
       Runtime.ie9: {
         System.win: 'C:\\Program Files\\Internet Explorer\\iexplore.exe'
diff --git a/pkg/test_runner/lib/src/test_suite.dart b/pkg/test_runner/lib/src/test_suite.dart
index faf6d57..818acd7 100644
--- a/pkg/test_runner/lib/src/test_suite.dart
+++ b/pkg/test_runner/lib/src/test_suite.dart
@@ -925,11 +925,7 @@
             _createUrlPathFromFile(Path('$compilationTempDir/$nameNoExt.js'));
         content = dart2jsHtml(testFile.path.toNativePath(), scriptPath);
       } else {
-        var packageRoot = packagesArgument(configuration.packages);
-        packageRoot =
-            packageRoot == null ? nameNoExt : packageRoot.split("=").last;
-        var nameFromModuleRoot =
-            testFile.path.relativeTo(Path(packageRoot).directoryPath);
+        var nameFromModuleRoot = testFile.path.relativeTo(Repository.dir);
         var nameFromModuleRootNoExt =
             "${nameFromModuleRoot.directoryPath}/$nameNoExt";
         var jsDir =
diff --git a/pkg/test_runner/lib/src/testing_servers.dart b/pkg/test_runner/lib/src/testing_servers.dart
index ac04529..0a6814b 100644
--- a/pkg/test_runner/lib/src/testing_servers.dart
+++ b/pkg/test_runner/lib/src/testing_servers.dart
@@ -91,7 +91,7 @@
         ? Repository.uri
         : Uri.base.resolveUri(Uri.directory(dartDirectory));
     var packagesUri = packages == null
-        ? dartDirectoryUri.resolve('.packages')
+        ? dartDirectoryUri.resolve('.dart_tool/package_config.json')
         : Uri.file(packages);
     return TestingServers._(useContentSecurityPolicy, buildDirectoryUri,
         dartDirectoryUri, packagesUri, runtime);
diff --git a/tools/VERSION b/tools/VERSION
index 57e8878..fc33a00 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 98
+PRERELEASE 99
 PRERELEASE_PATCH 0
\ No newline at end of file