Refactor file URI helpers (#7279)

diff --git a/packages/devtools_app/lib/src/extensions/extension_service.dart b/packages/devtools_app/lib/src/extensions/extension_service.dart
index cc427dd..0e78154 100644
--- a/packages/devtools_app/lib/src/extensions/extension_service.dart
+++ b/packages/devtools_app/lib/src/extensions/extension_service.dart
@@ -192,26 +192,9 @@
   }
 }
 
-// TODO(kenz): consider caching this for the duration of the VM service
-// connection.
 Future<Uri?> _connectedAppRoot() async {
-  final fileUriString = await serviceConnection.rootLibraryForMainIsolate();
-  _log.fine('fetching rootLibraryForMainIsolate: $fileUriString');
-  if (fileUriString == null) return null;
-  return Uri.parse(rootFromFileUriString(fileUriString));
-}
-
-@visibleForTesting
-String rootFromFileUriString(String fileUriString) {
-  // TODO(kenz): for robustness, consider sending the root library uri to the
-  // server and having the server look for the package folder that contains the
-  // `.dart_tool` directory.
-  final directoryRegExp =
-      RegExp(r'\/(lib|bin|integration_test|test|benchmark)\/.+\.dart');
-  final directoryIndex = fileUriString.indexOf(directoryRegExp);
-  if (directoryIndex != -1) {
-    fileUriString = fileUriString.substring(0, directoryIndex);
-  }
-  _log.fine('calculating rootFromFileUriString: $fileUriString');
-  return fileUriString;
+  final packageUriString =
+      await serviceConnection.rootPackageDirectoryForMainIsolate();
+  if (packageUriString == null) return null;
+  return Uri.parse(packageUriString);
 }
diff --git a/packages/devtools_app/lib/src/service/service_manager.dart b/packages/devtools_app/lib/src/service/service_manager.dart
index 73af59f..0acb43a 100644
--- a/packages/devtools_app/lib/src/service/service_manager.dart
+++ b/packages/devtools_app/lib/src/service/service_manager.dart
@@ -17,6 +17,7 @@
 import '../shared/error_badge_manager.dart';
 import '../shared/feature_flags.dart';
 import '../shared/globals.dart';
+import '../shared/primitives/utils.dart';
 import '../shared/title.dart';
 import '../shared/utils.dart';
 import 'service_registrations.dart' as registrations;
@@ -166,6 +167,8 @@
     await serviceManager.isolateManager.init(isolates);
   }
 
+  // TODO(kenz): consider caching this value for the duration of the VM service
+  // connection.
   Future<String?> rootLibraryForMainIsolate() async {
     final mainIsolateRef = await whenValueNonNull(
       serviceManager.isolateManager.mainIsolate,
@@ -182,10 +185,23 @@
     final selectedIsolateRefId = mainIsolateRef.id!;
     await serviceManager.resolvedUriManager
         .fetchFileUris(selectedIsolateRefId, [rootLib]);
-    return serviceManager.resolvedUriManager.lookupFileUri(
+    final fileUriString = serviceManager.resolvedUriManager.lookupFileUri(
       selectedIsolateRefId,
       rootLib,
     );
+    _log.fine('rootLibraryForMainIsolate: $fileUriString');
+    return fileUriString;
+  }
+
+  // TODO(kenz): consider caching this value for the duration of the VM service
+  // connection.
+  Future<String?> rootPackageDirectoryForMainIsolate() async {
+    final fileUriString = await serviceConnection.rootLibraryForMainIsolate();
+    final packageUriString = fileUriString != null
+        ? packageRootFromFileUriString(fileUriString)
+        : null;
+    _log.fine('rootPackageDirectoryForMainIsolate: $packageUriString');
+    return packageUriString;
   }
 
   Future<Response> get adbMemoryInfo async {
diff --git a/packages/devtools_app/lib/src/shared/primitives/utils.dart b/packages/devtools_app/lib/src/shared/primitives/utils.dart
index 1600f62..57d4ea3 100644
--- a/packages/devtools_app/lib/src/shared/primitives/utils.dart
+++ b/packages/devtools_app/lib/src/shared/primitives/utils.dart
@@ -1246,6 +1246,20 @@
 /// the directory separators '/', and returning the last element.
 String? fileNameFromUri(String? uri) => uri?.split('/').last;
 
+String packageRootFromFileUriString(String fileUriString) {
+  // TODO(kenz): for robustness, consider sending the root library uri to the
+  // server and having the server look for the package folder that contains the
+  // `.dart_tool` directory.
+  final directoryRegExp =
+      RegExp(r'\/(lib|bin|integration_test|test|benchmark)\/.+\.dart');
+  final directoryIndex = fileUriString.indexOf(directoryRegExp);
+  if (directoryIndex != -1) {
+    fileUriString = fileUriString.substring(0, directoryIndex);
+  }
+  _log.fine('calculated rootFromFileUriString: $fileUriString');
+  return fileUriString;
+}
+
 /// Calculates subtraction of two maps.
 ///
 /// Result map keys is union of the imput maps' keys.
diff --git a/packages/devtools_app/test/extensions/extension_service_test.dart b/packages/devtools_app/test/extensions/extension_service_test.dart
index 24e38a4..e9fe123 100644
--- a/packages/devtools_app/test/extensions/extension_service_test.dart
+++ b/packages/devtools_app/test/extensions/extension_service_test.dart
@@ -7,74 +7,6 @@
 
 void main() {
   group('$ExtensionService', () {
-    test('rootFromFileUriString', () {
-      // Dart file under 'lib'
-      expect(
-        rootFromFileUriString('file:///Users/me/foo/my_app_root/lib/main.dart'),
-        equals('file:///Users/me/foo/my_app_root'),
-      );
-      expect(
-        rootFromFileUriString(
-          'file:///Users/me/foo/my_app_root/lib/sub/main.dart',
-        ),
-        equals('file:///Users/me/foo/my_app_root'),
-      );
-
-      // Dart file under 'bin'
-      expect(
-        rootFromFileUriString(
-          'file:///Users/me/foo/my_app_root/bin/script.dart',
-        ),
-        equals('file:///Users/me/foo/my_app_root'),
-      );
-      expect(
-        rootFromFileUriString(
-          'file:///Users/me/foo/my_app_root/bin/sub/script.dart',
-        ),
-        equals('file:///Users/me/foo/my_app_root'),
-      );
-
-      // Dart file under 'test'
-      expect(
-        rootFromFileUriString(
-          'file:///Users/me/foo/my_app_root/test/some_test.dart',
-        ),
-        equals('file:///Users/me/foo/my_app_root'),
-      );
-      expect(
-        rootFromFileUriString(
-          'file:///Users/me/foo/my_app_root/test/sub/some_test.dart',
-        ),
-        equals('file:///Users/me/foo/my_app_root'),
-      );
-
-      // Dart file under 'integration_test'
-      expect(
-        rootFromFileUriString(
-          'file:///Users/me/foo/my_app_root/integration_test/some_test.dart',
-        ),
-        equals('file:///Users/me/foo/my_app_root'),
-      );
-      expect(
-        rootFromFileUriString(
-          'file:///Users/me/foo/my_app_root/integration_test/sub/some_test.dart',
-        ),
-        equals('file:///Users/me/foo/my_app_root'),
-      );
-
-      // Dart file under 'benchmark'
-      expect(
-        rootFromFileUriString(
-          'file:///Users/me/foo/my_app_root/benchmark/some_test.dart',
-        ),
-        equals('file:///Users/me/foo/my_app_root'),
-      );
-      expect(
-        rootFromFileUriString(
-          'file:///Users/me/foo/my_app_root/benchmark/sub/some_test.dart',
-        ),
-        equals('file:///Users/me/foo/my_app_root'),
-      );
-    });
+    // TODO(kenz): add tests for the ExtensionService class.
   });
 }
diff --git a/packages/devtools_app/test/primitives/utils_test.dart b/packages/devtools_app/test/primitives/utils_test.dart
index b49e9eb..95ac7ea 100644
--- a/packages/devtools_app/test/primitives/utils_test.dart
+++ b/packages/devtools_app/test/primitives/utils_test.dart
@@ -1479,6 +1479,80 @@
       equals('http://127.0.0.1:61962'),
     );
   });
