[flutter_plugin_tools] Make unit tests pass on Windows (#4149)

The purpose of this PR is to make running all unit tests on Windows pass (vs failing a large portion of the tests as currently happens). This does not mean that the commands actually work when run on Windows, or that Windows support is tested, only that it's possible to actually run the tests themselves. This is prep for actually supporting parts of the tool on Windows in future PRs.

Major changes:
- Make the tests significantly more hermetic:
  - Make almost all tools take a `Platform` constructor argument that can be used to inject a mock platform to control what OS the command acts like it is running on under test.
  - Add a path `Context` object to the base command, whose style matches the `Platform`, and use that almost everywhere instead of the top-level `path` functions.
  - In cases where Posix behavior is always required (such as parsing `git` output), explicitly use the `posix` context object for `path` functions.
- Start laying the groundwork for actual Windows support:
  - Replace all uses of `flutter` as a command with a getter that returns `flutter` or `flutter.bat` as appropriate.
  - For user messages that include relative paths, use a helper that always uses Posix-style relative paths for consistent output.

This bumps the version since quite a few changes have built up, and having a cut point before starting to make more changes to the commands to support Windows seems like a good idea.

Part of https://github.com/flutter/flutter/issues/86113
diff --git a/script/tool/lib/src/analyze_command.dart b/script/tool/lib/src/analyze_command.dart
index 2c4fc1b..e56b95d 100644
--- a/script/tool/lib/src/analyze_command.dart
+++ b/script/tool/lib/src/analyze_command.dart
@@ -5,7 +5,7 @@
 import 'dart:async';
 
 import 'package:file/file.dart';
-import 'package:path/path.dart' as p;
+import 'package:platform/platform.dart';
 
 import 'common/core.dart';
 import 'common/package_looping_command.dart';
@@ -19,7 +19,8 @@
   AnalyzeCommand(
     Directory packagesDir, {
     ProcessRunner processRunner = const ProcessRunner(),
-  }) : super(packagesDir, processRunner: processRunner) {
+    Platform platform = const LocalPlatform(),
+  }) : super(packagesDir, processRunner: processRunner, platform: platform) {
     argParser.addMultiOption(_customAnalysisFlag,
         help:
             'Directories (comma separated) that are allowed to have their own analysis options.',
@@ -57,9 +58,8 @@
 
       final bool allowed = (getStringListArg(_customAnalysisFlag)).any(
           (String directory) =>
-              directory != null &&
               directory.isNotEmpty &&
-              p.isWithin(
+              path.isWithin(
                   packagesDir.childDirectory(directory).path, file.path));
       if (allowed) {
         continue;
@@ -90,7 +90,7 @@
     });
     for (final Directory package in packageDirectories) {
       final int exitCode = await processRunner.runAndStream(
-          'flutter', <String>['packages', 'get'],
+          flutterCommand, <String>['packages', 'get'],
           workingDir: package);
       if (exitCode != 0) {
         return false;
@@ -109,7 +109,8 @@
 
     // Use the Dart SDK override if one was passed in.
     final String? dartSdk = argResults![_analysisSdk] as String?;
-    _dartBinaryPath = dartSdk == null ? 'dart' : p.join(dartSdk, 'bin', 'dart');
+    _dartBinaryPath =
+        dartSdk == null ? 'dart' : path.join(dartSdk, 'bin', 'dart');
   }
 
   @override
diff --git a/script/tool/lib/src/build_examples_command.dart b/script/tool/lib/src/build_examples_command.dart
index 32905c8..0cac099 100644
--- a/script/tool/lib/src/build_examples_command.dart
+++ b/script/tool/lib/src/build_examples_command.dart
@@ -5,7 +5,7 @@
 import 'dart:async';
 
 import 'package:file/file.dart';
-import 'package:path/path.dart' as p;
+import 'package:platform/platform.dart';
 
 import 'common/core.dart';
 import 'common/package_looping_command.dart';
@@ -23,7 +23,8 @@
   BuildExamplesCommand(
     Directory packagesDir, {
     ProcessRunner processRunner = const ProcessRunner(),
-  }) : super(packagesDir, processRunner: processRunner) {
+    Platform platform = const LocalPlatform(),
+  }) : super(packagesDir, processRunner: processRunner, platform: platform) {
     argParser.addFlag(kPlatformLinux);
     argParser.addFlag(kPlatformMacos);
     argParser.addFlag(kPlatformWeb);
@@ -127,7 +128,7 @@
 
     for (final Directory example in getExamplesForPlugin(package)) {
       final String packageName =
-          p.relative(example.path, from: packagesDir.path);
+          getRelativePosixPath(example, from: packagesDir);
 
       for (final _PlatformDetails platform in buildPlatforms) {
         String buildPlatform = platform.label;
diff --git a/script/tool/lib/src/common/package_looping_command.dart b/script/tool/lib/src/common/package_looping_command.dart
index de1e3b8..9f4039e 100644
--- a/script/tool/lib/src/common/package_looping_command.dart
+++ b/script/tool/lib/src/common/package_looping_command.dart
@@ -8,6 +8,7 @@
 import 'package:file/file.dart';
 import 'package:git/git.dart';
 import 'package:path/path.dart' as p;
+import 'package:platform/platform.dart';
 
 import 'core.dart';
 import 'plugin_command.dart';
@@ -63,8 +64,10 @@
   PackageLoopingCommand(
     Directory packagesDir, {
     ProcessRunner processRunner = const ProcessRunner(),
+    Platform platform = const LocalPlatform(),
     GitDir? gitDir,
-  }) : super(packagesDir, processRunner: processRunner, gitDir: gitDir);
+  }) : super(packagesDir,
+            processRunner: processRunner, platform: platform, gitDir: gitDir);
 
   /// Packages that had at least one [logWarning] call.
   final Set<Directory> _packagesWithWarnings = <Directory>{};
@@ -158,8 +161,8 @@
   /// an exact format (e.g., published name, or basename) is required, that
   /// should be used instead.
   String getPackageDescription(Directory package) {
-    String packageName = p.relative(package.path, from: packagesDir.path);
-    final List<String> components = p.split(packageName);
+    String packageName = getRelativePosixPath(package, from: packagesDir);
+    final List<String> components = p.posix.split(packageName);
     // For the common federated plugin pattern of `foo/foo_subpackage`, drop
     // the first part since it's not useful.
     if (components.length == 2 &&
@@ -169,6 +172,16 @@
     return packageName;
   }
 
+  /// Returns the relative path from [from] to [entity] in Posix style.
+  ///
+  /// This should be used when, for example, printing package-relative paths in
+  /// status or error messages.
+  String getRelativePosixPath(
+    FileSystemEntity entity, {
+    required Directory from,
+  }) =>
+      p.posix.joinAll(path.split(path.relative(entity.path, from: from.path)));
+
   /// The suggested indentation for printed output.
   String get indentation => hasLongOutput ? '' : '  ';
 
diff --git a/script/tool/lib/src/common/plugin_command.dart b/script/tool/lib/src/common/plugin_command.dart
index 74f607d..ecdcb05 100644
--- a/script/tool/lib/src/common/plugin_command.dart
+++ b/script/tool/lib/src/common/plugin_command.dart
@@ -21,6 +21,7 @@
   PluginCommand(
     this.packagesDir, {
     this.processRunner = const ProcessRunner(),
+    this.platform = const LocalPlatform(),
     GitDir? gitDir,
   }) : _gitDir = gitDir {
     argParser.addMultiOption(
@@ -79,6 +80,11 @@
   /// This can be overridden for testing.
   final ProcessRunner processRunner;
 
+  /// The current platform.
+  ///
+  /// This can be overridden for testing.
+  final Platform platform;
+
   /// The git directory to use. If unset, [gitDir] populates it from the
   /// packages directory's enclosing repository.
   ///
@@ -88,9 +94,11 @@
   int? _shardIndex;
   int? _shardCount;
 
+  /// A context that matches the default for [platform].
+  p.Context get path => platform.isWindows ? p.windows : p.posix;
+
   /// The command to use when running `flutter`.
-  String get flutterCommand =>
-      const LocalPlatform().isWindows ? 'flutter.bat' : 'flutter';
+  String get flutterCommand => platform.isWindows ? 'flutter.bat' : 'flutter';
 
   /// The shard of the overall command execution that this instance should run.
   int get shardIndex {
@@ -240,9 +248,9 @@
               // plugins under 'my_plugin'. Also match if the exact plugin is
               // passed.
               final String relativePath =
-                  p.relative(subdir.path, from: dir.path);
-              final String packageName = p.basename(subdir.path);
-              final String basenamePath = p.basename(entity.path);
+                  path.relative(subdir.path, from: dir.path);
+              final String packageName = path.basename(subdir.path);
+              final String basenamePath = path.basename(entity.path);
               if (!excludedPlugins.contains(basenamePath) &&
                   !excludedPlugins.contains(packageName) &&
                   !excludedPlugins.contains(relativePath) &&
diff --git a/script/tool/lib/src/create_all_plugins_app_command.dart b/script/tool/lib/src/create_all_plugins_app_command.dart
index fab41bc..ed70144 100644
--- a/script/tool/lib/src/create_all_plugins_app_command.dart
+++ b/script/tool/lib/src/create_all_plugins_app_command.dart
@@ -5,6 +5,7 @@
 import 'dart:io' as io;
 
 import 'package:file/file.dart';
+import 'package:path/path.dart' as p;
 import 'package:pub_semver/pub_semver.dart';
 import 'package:pubspec_parse/pubspec_parse.dart';
 
@@ -51,7 +52,7 @@
 
   Future<int> _createApp() async {
     final io.ProcessResult result = io.Process.runSync(
-      'flutter',
+      flutterCommand,
       <String>[
         'create',
         '--template=app',
@@ -156,7 +157,7 @@
         <String, PathDependency>{};
 
     await for (final Directory package in getPlugins()) {
-      final String pluginName = package.path.split('/').last;
+      final String pluginName = package.basename;
       final File pubspecFile = package.childFile('pubspec.yaml');
       final Pubspec pubspec = Pubspec.parse(pubspecFile.readAsStringSync());
 
@@ -172,6 +173,7 @@
 ### Generated file. Do not edit. Run `pub global run flutter_plugin_tools gen-pubspec` to update.
 name: ${pubspec.name}
 description: ${pubspec.description}
+publish_to: none
 
 version: ${pubspec.version}
 
@@ -197,7 +199,21 @@
         buffer.write('  ${entry.key}: \n    sdk: ${dep.sdk}');
       } else if (entry.value is PathDependency) {
         final PathDependency dep = entry.value as PathDependency;
-        buffer.write('  ${entry.key}: \n    path: ${dep.path}');
+        String depPath = dep.path;
+        if (path.style == p.Style.windows) {
+          // Posix-style path separators are preferred in pubspec.yaml (and
+          // using a consistent format makes unit testing simpler), so convert.
+          final List<String> components = path.split(depPath);
+          final String firstComponent = components.first;
+          // path.split leaves a \ on drive components that isn't necessary,
+          // and confuses pub, so remove it.
+          if (firstComponent.endsWith(r':\')) {
+            components[0] =
+                firstComponent.substring(0, firstComponent.length - 1);
+          }
+          depPath = p.posix.joinAll(components);
+        }
+        buffer.write('  ${entry.key}: \n    path: $depPath');
       } else {
         throw UnimplementedError(
           'Not available for type: ${entry.value.runtimeType}',
diff --git a/script/tool/lib/src/drive_examples_command.dart b/script/tool/lib/src/drive_examples_command.dart
index df74119..7e800ed 100644
--- a/script/tool/lib/src/drive_examples_command.dart
+++ b/script/tool/lib/src/drive_examples_command.dart
@@ -6,7 +6,7 @@
 import 'dart:io';
 
 import 'package:file/file.dart';
-import 'package:path/path.dart' as p;
+import 'package:platform/platform.dart';
 
 import 'common/core.dart';
 import 'common/package_looping_command.dart';
@@ -22,7 +22,8 @@
   DriveExamplesCommand(
     Directory packagesDir, {
     ProcessRunner processRunner = const ProcessRunner(),
-  }) : super(packagesDir, processRunner: processRunner) {
+    Platform platform = const LocalPlatform(),
+  }) : super(packagesDir, processRunner: processRunner, platform: platform) {
     argParser.addFlag(kPlatformAndroid,
         help: 'Runs the Android implementation of the examples');
     argParser.addFlag(kPlatformIos,
@@ -148,7 +149,7 @@
     for (final Directory example in getExamplesForPlugin(package)) {
       ++examplesFound;
       final String exampleName =
-          p.relative(example.path, from: packagesDir.path);
+          getRelativePosixPath(example, from: packagesDir);
 
       final List<File> drivers = await _getDrivers(example);
       if (drivers.isEmpty) {
@@ -172,11 +173,10 @@
 
         if (testTargets.isEmpty) {
           final String driverRelativePath =
-              p.relative(driver.path, from: package.path);
+              getRelativePosixPath(driver, from: package);
           printError(
               'Found $driverRelativePath, but no integration_test/*_test.dart files.');
-          errors.add(
-              'No test files for ${p.relative(driver.path, from: package.path)}');
+          errors.add('No test files for $driverRelativePath');
           continue;
         }
 
@@ -185,7 +185,7 @@
             example, driver, testTargets,
             deviceFlags: deviceFlags);
         for (final File failingTarget in failingTargets) {
-          errors.add(p.relative(failingTarget.path, from: package.path));
+          errors.add(getRelativePosixPath(failingTarget, from: package));
         }
       }
     }
@@ -296,9 +296,9 @@
             if (enableExperiment.isNotEmpty)
               '--enable-experiment=$enableExperiment',
             '--driver',
-            p.relative(driver.path, from: example.path),
+            getRelativePosixPath(driver, from: example),
             '--target',
-            p.relative(target.path, from: example.path),
+            getRelativePosixPath(target, from: example),
           ],
           workingDir: example);
       if (exitCode != 0) {
diff --git a/script/tool/lib/src/firebase_test_lab_command.dart b/script/tool/lib/src/firebase_test_lab_command.dart
index 8253cee..5e4d9f0 100644
--- a/script/tool/lib/src/firebase_test_lab_command.dart
+++ b/script/tool/lib/src/firebase_test_lab_command.dart
@@ -6,7 +6,7 @@
 import 'dart:io' as io;
 
 import 'package:file/file.dart';
-import 'package:path/path.dart' as p;
+import 'package:platform/platform.dart';
 import 'package:uuid/uuid.dart';
 
 import 'common/core.dart';
@@ -21,7 +21,8 @@
   FirebaseTestLabCommand(
     Directory packagesDir, {
     ProcessRunner processRunner = const ProcessRunner(),
-  }) : super(packagesDir, processRunner: processRunner) {
+    Platform platform = const LocalPlatform(),
+  }) : super(packagesDir, processRunner: processRunner, platform: platform) {
     argParser.addOption(
       'project',
       defaultsTo: 'flutter-infra',
@@ -29,8 +30,9 @@
     );
     final String? homeDir = io.Platform.environment['HOME'];
     argParser.addOption('service-key',
-        defaultsTo:
-            homeDir == null ? null : p.join(homeDir, 'gcloud-service-key.json'),
+        defaultsTo: homeDir == null
+            ? null
+            : path.join(homeDir, 'gcloud-service-key.json'),
         help: 'The path to the service key for gcloud authentication.\n'
             r'If not provided, \$HOME/gcloud-service-key.json will be '
             r'assumed if $HOME is set.');
@@ -150,7 +152,7 @@
     // test file's run.
     int resultsCounter = 0;
     for (final File test in _findIntegrationTestFiles(package)) {
-      final String testName = p.relative(test.path, from: package.path);
+      final String testName = getRelativePosixPath(test, from: package);
       print('Testing $testName...');
       if (!await _runGradle(androidDirectory, 'app:assembleDebug',
           testFile: test)) {
@@ -203,7 +205,7 @@
       print('Running flutter build apk...');
       final String experiment = getStringArg(kEnableExperiment);
       final int exitCode = await processRunner.runAndStream(
-          'flutter',
+          flutterCommand,
           <String>[
             'build',
             'apk',
diff --git a/script/tool/lib/src/format_command.dart b/script/tool/lib/src/format_command.dart
index 9d39d93..7954fd0 100644
--- a/script/tool/lib/src/format_command.dart
+++ b/script/tool/lib/src/format_command.dart
@@ -7,7 +7,7 @@
 
 import 'package:file/file.dart';
 import 'package:http/http.dart' as http;
-import 'package:path/path.dart' as p;
+import 'package:platform/platform.dart';
 import 'package:quiver/iterables.dart';
 
 import 'common/core.dart';
@@ -28,7 +28,8 @@
   FormatCommand(
     Directory packagesDir, {
     ProcessRunner processRunner = const ProcessRunner(),
-  }) : super(packagesDir, processRunner: processRunner) {
+    Platform platform = const LocalPlatform(),
+  }) : super(packagesDir, processRunner: processRunner, platform: platform) {
     argParser.addFlag('fail-on-change', hide: true);
     argParser.addOption('clang-format',
         defaultsTo: 'clang-format',
@@ -156,7 +157,7 @@
       // `flutter format` doesn't require the project to actually be a Flutter
       // project.
       final int exitCode = await processRunner.runAndStream(
-          'flutter', <String>['format', ...dartFiles],
+          flutterCommand, <String>['format', ...dartFiles],
           workingDir: packagesDir);
       if (exitCode != 0) {
         printError('Failed to format Dart files: exit code $exitCode.');
@@ -168,8 +169,12 @@
   Future<Iterable<String>> _getFilteredFilePaths(Stream<File> files) async {
     // Returns a pattern to check for [directories] as a subset of a file path.
     RegExp pathFragmentForDirectories(List<String> directories) {
-      final String s = p.separator;
-      return RegExp('(?:^|$s)${p.joinAll(directories)}$s');
+      String s = path.separator;
+      // Escape the separator for use in the regex.
+      if (s == r'\') {
+        s = r'\\';
+      }
+      return RegExp('(?:^|$s)${path.joinAll(directories)}$s');
     }
 
     return files
@@ -188,12 +193,13 @@
 
   Iterable<String> _getPathsWithExtensions(
       Iterable<String> files, Set<String> extensions) {
-    return files.where((String path) => extensions.contains(p.extension(path)));
+    return files.where(
+        (String filePath) => extensions.contains(path.extension(filePath)));
   }
 
   Future<String> _getGoogleFormatterPath() async {
-    final String javaFormatterPath = p.join(
-        p.dirname(p.fromUri(io.Platform.script)),
+    final String javaFormatterPath = path.join(
+        path.dirname(path.fromUri(platform.script)),
         'google-java-format-1.3-all-deps.jar');
     final File javaFormatterFile =
         packagesDir.fileSystem.file(javaFormatterPath);
diff --git a/script/tool/lib/src/java_test_command.dart b/script/tool/lib/src/java_test_command.dart
index 352197b..b36d110 100644
--- a/script/tool/lib/src/java_test_command.dart
+++ b/script/tool/lib/src/java_test_command.dart
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import 'package:file/file.dart';
-import 'package:path/path.dart' as p;
+import 'package:platform/platform.dart';
 
 import 'common/core.dart';
 import 'common/package_looping_command.dart';
@@ -15,7 +15,8 @@
   JavaTestCommand(
     Directory packagesDir, {
     ProcessRunner processRunner = const ProcessRunner(),
-  }) : super(packagesDir, processRunner: processRunner);
+    Platform platform = const LocalPlatform(),
+  }) : super(packagesDir, processRunner: processRunner, platform: platform);
 
   static const String _gradleWrapper = 'gradlew';
 
@@ -50,7 +51,7 @@
 
     final List<String> errors = <String>[];
     for (final Directory example in examplesWithTests) {
-      final String exampleName = p.relative(example.path, from: package.path);
+      final String exampleName = getRelativePosixPath(example, from: package);
       print('\nRUNNING JAVA TESTS for $exampleName');
 
       final Directory androidDirectory = example.childDirectory('android');
diff --git a/script/tool/lib/src/license_check_command.dart b/script/tool/lib/src/license_check_command.dart
index 1d3e49c..093f814 100644
--- a/script/tool/lib/src/license_check_command.dart
+++ b/script/tool/lib/src/license_check_command.dart
@@ -112,7 +112,7 @@
         !_shouldIgnoreFile(file));
     final Iterable<File> firstPartyLicenseFiles = (await _getAllFiles()).where(
         (File file) =>
-            p.basename(file.basename) == 'LICENSE' && !_isThirdParty(file));
+            path.basename(file.basename) == 'LICENSE' && !_isThirdParty(file));
 
     final bool copyrightCheckSucceeded = await _checkCodeLicenses(codeFiles);
     print('\n=======================================\n');
@@ -246,7 +246,7 @@
   }
 
   bool _isThirdParty(File file) {
-    return p.split(file.path).contains('third_party');
+    return path.split(file.path).contains('third_party');
   }
 
   Future<List<File>> _getAllFiles() => packagesDir.parent
diff --git a/script/tool/lib/src/lint_podspecs_command.dart b/script/tool/lib/src/lint_podspecs_command.dart
index 82cce0b..d0d93fc 100644
--- a/script/tool/lib/src/lint_podspecs_command.dart
+++ b/script/tool/lib/src/lint_podspecs_command.dart
@@ -25,8 +25,7 @@
     Directory packagesDir, {
     ProcessRunner processRunner = const ProcessRunner(),
     Platform platform = const LocalPlatform(),
-  })  : _platform = platform,
-        super(packagesDir, processRunner: processRunner) {
+  }) : super(packagesDir, processRunner: processRunner, platform: platform) {
     argParser.addMultiOption('ignore-warnings',
         help:
             'Do not pass --allow-warnings flag to "pod lib lint" for podspecs '
@@ -45,11 +44,9 @@
       'Runs "pod lib lint" on all iOS and macOS plugin podspecs.\n\n'
       'This command requires "pod" and "flutter" to be in your path. Runs on macOS only.';
 
-  final Platform _platform;
-
   @override
   Future<void> initializeRun() async {
-    if (!_platform.isMacOS) {
+    if (!platform.isMacOS) {
       printError('This command is only supported on macOS');
       throw ToolExit(_exitUnsupportedPlatform);
     }
@@ -89,11 +86,10 @@
     final List<File> podspecs =
         await getFilesForPackage(package).where((File entity) {
       final String filePath = entity.path;
-      return p.extension(filePath) == '.podspec';
+      return path.extension(filePath) == '.podspec';
     }).toList();
 
-    podspecs.sort(
-        (File a, File b) => p.basename(a.path).compareTo(p.basename(b.path)));
+    podspecs.sort((File a, File b) => a.basename.compareTo(b.basename));
     return podspecs;
   }
 
diff --git a/script/tool/lib/src/list_command.dart b/script/tool/lib/src/list_command.dart
index 39515cf..20f01ff 100644
--- a/script/tool/lib/src/list_command.dart
+++ b/script/tool/lib/src/list_command.dart
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import 'package:file/file.dart';
+import 'package:platform/platform.dart';
 
 import 'common/plugin_command.dart';
 
@@ -10,7 +11,10 @@
 class ListCommand extends PluginCommand {
   /// Creates an instance of the list command, whose behavior depends on the
   /// 'type' argument it provides.
-  ListCommand(Directory packagesDir) : super(packagesDir) {
+  ListCommand(
+    Directory packagesDir, {
+    Platform platform = const LocalPlatform(),
+  }) : super(packagesDir, platform: platform) {
     argParser.addOption(
       _type,
       defaultsTo: _plugin,
diff --git a/script/tool/lib/src/publish_check_command.dart b/script/tool/lib/src/publish_check_command.dart
index ccafabf..fda68a6 100644
--- a/script/tool/lib/src/publish_check_command.dart
+++ b/script/tool/lib/src/publish_check_command.dart
@@ -8,6 +8,7 @@
 
 import 'package:file/file.dart';
 import 'package:http/http.dart' as http;
+import 'package:platform/platform.dart';
 import 'package:pub_semver/pub_semver.dart';
 import 'package:pubspec_parse/pubspec_parse.dart';
 
@@ -22,10 +23,11 @@
   PublishCheckCommand(
     Directory packagesDir, {
     ProcessRunner processRunner = const ProcessRunner(),
+    Platform platform = const LocalPlatform(),
     http.Client? httpClient,
   })  : _pubVersionFinder =
             PubVersionFinder(httpClient: httpClient ?? http.Client()),
-        super(packagesDir, processRunner: processRunner) {
+        super(packagesDir, processRunner: processRunner, platform: platform) {
     argParser.addFlag(
       _allowPrereleaseFlag,
       help: 'Allows the pre-release SDK warning to pass.\n'
@@ -128,7 +130,7 @@
   Future<bool> _hasValidPublishCheckRun(Directory package) async {
     print('Running pub publish --dry-run:');
     final io.Process process = await processRunner.start(
-      'flutter',
+      flutterCommand,
       <String>['pub', 'publish', '--', '--dry-run'],
       workingDirectory: package,
     );
diff --git a/script/tool/lib/src/publish_plugin_command.dart b/script/tool/lib/src/publish_plugin_command.dart
index 6de53ba..8bcb9e3 100644
--- a/script/tool/lib/src/publish_plugin_command.dart
+++ b/script/tool/lib/src/publish_plugin_command.dart
@@ -208,9 +208,13 @@
     final List<String> packagesFailed = <String>[];
 
     for (final String pubspecPath in changedPubspecs) {
+      // Convert git's Posix-style paths to a path that matches the current
+      // filesystem.
+      final String localStylePubspecPath =
+          path.joinAll(p.posix.split(pubspecPath));
       final File pubspecFile = packagesDir.fileSystem
           .directory(baseGitDir.path)
-          .childFile(pubspecPath);
+          .childFile(localStylePubspecPath);
       final _CheckNeedsReleaseResult result = await _checkNeedsRelease(
         pubspecFile: pubspecFile,
         existingTags: existingTags,
@@ -445,7 +449,7 @@
     }
 
     final io.Process publish = await processRunner.start(
-        'flutter', <String>['pub', 'publish'] + publishFlags,
+        flutterCommand, <String>['pub', 'publish'] + publishFlags,
         workingDirectory: packageDir);
     publish.stdout
         .transform(utf8.decoder)
diff --git a/script/tool/lib/src/pubspec_check_command.dart b/script/tool/lib/src/pubspec_check_command.dart
index 7d39c73..539b170 100644
--- a/script/tool/lib/src/pubspec_check_command.dart
+++ b/script/tool/lib/src/pubspec_check_command.dart
@@ -4,6 +4,7 @@
 
 import 'package:file/file.dart';
 import 'package:git/git.dart';
+import 'package:platform/platform.dart';
 import 'package:pubspec_parse/pubspec_parse.dart';
 
 import 'common/package_looping_command.dart';
@@ -19,8 +20,14 @@
   PubspecCheckCommand(
     Directory packagesDir, {
     ProcessRunner processRunner = const ProcessRunner(),
+    Platform platform = const LocalPlatform(),
     GitDir? gitDir,
-  }) : super(packagesDir, processRunner: processRunner, gitDir: gitDir);
+  }) : super(
+          packagesDir,
+          processRunner: processRunner,
+          platform: platform,
+          gitDir: gitDir,
+        );
 
   // Section order for plugins. Because the 'flutter' section is critical
   // information for plugins, and usually small, it goes near the top unlike in
diff --git a/script/tool/lib/src/test_command.dart b/script/tool/lib/src/test_command.dart
index d06a284..9dfe66b 100644
--- a/script/tool/lib/src/test_command.dart
+++ b/script/tool/lib/src/test_command.dart
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import 'package:file/file.dart';
+import 'package:platform/platform.dart';
 
 import 'common/core.dart';
 import 'common/package_looping_command.dart';
@@ -15,7 +16,8 @@
   TestCommand(
     Directory packagesDir, {
     ProcessRunner processRunner = const ProcessRunner(),
-  }) : super(packagesDir, processRunner: processRunner) {
+    Platform platform = const LocalPlatform(),
+  }) : super(packagesDir, processRunner: processRunner, platform: platform) {
     argParser.addOption(
       kEnableExperiment,
       defaultsTo: '',
diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart
index f0902f0..c08600c 100644
--- a/script/tool/lib/src/version_check_command.dart
+++ b/script/tool/lib/src/version_check_command.dart
@@ -7,6 +7,7 @@
 import 'package:http/http.dart' as http;
 import 'package:meta/meta.dart';
 import 'package:path/path.dart' as p;
+import 'package:platform/platform.dart';
 import 'package:pub_semver/pub_semver.dart';
 import 'package:pubspec_parse/pubspec_parse.dart';
 
@@ -77,11 +78,17 @@
   VersionCheckCommand(
     Directory packagesDir, {
     ProcessRunner processRunner = const ProcessRunner(),
+    Platform platform = const LocalPlatform(),
     GitDir? gitDir,
     http.Client? httpClient,
   })  : _pubVersionFinder =
             PubVersionFinder(httpClient: httpClient ?? http.Client()),
-        super(packagesDir, processRunner: processRunner, gitDir: gitDir) {
+        super(
+          packagesDir,
+          processRunner: processRunner,
+          platform: platform,
+          gitDir: gitDir,
+        ) {
     argParser.addFlag(
       _againstPubFlag,
       help: 'Whether the version check should run against the version on pub.\n'
@@ -179,8 +186,13 @@
     required GitVersionFinder gitVersionFinder,
   }) async {
     final File pubspecFile = package.childFile('pubspec.yaml');
-    return await gitVersionFinder.getPackageVersion(
-        p.relative(pubspecFile.absolute.path, from: (await gitDir).path));
+    final String relativePath =
+        path.relative(pubspecFile.absolute.path, from: (await gitDir).path);
+    // Use Posix-style paths for git.
+    final String gitPath = path.style == p.Style.windows
+        ? p.posix.joinAll(path.split(relativePath))
+        : relativePath;
+    return await gitVersionFinder.getPackageVersion(gitPath);
   }
 
   /// Returns true if the version of [package] is either unchanged relative to
diff --git a/script/tool/lib/src/xctest_command.dart b/script/tool/lib/src/xctest_command.dart
index cd3b674..176adad 100644
--- a/script/tool/lib/src/xctest_command.dart
+++ b/script/tool/lib/src/xctest_command.dart
@@ -6,7 +6,7 @@
 import 'dart:io' as io;
 
 import 'package:file/file.dart';
-import 'package:path/path.dart' as p;
+import 'package:platform/platform.dart';
 
 import 'common/core.dart';
 import 'common/package_looping_command.dart';
@@ -32,7 +32,8 @@
   XCTestCommand(
     Directory packagesDir, {
     ProcessRunner processRunner = const ProcessRunner(),
-  }) : super(packagesDir, processRunner: processRunner) {
+    Platform platform = const LocalPlatform(),
+  }) : super(packagesDir, processRunner: processRunner, platform: platform) {
     argParser.addOption(
       _kiOSDestination,
       help:
@@ -142,7 +143,7 @@
     for (final Directory example in getExamplesForPlugin(plugin)) {
       // Running tests and static analyzer.
       final String examplePath =
-          p.relative(example.path, from: plugin.parent.path);
+          getRelativePosixPath(example, from: plugin.parent);
       print('Running $platform tests and analyzer for $examplePath...');
       int exitCode =
           await _runTests(true, example, platform, extraFlags: extraXcrunFlags);
diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart
index adeaaba..69a2c4f 100644
--- a/script/tool/test/analyze_command_test.dart
+++ b/script/tool/test/analyze_command_test.dart
@@ -16,16 +16,21 @@
 
 void main() {
   late FileSystem fileSystem;
+  late MockPlatform mockPlatform;
   late Directory packagesDir;
   late RecordingProcessRunner processRunner;
   late CommandRunner<void> runner;
 
   setUp(() {
     fileSystem = MemoryFileSystem();
+    mockPlatform = MockPlatform();
     packagesDir = createPackagesDirectory(fileSystem: fileSystem);
     processRunner = RecordingProcessRunner();
-    final AnalyzeCommand analyzeCommand =
-        AnalyzeCommand(packagesDir, processRunner: processRunner);
+    final AnalyzeCommand analyzeCommand = AnalyzeCommand(
+      packagesDir,
+      processRunner: processRunner,
+      platform: mockPlatform,
+    );
 
     runner = CommandRunner<void>('analyze_command', 'Test for analyze_command');
     runner.addCommand(analyzeCommand);
diff --git a/script/tool/test/build_examples_command_test.dart b/script/tool/test/build_examples_command_test.dart
index c0c90a1..27489a5 100644
--- a/script/tool/test/build_examples_command_test.dart
+++ b/script/tool/test/build_examples_command_test.dart
@@ -10,8 +10,6 @@
 import 'package:flutter_plugin_tools/src/build_examples_command.dart';
 import 'package:flutter_plugin_tools/src/common/core.dart';
 import 'package:flutter_plugin_tools/src/common/plugin_utils.dart';
-import 'package:path/path.dart' as p;
-import 'package:platform/platform.dart';
 import 'package:test/test.dart';
 
 import 'mocks.dart';
@@ -20,18 +18,21 @@
 void main() {
   group('build-example', () {
     late FileSystem fileSystem;
+    late MockPlatform mockPlatform;
     late Directory packagesDir;
     late CommandRunner<void> runner;
     late RecordingProcessRunner processRunner;
-    final String flutterCommand =
-        const LocalPlatform().isWindows ? 'flutter.bat' : 'flutter';
 
     setUp(() {
       fileSystem = MemoryFileSystem();
+      mockPlatform = MockPlatform();
       packagesDir = createPackagesDirectory(fileSystem: fileSystem);
       processRunner = RecordingProcessRunner();
-      final BuildExamplesCommand command =
-          BuildExamplesCommand(packagesDir, processRunner: processRunner);
+      final BuildExamplesCommand command = BuildExamplesCommand(
+        packagesDir,
+        processRunner: processRunner,
+        platform: mockPlatform,
+      );
 
       runner = CommandRunner<void>(
           'build_examples_command', 'Test for build_example_command');
@@ -59,7 +60,9 @@
             kPlatformIos: PlatformSupport.inline
           });
 
-      processRunner.mockProcessesForExecutable['flutter'] = <io.Process>[
+      processRunner
+              .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] =
+          <io.Process>[
         MockProcess.failing() // flutter packages get
       ];
 
@@ -81,6 +84,7 @@
 
     test('building for iOS when plugin is not set up for iOS results in no-op',
         () async {
+      mockPlatform.isMacOS = true;
       createFakePlugin('plugin', packagesDir);
 
       final List<String> output =
@@ -99,7 +103,8 @@
       expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
     });
 
-    test('building for ios', () async {
+    test('building for iOS', () async {
+      mockPlatform.isMacOS = true;
       final Directory pluginDirectory = createFakePlugin('plugin', packagesDir,
           platformSupport: <String, PlatformSupport>{
             kPlatformIos: PlatformSupport.inline
@@ -110,13 +115,11 @@
 
       final List<String> output = await runCapturingPrint(runner,
           <String>['build-examples', '--ios', '--enable-experiment=exp1']);
-      final String packageName =
-          p.relative(pluginExampleDirectory.path, from: packagesDir.path);
 
       expect(
         output,
         containsAllInOrder(<String>[
-          '\nBUILDING $packageName for iOS',
+          '\nBUILDING plugin/example for iOS',
         ]),
       );
 
@@ -124,7 +127,7 @@
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
             ProcessCall(
-                flutterCommand,
+                getFlutterCommand(mockPlatform),
                 const <String>[
                   'build',
                   'ios',
@@ -138,6 +141,7 @@
     test(
         'building for Linux when plugin is not set up for Linux results in no-op',
         () async {
+      mockPlatform.isLinux = true;
       createFakePlugin('plugin', packagesDir);
 
       final List<String> output = await runCapturingPrint(
@@ -157,6 +161,7 @@
     });
 
     test('building for Linux', () async {
+      mockPlatform.isLinux = true;
       final Directory pluginDirectory = createFakePlugin('plugin', packagesDir,
           platformSupport: <String, PlatformSupport>{
             kPlatformLinux: PlatformSupport.inline,
@@ -167,26 +172,25 @@
 
       final List<String> output = await runCapturingPrint(
           runner, <String>['build-examples', '--linux']);
-      final String packageName =
-          p.relative(pluginExampleDirectory.path, from: packagesDir.path);
 
       expect(
         output,
         containsAllInOrder(<String>[
-          '\nBUILDING $packageName for Linux',
+          '\nBUILDING plugin/example for Linux',
         ]),
       );
 
       expect(
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
-            ProcessCall(flutterCommand, const <String>['build', 'linux'],
-                pluginExampleDirectory.path),
+            ProcessCall(getFlutterCommand(mockPlatform),
+                const <String>['build', 'linux'], pluginExampleDirectory.path),
           ]));
     });
 
-    test('building for macos with no implementation results in no-op',
+    test('building for macOS with no implementation results in no-op',
         () async {
+      mockPlatform.isMacOS = true;
       createFakePlugin('plugin', packagesDir);
 
       final List<String> output = await runCapturingPrint(
@@ -205,7 +209,8 @@
       expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
     });
 
-    test('building for macos', () async {
+    test('building for macOS', () async {
+      mockPlatform.isMacOS = true;
       final Directory pluginDirectory = createFakePlugin('plugin', packagesDir,
           platformSupport: <String, PlatformSupport>{
             kPlatformMacos: PlatformSupport.inline,
@@ -216,21 +221,19 @@
 
       final List<String> output = await runCapturingPrint(
           runner, <String>['build-examples', '--macos']);
-      final String packageName =
-          p.relative(pluginExampleDirectory.path, from: packagesDir.path);
 
       expect(
         output,
         containsAllInOrder(<String>[
-          '\nBUILDING $packageName for macOS',
+          '\nBUILDING plugin/example for macOS',
         ]),
       );
 
       expect(
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
-            ProcessCall(flutterCommand, const <String>['build', 'macos'],
-                pluginExampleDirectory.path),
+            ProcessCall(getFlutterCommand(mockPlatform),
+                const <String>['build', 'macos'], pluginExampleDirectory.path),
           ]));
     });
 
@@ -264,27 +267,26 @@
 
       final List<String> output =
           await runCapturingPrint(runner, <String>['build-examples', '--web']);
-      final String packageName =
-          p.relative(pluginExampleDirectory.path, from: packagesDir.path);
 
       expect(
         output,
         containsAllInOrder(<String>[
-          '\nBUILDING $packageName for web',
+          '\nBUILDING plugin/example for web',
         ]),
       );
 
       expect(
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
-            ProcessCall(flutterCommand, const <String>['build', 'web'],
-                pluginExampleDirectory.path),
+            ProcessCall(getFlutterCommand(mockPlatform),
+                const <String>['build', 'web'], pluginExampleDirectory.path),
           ]));
     });
 
     test(
         'building for Windows when plugin is not set up for Windows results in no-op',
         () async {
+      mockPlatform.isWindows = true;
       createFakePlugin('plugin', packagesDir);
 
       final List<String> output = await runCapturingPrint(
@@ -303,7 +305,8 @@
       expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
     });
 
-    test('building for windows', () async {
+    test('building for Windows', () async {
+      mockPlatform.isWindows = true;
       final Directory pluginDirectory = createFakePlugin('plugin', packagesDir,
           platformSupport: <String, PlatformSupport>{
             kPlatformWindows: PlatformSupport.inline
@@ -314,20 +317,20 @@
 
       final List<String> output = await runCapturingPrint(
           runner, <String>['build-examples', '--windows']);
-      final String packageName =
-          p.relative(pluginExampleDirectory.path, from: packagesDir.path);
 
       expect(
         output,
         containsAllInOrder(<String>[
-          '\nBUILDING $packageName for Windows',
+          '\nBUILDING plugin/example for Windows',
         ]),
       );
 
       expect(
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
-            ProcessCall(flutterCommand, const <String>['build', 'windows'],
+            ProcessCall(
+                getFlutterCommand(mockPlatform),
+                const <String>['build', 'windows'],
                 pluginExampleDirectory.path),
           ]));
     });
@@ -353,7 +356,7 @@
       expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
     });
 
-    test('building for android', () async {
+    test('building for Android', () async {
       final Directory pluginDirectory = createFakePlugin('plugin', packagesDir,
           platformSupport: <String, PlatformSupport>{
             kPlatformAndroid: PlatformSupport.inline
@@ -366,21 +369,19 @@
         'build-examples',
         '--apk',
       ]);
-      final String packageName =
-          p.relative(pluginExampleDirectory.path, from: packagesDir.path);
 
       expect(
         output,
         containsAllInOrder(<String>[
-          '\nBUILDING $packageName for Android (apk)',
+          '\nBUILDING plugin/example for Android (apk)',
         ]),
       );
 
       expect(
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
-            ProcessCall(flutterCommand, const <String>['build', 'apk'],
-                pluginExampleDirectory.path),
+            ProcessCall(getFlutterCommand(mockPlatform),
+                const <String>['build', 'apk'], pluginExampleDirectory.path),
           ]));
     });
 
@@ -400,7 +401,7 @@
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
             ProcessCall(
-                flutterCommand,
+                getFlutterCommand(mockPlatform),
                 const <String>['build', 'apk', '--enable-experiment=exp1'],
                 pluginExampleDirectory.path),
           ]));
@@ -421,7 +422,7 @@
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
             ProcessCall(
-                flutterCommand,
+                getFlutterCommand(mockPlatform),
                 const <String>[
                   'build',
                   'ios',
diff --git a/script/tool/test/common/package_looping_command_test.dart b/script/tool/test/common/package_looping_command_test.dart
index 917fbc0..542e91a 100644
--- a/script/tool/test/common/package_looping_command_test.dart
+++ b/script/tool/test/common/package_looping_command_test.dart
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:io';
+import 'dart:io' as io;
 
 import 'package:args/command_runner.dart';
 import 'package:file/file.dart';
@@ -13,8 +13,10 @@
 import 'package:flutter_plugin_tools/src/common/process_runner.dart';
 import 'package:git/git.dart';
 import 'package:mockito/mockito.dart';
+import 'package:platform/platform.dart';
 import 'package:test/test.dart';
 
+import '../mocks.dart';
 import '../util.dart';
 import 'plugin_command_test.mocks.dart';
 
@@ -36,11 +38,13 @@
 
 void main() {
   late FileSystem fileSystem;
+  late MockPlatform mockPlatform;
   late Directory packagesDir;
   late Directory thirdPartyPackagesDir;
 
   setUp(() {
     fileSystem = MemoryFileSystem();
+    mockPlatform = MockPlatform();
     packagesDir = createPackagesDirectory(fileSystem: fileSystem);
     thirdPartyPackagesDir = packagesDir.parent
         .childDirectory('third_party')
@@ -69,11 +73,12 @@
         when<String?>(mockProcessResult.stdout as String?)
             .thenReturn(gitDiffResponse);
       }
-      return Future<ProcessResult>.value(mockProcessResult);
+      return Future<io.ProcessResult>.value(mockProcessResult);
     });
 
     return TestPackageLoopingCommand(
       packagesDir,
+      platform: mockPlatform,
       hasLongOutput: hasLongOutput,
       includeSubpackages: includeSubpackages,
       failsDuringInit: failsDuringInit,
@@ -502,7 +507,25 @@
     test('getPackageDescription prints packageDir-relative paths by default',
         () async {
       final TestPackageLoopingCommand command =
-          TestPackageLoopingCommand(packagesDir);
+          TestPackageLoopingCommand(packagesDir, platform: mockPlatform);
+
+      expect(
+        command.getPackageDescription(packagesDir.childDirectory('foo')),
+        'foo',
+      );
+      expect(
+        command.getPackageDescription(packagesDir
+            .childDirectory('foo')
+            .childDirectory('bar')
+            .childDirectory('baz')),
+        'foo/bar/baz',
+      );
+    });
+
+    test('getPackageDescription always uses Posix-style paths', () async {
+      mockPlatform.isWindows = true;
+      final TestPackageLoopingCommand command =
+          TestPackageLoopingCommand(packagesDir, platform: mockPlatform);
 
       expect(
         command.getPackageDescription(packagesDir.childDirectory('foo')),
@@ -521,7 +544,7 @@
         'getPackageDescription elides group name in grouped federated plugin structure',
         () async {
       final TestPackageLoopingCommand command =
-          TestPackageLoopingCommand(packagesDir);
+          TestPackageLoopingCommand(packagesDir, platform: mockPlatform);
 
       expect(
         command.getPackageDescription(packagesDir
@@ -542,6 +565,7 @@
 class TestPackageLoopingCommand extends PackageLoopingCommand {
   TestPackageLoopingCommand(
     Directory packagesDir, {
+    required Platform platform,
     this.hasLongOutput = true,
     this.includeSubpackages = false,
     this.customFailureListHeader,
@@ -552,7 +576,8 @@
     this.captureOutput = false,
     ProcessRunner processRunner = const ProcessRunner(),
     GitDir? gitDir,
-  }) : super(packagesDir, processRunner: processRunner, gitDir: gitDir);
+  }) : super(packagesDir,
+            processRunner: processRunner, platform: platform, gitDir: gitDir);
 
   final List<String> checkedPackages = <String>[];
   final List<String> capturedOutput = <String>[];
@@ -629,4 +654,4 @@
   }
 }
 
-class MockProcessResult extends Mock implements ProcessResult {}
+class MockProcessResult extends Mock implements io.ProcessResult {}
diff --git a/script/tool/test/common/plugin_command_test.dart b/script/tool/test/common/plugin_command_test.dart
index 3f1f1ad..fdab961 100644
--- a/script/tool/test/common/plugin_command_test.dart
+++ b/script/tool/test/common/plugin_command_test.dart
@@ -12,8 +12,10 @@
 import 'package:git/git.dart';
 import 'package:mockito/annotations.dart';
 import 'package:mockito/mockito.dart';
+import 'package:platform/platform.dart';
 import 'package:test/test.dart';
 
+import '../mocks.dart';
 import '../util.dart';
 import 'plugin_command_test.mocks.dart';
 
@@ -22,6 +24,7 @@
   late RecordingProcessRunner processRunner;
   late CommandRunner<void> runner;
   late FileSystem fileSystem;
+  late MockPlatform mockPlatform;
   late Directory packagesDir;
   late Directory thirdPartyPackagesDir;
   late List<String> plugins;
@@ -30,6 +33,7 @@
 
   setUp(() {
     fileSystem = MemoryFileSystem();
+    mockPlatform = MockPlatform();
     packagesDir = createPackagesDirectory(fileSystem: fileSystem);
     thirdPartyPackagesDir = packagesDir.parent
         .childDirectory('third_party')
@@ -54,6 +58,7 @@
       plugins,
       packagesDir,
       processRunner: processRunner,
+      platform: mockPlatform,
       gitDir: gitDir,
     );
     runner =
@@ -414,8 +419,10 @@
     this._plugins,
     Directory packagesDir, {
     ProcessRunner processRunner = const ProcessRunner(),
+    Platform platform = const LocalPlatform(),
     GitDir? gitDir,
-  }) : super(packagesDir, processRunner: processRunner, gitDir: gitDir);
+  }) : super(packagesDir,
+            processRunner: processRunner, platform: platform, gitDir: gitDir);
 
   final List<String> _plugins;
 
diff --git a/script/tool/test/drive_examples_command_test.dart b/script/tool/test/drive_examples_command_test.dart
index 681a9e0..c689318 100644
--- a/script/tool/test/drive_examples_command_test.dart
+++ b/script/tool/test/drive_examples_command_test.dart
@@ -10,7 +10,6 @@
 import 'package:flutter_plugin_tools/src/common/core.dart';
 import 'package:flutter_plugin_tools/src/common/plugin_utils.dart';
 import 'package:flutter_plugin_tools/src/drive_examples_command.dart';
-import 'package:path/path.dart' as p;
 import 'package:platform/platform.dart';
 import 'package:test/test.dart';
 
@@ -23,18 +22,18 @@
 void main() {
   group('test drive_example_command', () {
     late FileSystem fileSystem;
+    late Platform mockPlatform;
     late Directory packagesDir;
     late CommandRunner<void> runner;
     late RecordingProcessRunner processRunner;
-    final String flutterCommand =
-        const LocalPlatform().isWindows ? 'flutter.bat' : 'flutter';
 
     setUp(() {
       fileSystem = MemoryFileSystem();
+      mockPlatform = MockPlatform();
       packagesDir = createPackagesDirectory(fileSystem: fileSystem);
       processRunner = RecordingProcessRunner();
-      final DriveExamplesCommand command =
-          DriveExamplesCommand(packagesDir, processRunner: processRunner);
+      final DriveExamplesCommand command = DriveExamplesCommand(packagesDir,
+          processRunner: processRunner, platform: mockPlatform);
 
       runner = CommandRunner<void>(
           'drive_examples_command', 'Test for drive_example_command');
@@ -63,9 +62,9 @@
 
       final MockProcess mockDevicesProcess = MockProcess.succeeding();
       mockDevicesProcess.stdoutController.close(); // ignore: unawaited_futures
-      processRunner.mockProcessesForExecutable['flutter'] = <io.Process>[
-        mockDevicesProcess
-      ];
+      processRunner
+              .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] =
+          <io.Process>[mockDevicesProcess];
       processRunner.resultStdout = output;
     }
 
@@ -150,9 +149,9 @@
 
     test('fails for iOS if getting devices fails', () async {
       // Simulate failure from `flutter devices`.
-      processRunner.mockProcessesForExecutable['flutter'] = <io.Process>[
-        MockProcess.failing()
-      ];
+      processRunner
+              .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] =
+          <io.Process>[MockProcess.failing()];
 
       Error? commandError;
       final List<String> output = await runCapturingPrint(
@@ -216,23 +215,21 @@
         ]),
       );
 
-      final String deviceTestPath = p.join('test_driver', 'plugin.dart');
-      final String driverTestPath = p.join('test_driver', 'plugin_test.dart');
       expect(
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
+            ProcessCall(getFlutterCommand(mockPlatform),
+                const <String>['devices', '--machine'], null),
             ProcessCall(
-                flutterCommand, const <String>['devices', '--machine'], null),
-            ProcessCall(
-                flutterCommand,
-                <String>[
+                getFlutterCommand(mockPlatform),
+                const <String>[
                   'drive',
                   '-d',
                   _fakeIosDevice,
                   '--driver',
-                  driverTestPath,
+                  'test_driver/plugin_test.dart',
                   '--target',
-                  deviceTestPath
+                  'test_driver/plugin.dart'
                 ],
                 pluginExampleDirectory.path),
           ]));
@@ -337,35 +334,33 @@
         ]),
       );
 
-      final String driverTestPath =
-          p.join('test_driver', 'integration_test.dart');
       expect(
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
+            ProcessCall(getFlutterCommand(mockPlatform),
+                const <String>['devices', '--machine'], null),
             ProcessCall(
-                flutterCommand, const <String>['devices', '--machine'], null),
-            ProcessCall(
-                flutterCommand,
-                <String>[
+                getFlutterCommand(mockPlatform),
+                const <String>[
                   'drive',
                   '-d',
                   _fakeIosDevice,
                   '--driver',
-                  driverTestPath,
+                  'test_driver/integration_test.dart',
                   '--target',
-                  p.join('integration_test', 'bar_test.dart'),
+                  'integration_test/bar_test.dart',
                 ],
                 pluginExampleDirectory.path),
             ProcessCall(
-                flutterCommand,
-                <String>[
+                getFlutterCommand(mockPlatform),
+                const <String>[
                   'drive',
                   '-d',
                   _fakeIosDevice,
                   '--driver',
-                  driverTestPath,
+                  'test_driver/integration_test.dart',
                   '--target',
-                  p.join('integration_test', 'foo_test.dart'),
+                  'integration_test/foo_test.dart',
                 ],
                 pluginExampleDirectory.path),
           ]));
@@ -425,21 +420,19 @@
         ]),
       );
 
-      final String deviceTestPath = p.join('test_driver', 'plugin.dart');
-      final String driverTestPath = p.join('test_driver', 'plugin_test.dart');
       expect(
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
             ProcessCall(
-                flutterCommand,
-                <String>[
+                getFlutterCommand(mockPlatform),
+                const <String>[
                   'drive',
                   '-d',
                   'linux',
                   '--driver',
-                  driverTestPath,
+                  'test_driver/plugin_test.dart',
                   '--target',
-                  deviceTestPath
+                  'test_driver/plugin.dart'
                 ],
                 pluginExampleDirectory.path),
           ]));
@@ -500,21 +493,19 @@
         ]),
       );
 
-      final String deviceTestPath = p.join('test_driver', 'plugin.dart');
-      final String driverTestPath = p.join('test_driver', 'plugin_test.dart');
       expect(
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
             ProcessCall(
-                flutterCommand,
-                <String>[
+                getFlutterCommand(mockPlatform),
+                const <String>[
                   'drive',
                   '-d',
                   'macos',
                   '--driver',
-                  driverTestPath,
+                  'test_driver/plugin_test.dart',
                   '--target',
-                  deviceTestPath
+                  'test_driver/plugin.dart'
                 ],
                 pluginExampleDirectory.path),
           ]));
@@ -573,23 +564,21 @@
         ]),
       );
 
-      final String deviceTestPath = p.join('test_driver', 'plugin.dart');
-      final String driverTestPath = p.join('test_driver', 'plugin_test.dart');
       expect(
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
             ProcessCall(
-                flutterCommand,
-                <String>[
+                getFlutterCommand(mockPlatform),
+                const <String>[
                   'drive',
                   '-d',
                   'web-server',
                   '--web-port=7357',
                   '--browser-name=chrome',
                   '--driver',
-                  driverTestPath,
+                  'test_driver/plugin_test.dart',
                   '--target',
-                  deviceTestPath
+                  'test_driver/plugin.dart'
                 ],
                 pluginExampleDirectory.path),
           ]));
@@ -649,21 +638,19 @@
         ]),
       );
 
-      final String deviceTestPath = p.join('test_driver', 'plugin.dart');
-      final String driverTestPath = p.join('test_driver', 'plugin_test.dart');
       expect(
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
             ProcessCall(
-                flutterCommand,
-                <String>[
+                getFlutterCommand(mockPlatform),
+                const <String>[
                   'drive',
                   '-d',
                   'windows',
                   '--driver',
-                  driverTestPath,
+                  'test_driver/plugin_test.dart',
                   '--target',
-                  deviceTestPath
+                  'test_driver/plugin.dart'
                 ],
                 pluginExampleDirectory.path),
           ]));
@@ -699,23 +686,21 @@
         ]),
       );
 
-      final String deviceTestPath = p.join('test_driver', 'plugin.dart');
-      final String driverTestPath = p.join('test_driver', 'plugin_test.dart');
       expect(
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
+            ProcessCall(getFlutterCommand(mockPlatform),
+                const <String>['devices', '--machine'], null),
             ProcessCall(
-                flutterCommand, const <String>['devices', '--machine'], null),
-            ProcessCall(
-                flutterCommand,
-                <String>[
+                getFlutterCommand(mockPlatform),
+                const <String>[
                   'drive',
                   '-d',
                   _fakeAndroidDevice,
                   '--driver',
-                  driverTestPath,
+                  'test_driver/plugin_test.dart',
                   '--target',
-                  deviceTestPath
+                  'test_driver/plugin.dart'
                 ],
                 pluginExampleDirectory.path),
           ]));
@@ -749,8 +734,8 @@
 
       // Output should be empty other than the device query.
       expect(processRunner.recordedCalls, <ProcessCall>[
-        ProcessCall(
-            flutterCommand, const <String>['devices', '--machine'], null),
+        ProcessCall(getFlutterCommand(mockPlatform),
+            const <String>['devices', '--machine'], null),
       ]);
     });
 
@@ -782,8 +767,8 @@
 
       // Output should be empty other than the device query.
       expect(processRunner.recordedCalls, <ProcessCall>[
-        ProcessCall(
-            flutterCommand, const <String>['devices', '--machine'], null),
+        ProcessCall(getFlutterCommand(mockPlatform),
+            const <String>['devices', '--machine'], null),
       ]);
     });
 
@@ -833,24 +818,22 @@
         '--enable-experiment=exp1',
       ]);
 
-      final String deviceTestPath = p.join('test_driver', 'plugin.dart');
-      final String driverTestPath = p.join('test_driver', 'plugin_test.dart');
       expect(
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
+            ProcessCall(getFlutterCommand(mockPlatform),
+                const <String>['devices', '--machine'], null),
             ProcessCall(
-                flutterCommand, const <String>['devices', '--machine'], null),
-            ProcessCall(
-                flutterCommand,
-                <String>[
+                getFlutterCommand(mockPlatform),
+                const <String>[
                   'drive',
                   '-d',
                   _fakeIosDevice,
                   '--enable-experiment=exp1',
                   '--driver',
-                  driverTestPath,
+                  'test_driver/plugin_test.dart',
                   '--target',
-                  deviceTestPath
+                  'test_driver/plugin.dart'
                 ],
                 pluginExampleDirectory.path),
           ]));
@@ -967,7 +950,9 @@
       );
 
       // Simulate failure from `flutter drive`.
-      processRunner.mockProcessesForExecutable['flutter'] = <io.Process>[
+      processRunner
+              .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] =
+          <io.Process>[
         // No mock for 'devices', since it's running for macOS.
         MockProcess.failing(), // 'drive' #1
         MockProcess.failing(), // 'drive' #2
@@ -994,33 +979,31 @@
 
       final Directory pluginExampleDirectory =
           pluginDirectory.childDirectory('example');
-      final String driverTestPath =
-          p.join('test_driver', 'integration_test.dart');
       expect(
           processRunner.recordedCalls,
           orderedEquals(<ProcessCall>[
             ProcessCall(
-                flutterCommand,
-                <String>[
+                getFlutterCommand(mockPlatform),
+                const <String>[
                   'drive',
                   '-d',
                   'macos',
                   '--driver',
-                  driverTestPath,
+                  'test_driver/integration_test.dart',
                   '--target',
-                  p.join('integration_test', 'bar_test.dart'),
+                  'integration_test/bar_test.dart',
                 ],
                 pluginExampleDirectory.path),
             ProcessCall(
-                flutterCommand,
-                <String>[
+                getFlutterCommand(mockPlatform),
+                const <String>[
                   'drive',
                   '-d',
                   'macos',
                   '--driver',
-                  driverTestPath,
+                  'test_driver/integration_test.dart',
                   '--target',
-                  p.join('integration_test', 'foo_test.dart'),
+                  'integration_test/foo_test.dart',
                 ],
                 pluginExampleDirectory.path),
           ]));
diff --git a/script/tool/test/firebase_test_lab_command_test.dart b/script/tool/test/firebase_test_lab_command_test.dart
index 0199eba..c265868 100644
--- a/script/tool/test/firebase_test_lab_command_test.dart
+++ b/script/tool/test/firebase_test_lab_command_test.dart
@@ -17,16 +17,21 @@
 void main() {
   group('$FirebaseTestLabCommand', () {
     FileSystem fileSystem;
+    late MockPlatform mockPlatform;
     late Directory packagesDir;
     late CommandRunner<void> runner;
     late RecordingProcessRunner processRunner;
 
     setUp(() {
       fileSystem = MemoryFileSystem();
+      mockPlatform = MockPlatform();
       packagesDir = createPackagesDirectory(fileSystem: fileSystem);
       processRunner = RecordingProcessRunner();
-      final FirebaseTestLabCommand command =
-          FirebaseTestLabCommand(packagesDir, processRunner: processRunner);
+      final FirebaseTestLabCommand command = FirebaseTestLabCommand(
+        packagesDir,
+        processRunner: processRunner,
+        platform: mockPlatform,
+      );
 
       runner = CommandRunner<void>(
           'firebase_test_lab_command', 'Test for $FirebaseTestLabCommand');
diff --git a/script/tool/test/format_command_test.dart b/script/tool/test/format_command_test.dart
index e7f4d79..fabef31 100644
--- a/script/tool/test/format_command_test.dart
+++ b/script/tool/test/format_command_test.dart
@@ -17,21 +17,28 @@
 
 void main() {
   late FileSystem fileSystem;
+  late MockPlatform mockPlatform;
   late Directory packagesDir;
+  late p.Context path;
   late RecordingProcessRunner processRunner;
   late CommandRunner<void> runner;
   late String javaFormatPath;
 
   setUp(() {
     fileSystem = MemoryFileSystem();
+    mockPlatform = MockPlatform();
     packagesDir = createPackagesDirectory(fileSystem: fileSystem);
     processRunner = RecordingProcessRunner();
-    final FormatCommand analyzeCommand =
-        FormatCommand(packagesDir, processRunner: processRunner);
+    final FormatCommand analyzeCommand = FormatCommand(
+      packagesDir,
+      processRunner: processRunner,
+      platform: mockPlatform,
+    );
 
     // Create the java formatter file that the command checks for, to avoid
     // a download.
-    javaFormatPath = p.join(p.dirname(p.fromUri(io.Platform.script)),
+    path = analyzeCommand.path;
+    javaFormatPath = path.join(path.dirname(path.fromUri(mockPlatform.script)),
         'google-java-format-1.3-all-deps.jar');
     fileSystem.file(javaFormatPath).createSync(recursive: true);
 
@@ -42,7 +49,7 @@
   List<String> _getAbsolutePaths(
       Directory package, List<String> relativePaths) {
     return relativePaths
-        .map((String path) => p.join(package.path, path))
+        .map((String relativePath) => path.join(package.path, relativePath))
         .toList();
   }
 
diff --git a/script/tool/test/java_test_command_test.dart b/script/tool/test/java_test_command_test.dart
index 9ae9597..13e0e7f 100644
--- a/script/tool/test/java_test_command_test.dart
+++ b/script/tool/test/java_test_command_test.dart
@@ -10,7 +10,6 @@
 import 'package:flutter_plugin_tools/src/common/core.dart';
 import 'package:flutter_plugin_tools/src/common/plugin_utils.dart';
 import 'package:flutter_plugin_tools/src/java_test_command.dart';
-import 'package:path/path.dart' as p;
 import 'package:test/test.dart';
 
 import 'mocks.dart';
@@ -19,16 +18,21 @@
 void main() {
   group('$JavaTestCommand', () {
     late FileSystem fileSystem;
+    late MockPlatform mockPlatform;
     late Directory packagesDir;
     late CommandRunner<void> runner;
     late RecordingProcessRunner processRunner;
 
     setUp(() {
       fileSystem = MemoryFileSystem();
+      mockPlatform = MockPlatform();
       packagesDir = createPackagesDirectory(fileSystem: fileSystem);
       processRunner = RecordingProcessRunner();
-      final JavaTestCommand command =
-          JavaTestCommand(packagesDir, processRunner: processRunner);
+      final JavaTestCommand command = JavaTestCommand(
+        packagesDir,
+        processRunner: processRunner,
+        platform: mockPlatform,
+      );
 
       runner =
           CommandRunner<void>('java_test_test', 'Test for $JavaTestCommand');
@@ -50,13 +54,16 @@
 
       await runCapturingPrint(runner, <String>['java-test']);
 
+      final Directory androidFolder =
+          plugin.childDirectory('example').childDirectory('android');
+
       expect(
         processRunner.recordedCalls,
         orderedEquals(<ProcessCall>[
           ProcessCall(
-            p.join(plugin.path, 'example/android/gradlew'),
+            androidFolder.childFile('gradlew').path,
             const <String>['testDebugUnitTest', '--info'],
-            p.join(plugin.path, 'example/android'),
+            androidFolder.path,
           ),
         ]),
       );
@@ -77,13 +84,16 @@
 
       await runCapturingPrint(runner, <String>['java-test']);
 
+      final Directory androidFolder =
+          plugin.childDirectory('example').childDirectory('android');
+
       expect(
         processRunner.recordedCalls,
         orderedEquals(<ProcessCall>[
           ProcessCall(
-            p.join(plugin.path, 'example/android/gradlew'),
+            androidFolder.childFile('gradlew').path,
             const <String>['testDebugUnitTest', '--info'],
-            p.join(plugin.path, 'example/android'),
+            androidFolder.path,
           ),
         ]),
       );
diff --git a/script/tool/test/lint_podspecs_command_test.dart b/script/tool/test/lint_podspecs_command_test.dart
index 1236ec0..51a4e62 100644
--- a/script/tool/test/lint_podspecs_command_test.dart
+++ b/script/tool/test/lint_podspecs_command_test.dart
@@ -9,7 +9,6 @@
 import 'package:file/memory.dart';
 import 'package:flutter_plugin_tools/src/common/core.dart';
 import 'package:flutter_plugin_tools/src/lint_podspecs_command.dart';
-import 'package:path/path.dart' as p;
 import 'package:test/test.dart';
 
 import 'mocks.dart';
@@ -24,7 +23,7 @@
     late RecordingProcessRunner processRunner;
 
     setUp(() {
-      fileSystem = MemoryFileSystem();
+      fileSystem = MemoryFileSystem(style: FileSystemStyle.posix);
       packagesDir = createPackagesDirectory(fileSystem: fileSystem);
 
       mockPlatform = MockPlatform(isMacOS: true);
@@ -94,7 +93,10 @@
               <String>[
                 'lib',
                 'lint',
-                p.join(plugin1Dir.path, 'ios', 'plugin1.podspec'),
+                plugin1Dir
+                    .childDirectory('ios')
+                    .childFile('plugin1.podspec')
+                    .path,
                 '--configuration=Debug',
                 '--skip-tests',
                 '--use-modular-headers',
@@ -106,7 +108,10 @@
               <String>[
                 'lib',
                 'lint',
-                p.join(plugin1Dir.path, 'ios', 'plugin1.podspec'),
+                plugin1Dir
+                    .childDirectory('ios')
+                    .childFile('plugin1.podspec')
+                    .path,
                 '--configuration=Debug',
                 '--skip-tests',
                 '--use-modular-headers',
@@ -136,7 +141,7 @@
               <String>[
                 'lib',
                 'lint',
-                p.join(plugin1Dir.path, 'plugin1.podspec'),
+                plugin1Dir.childFile('plugin1.podspec').path,
                 '--configuration=Debug',
                 '--skip-tests',
                 '--use-modular-headers',
@@ -149,7 +154,7 @@
               <String>[
                 'lib',
                 'lint',
-                p.join(plugin1Dir.path, 'plugin1.podspec'),
+                plugin1Dir.childFile('plugin1.podspec').path,
                 '--configuration=Debug',
                 '--skip-tests',
                 '--use-modular-headers',
diff --git a/script/tool/test/list_command_test.dart b/script/tool/test/list_command_test.dart
index 836d066..488fc9b 100644
--- a/script/tool/test/list_command_test.dart
+++ b/script/tool/test/list_command_test.dart
@@ -8,18 +8,22 @@
 import 'package:flutter_plugin_tools/src/list_command.dart';
 import 'package:test/test.dart';
 
+import 'mocks.dart';
 import 'util.dart';
 
 void main() {
   group('$ListCommand', () {
     late FileSystem fileSystem;
+    late MockPlatform mockPlatform;
     late Directory packagesDir;
     late CommandRunner<void> runner;
 
     setUp(() {
       fileSystem = MemoryFileSystem();
+      mockPlatform = MockPlatform();
       packagesDir = createPackagesDirectory(fileSystem: fileSystem);
-      final ListCommand command = ListCommand(packagesDir);
+      final ListCommand command =
+          ListCommand(packagesDir, platform: mockPlatform);
 
       runner = CommandRunner<void>('list_test', 'Test for $ListCommand');
       runner.addCommand(command);
diff --git a/script/tool/test/mocks.dart b/script/tool/test/mocks.dart
index 02b0065..0dcdedd 100644
--- a/script/tool/test/mocks.dart
+++ b/script/tool/test/mocks.dart
@@ -10,10 +10,25 @@
 import 'package:platform/platform.dart';
 
 class MockPlatform extends Mock implements Platform {
-  MockPlatform({this.isMacOS = false});
+  MockPlatform({
+    this.isLinux = false,
+    this.isMacOS = false,
+    this.isWindows = false,
+  });
+
+  @override
+  bool isLinux;
 
   @override
   bool isMacOS;
+
+  @override
+  bool isWindows;
+
+  @override
+  Uri get script => isWindows
+      ? Uri.file(r'C:\foo\bar', windows: true)
+      : Uri.file('/foo/bar', windows: false);
 }
 
 class MockProcess extends Mock implements io.Process {
diff --git a/script/tool/test/publish_check_command_test.dart b/script/tool/test/publish_check_command_test.dart
index 5140316..11de9f0 100644
--- a/script/tool/test/publish_check_command_test.dart
+++ b/script/tool/test/publish_check_command_test.dart
@@ -21,16 +21,21 @@
 void main() {
   group('$PublishCheckProcessRunner tests', () {
     FileSystem fileSystem;
+    late MockPlatform mockPlatform;
     late Directory packagesDir;
     late PublishCheckProcessRunner processRunner;
     late CommandRunner<void> runner;
 
     setUp(() {
       fileSystem = MemoryFileSystem();
+      mockPlatform = MockPlatform();
       packagesDir = createPackagesDirectory(fileSystem: fileSystem);
       processRunner = PublishCheckProcessRunner();
-      final PublishCheckCommand publishCheckCommand =
-          PublishCheckCommand(packagesDir, processRunner: processRunner);
+      final PublishCheckCommand publishCheckCommand = PublishCheckCommand(
+        packagesDir,
+        processRunner: processRunner,
+        platform: mockPlatform,
+      );
 
       runner = CommandRunner<void>(
         'publish_check_command',
@@ -339,12 +344,16 @@
       });
       expect(hasError, isTrue);
 
-      expect(output.first, r'''
+      expect(output.first, contains(r'''
 {
   "status": "error",
   "humanMessage": [
     "\n============================================================\n|| Running for no_publish_a\n============================================================\n",
-    "Failed to parse `pubspec.yaml` at /packages/no_publish_a/pubspec.yaml: ParsedYamlException: line 1, column 1: Not a map\n  ╷\n1 │ bad-yaml\n  │ ^^^^^^^^\n  ╵}",
+    "Failed to parse `pubspec.yaml` at /packages/no_publish_a/pubspec.yaml: ParsedYamlException:'''));
+      // This is split into two checks since the details of the YamlException
+      // aren't controlled by this package, so asserting its exact format would
+      // make the test fragile to irrelevant changes in those details.
+      expect(output.first, contains(r'''
     "no pubspec",
     "\n============================================================\n|| Running for no_publish_b\n============================================================\n",
     "url https://pub.dev/packages/no_publish_b.json",
@@ -356,7 +365,7 @@
     "  no_publish_a",
     "See above for full details."
   ]
-}''');
+}'''));
     });
   });
 }
diff --git a/script/tool/test/publish_plugin_command_test.dart b/script/tool/test/publish_plugin_command_test.dart
index 497579b..c7df819 100644
--- a/script/tool/test/publish_plugin_command_test.dart
+++ b/script/tool/test/publish_plugin_command_test.dart
@@ -16,6 +16,7 @@
 import 'package:http/http.dart' as http;
 import 'package:http/testing.dart';
 import 'package:mockito/mockito.dart';
+import 'package:platform/platform.dart';
 import 'package:test/test.dart';
 
 import 'mocks.dart';
@@ -1091,7 +1092,7 @@
       {Directory? workingDirectory}) async {
     /// Never actually publish anything. Start is always and only used for this
     /// since it returns something we can route stdin through.
-    assert(executable == 'flutter' &&
+    assert(executable == getFlutterCommand(const LocalPlatform()) &&
         args.isNotEmpty &&
         args[0] == 'pub' &&
         args[1] == 'publish');
diff --git a/script/tool/test/pubspec_check_command_test.dart b/script/tool/test/pubspec_check_command_test.dart
index 9e633e2..177ed7f 100644
--- a/script/tool/test/pubspec_check_command_test.dart
+++ b/script/tool/test/pubspec_check_command_test.dart
@@ -9,6 +9,7 @@
 import 'package:flutter_plugin_tools/src/pubspec_check_command.dart';
 import 'package:test/test.dart';
 
+import 'mocks.dart';
 import 'util.dart';
 
 void main() {
@@ -16,15 +17,20 @@
     late CommandRunner<void> runner;
     late RecordingProcessRunner processRunner;
     late FileSystem fileSystem;
+    late MockPlatform mockPlatform;
     late Directory packagesDir;
 
     setUp(() {
       fileSystem = MemoryFileSystem();
+      mockPlatform = MockPlatform();
       packagesDir = fileSystem.currentDirectory.childDirectory('packages');
       createPackagesDirectory(parentDir: packagesDir.parent);
       processRunner = RecordingProcessRunner();
-      final PubspecCheckCommand command =
-          PubspecCheckCommand(packagesDir, processRunner: processRunner);
+      final PubspecCheckCommand command = PubspecCheckCommand(
+        packagesDir,
+        processRunner: processRunner,
+        platform: mockPlatform,
+      );
 
       runner = CommandRunner<void>(
           'pubspec_check_command', 'Test for pubspec_check_command');
diff --git a/script/tool/test/test_command_test.dart b/script/tool/test/test_command_test.dart
index ac0ac4b..503e24d 100644
--- a/script/tool/test/test_command_test.dart
+++ b/script/tool/test/test_command_test.dart
@@ -10,6 +10,7 @@
 import 'package:flutter_plugin_tools/src/common/core.dart';
 import 'package:flutter_plugin_tools/src/common/plugin_utils.dart';
 import 'package:flutter_plugin_tools/src/test_command.dart';
+import 'package:platform/platform.dart';
 import 'package:test/test.dart';
 
 import 'mocks.dart';
@@ -18,16 +19,21 @@
 void main() {
   group('$TestCommand', () {
     late FileSystem fileSystem;
+    late Platform mockPlatform;
     late Directory packagesDir;
     late CommandRunner<void> runner;
     late RecordingProcessRunner processRunner;
 
     setUp(() {
       fileSystem = MemoryFileSystem();
+      mockPlatform = MockPlatform();
       packagesDir = createPackagesDirectory(fileSystem: fileSystem);
       processRunner = RecordingProcessRunner();
-      final TestCommand command =
-          TestCommand(packagesDir, processRunner: processRunner);
+      final TestCommand command = TestCommand(
+        packagesDir,
+        processRunner: processRunner,
+        platform: mockPlatform,
+      );
 
       runner = CommandRunner<void>('test_test', 'Test for $TestCommand');
       runner.addCommand(command);
@@ -44,10 +50,10 @@
       expect(
         processRunner.recordedCalls,
         orderedEquals(<ProcessCall>[
-          ProcessCall(
-              'flutter', const <String>['test', '--color'], plugin1Dir.path),
-          ProcessCall(
-              'flutter', const <String>['test', '--color'], plugin2Dir.path),
+          ProcessCall(getFlutterCommand(mockPlatform),
+              const <String>['test', '--color'], plugin1Dir.path),
+          ProcessCall(getFlutterCommand(mockPlatform),
+              const <String>['test', '--color'], plugin2Dir.path),
         ]),
       );
     });
@@ -58,7 +64,9 @@
       createFakePlugin('plugin2', packagesDir,
           extraFiles: <String>['test/empty_test.dart']);
 
-      processRunner.mockProcessesForExecutable['flutter'] = <io.Process>[
+      processRunner
+              .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] =
+          <io.Process>[
         MockProcess.failing(), // plugin 1 test
         MockProcess.succeeding(), // plugin 2 test
       ];
@@ -88,8 +96,8 @@
       expect(
         processRunner.recordedCalls,
         orderedEquals(<ProcessCall>[
-          ProcessCall(
-              'flutter', const <String>['test', '--color'], plugin2Dir.path),
+          ProcessCall(getFlutterCommand(mockPlatform),
+              const <String>['test', '--color'], plugin2Dir.path),
         ]),
       );
     });
@@ -107,7 +115,7 @@
         processRunner.recordedCalls,
         orderedEquals(<ProcessCall>[
           ProcessCall(
-              'flutter',
+              getFlutterCommand(mockPlatform),
               const <String>['test', '--color', '--enable-experiment=exp1'],
               pluginDir.path),
           ProcessCall('dart', const <String>['pub', 'get'], packageDir.path),
@@ -183,7 +191,7 @@
         processRunner.recordedCalls,
         orderedEquals(<ProcessCall>[
           ProcessCall(
-              'flutter',
+              getFlutterCommand(mockPlatform),
               const <String>['test', '--color', '--platform=chrome'],
               pluginDir.path),
         ]),
@@ -203,7 +211,7 @@
         processRunner.recordedCalls,
         orderedEquals(<ProcessCall>[
           ProcessCall(
-              'flutter',
+              getFlutterCommand(mockPlatform),
               const <String>['test', '--color', '--enable-experiment=exp1'],
               pluginDir.path),
           ProcessCall('dart', const <String>['pub', 'get'], packageDir.path),
diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart
index b65b1fc..1984a25 100644
--- a/script/tool/test/util.dart
+++ b/script/tool/test/util.dart
@@ -14,8 +14,14 @@
 import 'package:flutter_plugin_tools/src/common/process_runner.dart';
 import 'package:meta/meta.dart';
 import 'package:path/path.dart' as p;
+import 'package:platform/platform.dart';
 import 'package:quiver/collection.dart';
 
+/// Returns the exe name that command will use when running Flutter on
+/// [platform].
+String getFlutterCommand(Platform platform) =>
+    platform.isWindows ? 'flutter.bat' : 'flutter';
+
 /// Creates a packages directory in the given location.
 ///
 /// If [parentDir] is set the packages directory will be created there,
diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart
index 6fbed9c..587de1a 100644
--- a/script/tool/test/version_check_command_test.dart
+++ b/script/tool/test/version_check_command_test.dart
@@ -18,6 +18,7 @@
 import 'package:test/test.dart';
 
 import 'common/plugin_command_test.mocks.dart';
+import 'mocks.dart';
 import 'util.dart';
 
 void testAllowedVersion(
@@ -46,6 +47,7 @@
   const String indentation = '  ';
   group('$VersionCheckCommand', () {
     FileSystem fileSystem;
+    late MockPlatform mockPlatform;
     late Directory packagesDir;
     late CommandRunner<void> runner;
     late RecordingProcessRunner processRunner;
@@ -55,6 +57,7 @@
 
     setUp(() {
       fileSystem = MemoryFileSystem();
+      mockPlatform = MockPlatform();
       packagesDir = createPackagesDirectory(fileSystem: fileSystem);
       gitDirCommands = <List<String>>[];
       gitShowResponses = <String, String>{};
@@ -80,7 +83,7 @@
       });
       processRunner = RecordingProcessRunner();
       final VersionCheckCommand command = VersionCheckCommand(packagesDir,
-          processRunner: processRunner, gitDir: gitDir);
+          processRunner: processRunner, platform: mockPlatform, gitDir: gitDir);
 
       runner = CommandRunner<void>(
           'version_check_command', 'Test for $VersionCheckCommand');
diff --git a/script/tool/test/xctest_command_test.dart b/script/tool/test/xctest_command_test.dart
index 10329b1..aa6d23f 100644
--- a/script/tool/test/xctest_command_test.dart
+++ b/script/tool/test/xctest_command_test.dart
@@ -90,16 +90,18 @@
 
   group('test xctest_command', () {
     late FileSystem fileSystem;
+    late MockPlatform mockPlatform;
     late Directory packagesDir;
     late CommandRunner<void> runner;
     late RecordingProcessRunner processRunner;
 
     setUp(() {
       fileSystem = MemoryFileSystem();
+      mockPlatform = MockPlatform(isMacOS: true);
       packagesDir = createPackagesDirectory(fileSystem: fileSystem);
       processRunner = RecordingProcessRunner();
-      final XCTestCommand command =
-          XCTestCommand(packagesDir, processRunner: processRunner);
+      final XCTestCommand command = XCTestCommand(packagesDir,
+          processRunner: processRunner, platform: mockPlatform);
 
       runner = CommandRunner<void>('xctest_command', 'Test for xctest_command');
       runner.addCommand(command);