+
+  group('file uri helpers', () {
+    test('rootFromFileUriString', () {
+      // Dart file under 'lib'
+      expect(
+        packageRootFromFileUriString(
+          'file:///Users/me/foo/my_app_root/lib/main.dart',
+        ),
+        equals('file:///Users/me/foo/my_app_root'),
+      );
+      expect(
+        packageRootFromFileUriString(
+          'file:///Users/me/foo/my_app_root/lib/sub/main.dart',
+        ),
+        equals('file:///Users/me/foo/my_app_root'),
+      );
+
+      // Dart file under 'bin'
+      expect(
+        packageRootFromFileUriString(
+          'file:///Users/me/foo/my_app_root/bin/script.dart',
+        ),
+        equals('file:///Users/me/foo/my_app_root'),
+      );
+      expect(
+        packageRootFromFileUriString(
+          'file:///Users/me/foo/my_app_root/bin/sub/script.dart',
+        ),
+        equals('file:///Users/me/foo/my_app_root'),
+      );
+
+      // Dart file under 'test'
+      expect(
+        packageRootFromFileUriString(
+          'file:///Users/me/foo/my_app_root/test/some_test.dart',
+        ),
+        equals('file:///Users/me/foo/my_app_root'),
+      );
+      expect(
+        packageRootFromFileUriString(
+          'file:///Users/me/foo/my_app_root/test/sub/some_test.dart',
+        ),
+        equals('file:///Users/me/foo/my_app_root'),
+      );
+
+      // Dart file under 'integration_test'
+      expect(
+        packageRootFromFileUriString(
+          'file:///Users/me/foo/my_app_root/integration_test/some_test.dart',
+        ),
+        equals('file:///Users/me/foo/my_app_root'),
+      );
+      expect(
+        packageRootFromFileUriString(
+          'file:///Users/me/foo/my_app_root/integration_test/sub/some_test.dart',
+        ),
+        equals('file:///Users/me/foo/my_app_root'),
+      );
+
+      // Dart file under 'benchmark'
+      expect(
+        packageRootFromFileUriString(
+          'file:///Users/me/foo/my_app_root/benchmark/some_test.dart',
+        ),
+        equals('file:///Users/me/foo/my_app_root'),
+      );
+      expect(
+        packageRootFromFileUriString(
+          'file:///Users/me/foo/my_app_root/benchmark/sub/some_test.dart',
+        ),
+        equals('file:///Users/me/foo/my_app_root'),
+      );
+    });
+  });
 }
 
 class _SubtractionResult {
diff --git a/packages/devtools_app/test/test_infra/goldens/timeline_flame_chart_with_selected_frame.png b/packages/devtools_app/test/test_infra/goldens/timeline_flame_chart_with_selected_frame.png
index cf85116..eb3f36f 100644
--- a/packages/devtools_app/test/test_infra/goldens/timeline_flame_chart_with_selected_frame.png
+++ b/packages/devtools_app/test/test_infra/goldens/timeline_flame_chart_with_selected_frame.png
Binary files differ