| // Copyright 2013 The Flutter Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | import 'dart:io' as io; | 
 |  | 
 | import 'package:args/command_runner.dart'; | 
 | import 'package:file/file.dart'; | 
 | import 'package:file/memory.dart'; | 
 | import 'package:flutter_plugin_tools/src/common/core.dart'; | 
 | import 'package:flutter_plugin_tools/src/common/file_utils.dart'; | 
 | import 'package:flutter_plugin_tools/src/format_command.dart'; | 
 | import 'package:path/path.dart' as p; | 
 | import 'package:test/test.dart'; | 
 |  | 
 | import 'mocks.dart'; | 
 | import 'util.dart'; | 
 |  | 
 | void main() { | 
 |   late FileSystem fileSystem; | 
 |   late MockPlatform mockPlatform; | 
 |   late Directory packagesDir; | 
 |   late RecordingProcessRunner processRunner; | 
 |   late FormatCommand analyzeCommand; | 
 |   late CommandRunner<void> runner; | 
 |   late String javaFormatPath; | 
 |  | 
 |   setUp(() { | 
 |     fileSystem = MemoryFileSystem(); | 
 |     mockPlatform = MockPlatform(); | 
 |     packagesDir = createPackagesDirectory(fileSystem: fileSystem); | 
 |     processRunner = RecordingProcessRunner(); | 
 |     analyzeCommand = FormatCommand( | 
 |       packagesDir, | 
 |       processRunner: processRunner, | 
 |       platform: mockPlatform, | 
 |     ); | 
 |  | 
 |     // Create the java formatter file that the command checks for, to avoid | 
 |     // a download. | 
 |     final p.Context 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); | 
 |  | 
 |     runner = CommandRunner<void>('format_command', 'Test for format_command'); | 
 |     runner.addCommand(analyzeCommand); | 
 |   }); | 
 |  | 
 |   /// Returns a modified version of a list of [relativePaths] that are relative | 
 |   /// to [package] to instead be relative to [packagesDir]. | 
 |   List<String> _getPackagesDirRelativePaths( | 
 |       RepositoryPackage package, List<String> relativePaths) { | 
 |     final p.Context path = analyzeCommand.path; | 
 |     final String relativeBase = | 
 |         path.relative(package.path, from: packagesDir.path); | 
 |     return relativePaths | 
 |         .map((String relativePath) => path.join(relativeBase, relativePath)) | 
 |         .toList(); | 
 |   } | 
 |  | 
 |   /// Returns a list of [count] relative paths to pass to [createFakePlugin] | 
 |   /// with name [pluginName] such that each path will be 99 characters long | 
 |   /// relative to [packagesDir]. | 
 |   /// | 
 |   /// This is for each of testing batching, since it means each file will | 
 |   /// consume 100 characters of the batch length. | 
 |   List<String> _get99CharacterPathExtraFiles(String pluginName, int count) { | 
 |     final int padding = 99 - | 
 |         pluginName.length - | 
 |         1 - // the path separator after the plugin name | 
 |         1 - // the path separator after the padding | 
 |         10; // the file name | 
 |     const int filenameBase = 10000; | 
 |  | 
 |     final p.Context path = analyzeCommand.path; | 
 |     return <String>[ | 
 |       for (int i = filenameBase; i < filenameBase + count; ++i) | 
 |         path.join('a' * padding, '$i.dart'), | 
 |     ]; | 
 |   } | 
 |  | 
 |   test('formats .dart files', () async { | 
 |     const List<String> files = <String>[ | 
 |       'lib/a.dart', | 
 |       'lib/src/b.dart', | 
 |       'lib/src/c.dart', | 
 |     ]; | 
 |     final RepositoryPackage plugin = createFakePlugin( | 
 |       'a_plugin', | 
 |       packagesDir, | 
 |       extraFiles: files, | 
 |     ); | 
 |  | 
 |     await runCapturingPrint(runner, <String>['format']); | 
 |  | 
 |     expect( | 
 |         processRunner.recordedCalls, | 
 |         orderedEquals(<ProcessCall>[ | 
 |           ProcessCall( | 
 |               getFlutterCommand(mockPlatform), | 
 |               <String>[ | 
 |                 'format', | 
 |                 ..._getPackagesDirRelativePaths(plugin, files) | 
 |               ], | 
 |               packagesDir.path), | 
 |         ])); | 
 |   }); | 
 |  | 
 |   test('does not format .dart files with pragma', () async { | 
 |     const List<String> formattedFiles = <String>[ | 
 |       'lib/a.dart', | 
 |       'lib/src/b.dart', | 
 |       'lib/src/c.dart', | 
 |     ]; | 
 |     const String unformattedFile = 'lib/src/d.dart'; | 
 |     final RepositoryPackage plugin = createFakePlugin( | 
 |       'a_plugin', | 
 |       packagesDir, | 
 |       extraFiles: <String>[ | 
 |         ...formattedFiles, | 
 |         unformattedFile, | 
 |       ], | 
 |     ); | 
 |  | 
 |     final p.Context posixContext = p.posix; | 
 |     childFileWithSubcomponents( | 
 |             plugin.directory, posixContext.split(unformattedFile)) | 
 |         .writeAsStringSync( | 
 |             '// copyright bla bla\n// This file is hand-formatted.\ncode...'); | 
 |  | 
 |     await runCapturingPrint(runner, <String>['format']); | 
 |  | 
 |     expect( | 
 |         processRunner.recordedCalls, | 
 |         orderedEquals(<ProcessCall>[ | 
 |           ProcessCall( | 
 |               getFlutterCommand(mockPlatform), | 
 |               <String>[ | 
 |                 'format', | 
 |                 ..._getPackagesDirRelativePaths(plugin, formattedFiles) | 
 |               ], | 
 |               packagesDir.path), | 
 |         ])); | 
 |   }); | 
 |  | 
 |   test('fails if flutter format fails', () async { | 
 |     const List<String> files = <String>[ | 
 |       'lib/a.dart', | 
 |       'lib/src/b.dart', | 
 |       'lib/src/c.dart', | 
 |     ]; | 
 |     createFakePlugin('a_plugin', packagesDir, extraFiles: files); | 
 |  | 
 |     processRunner.mockProcessesForExecutable[getFlutterCommand(mockPlatform)] = | 
 |         <io.Process>[MockProcess(exitCode: 1)]; | 
 |     Error? commandError; | 
 |     final List<String> output = await runCapturingPrint( | 
 |         runner, <String>['format'], errorHandler: (Error e) { | 
 |       commandError = e; | 
 |     }); | 
 |  | 
 |     expect(commandError, isA<ToolExit>()); | 
 |     expect( | 
 |         output, | 
 |         containsAllInOrder(<Matcher>[ | 
 |           contains('Failed to format Dart files: exit code 1.'), | 
 |         ])); | 
 |   }); | 
 |  | 
 |   test('formats .java files', () async { | 
 |     const List<String> files = <String>[ | 
 |       'android/src/main/java/io/flutter/plugins/a_plugin/a.java', | 
 |       'android/src/main/java/io/flutter/plugins/a_plugin/b.java', | 
 |     ]; | 
 |     final RepositoryPackage plugin = createFakePlugin( | 
 |       'a_plugin', | 
 |       packagesDir, | 
 |       extraFiles: files, | 
 |     ); | 
 |  | 
 |     await runCapturingPrint(runner, <String>['format']); | 
 |  | 
 |     expect( | 
 |         processRunner.recordedCalls, | 
 |         orderedEquals(<ProcessCall>[ | 
 |           const ProcessCall('java', <String>['-version'], null), | 
 |           ProcessCall( | 
 |               'java', | 
 |               <String>[ | 
 |                 '-jar', | 
 |                 javaFormatPath, | 
 |                 '--replace', | 
 |                 ..._getPackagesDirRelativePaths(plugin, files) | 
 |               ], | 
 |               packagesDir.path), | 
 |         ])); | 
 |   }); | 
 |  | 
 |   test('fails with a clear message if Java is not in the path', () async { | 
 |     const List<String> files = <String>[ | 
 |       'android/src/main/java/io/flutter/plugins/a_plugin/a.java', | 
 |       'android/src/main/java/io/flutter/plugins/a_plugin/b.java', | 
 |     ]; | 
 |     createFakePlugin('a_plugin', packagesDir, extraFiles: files); | 
 |  | 
 |     processRunner.mockProcessesForExecutable['java'] = <io.Process>[ | 
 |       MockProcess(exitCode: 1) | 
 |     ]; | 
 |     Error? commandError; | 
 |     final List<String> output = await runCapturingPrint( | 
 |         runner, <String>['format'], errorHandler: (Error e) { | 
 |       commandError = e; | 
 |     }); | 
 |  | 
 |     expect(commandError, isA<ToolExit>()); | 
 |     expect( | 
 |         output, | 
 |         containsAllInOrder(<Matcher>[ | 
 |           contains( | 
 |               'Unable to run "java". Make sure that it is in your path, or ' | 
 |               'provide a full path with --java.'), | 
 |         ])); | 
 |   }); | 
 |  | 
 |   test('fails if Java formatter fails', () async { | 
 |     const List<String> files = <String>[ | 
 |       'android/src/main/java/io/flutter/plugins/a_plugin/a.java', | 
 |       'android/src/main/java/io/flutter/plugins/a_plugin/b.java', | 
 |     ]; | 
 |     createFakePlugin('a_plugin', packagesDir, extraFiles: files); | 
 |  | 
 |     processRunner.mockProcessesForExecutable['java'] = <io.Process>[ | 
 |       MockProcess(), // check for working java | 
 |       MockProcess(exitCode: 1), // format | 
 |     ]; | 
 |     Error? commandError; | 
 |     final List<String> output = await runCapturingPrint( | 
 |         runner, <String>['format'], errorHandler: (Error e) { | 
 |       commandError = e; | 
 |     }); | 
 |  | 
 |     expect(commandError, isA<ToolExit>()); | 
 |     expect( | 
 |         output, | 
 |         containsAllInOrder(<Matcher>[ | 
 |           contains('Failed to format Java files: exit code 1.'), | 
 |         ])); | 
 |   }); | 
 |  | 
 |   test('honors --java flag', () async { | 
 |     const List<String> files = <String>[ | 
 |       'android/src/main/java/io/flutter/plugins/a_plugin/a.java', | 
 |       'android/src/main/java/io/flutter/plugins/a_plugin/b.java', | 
 |     ]; | 
 |     final RepositoryPackage plugin = createFakePlugin( | 
 |       'a_plugin', | 
 |       packagesDir, | 
 |       extraFiles: files, | 
 |     ); | 
 |  | 
 |     await runCapturingPrint(runner, <String>['format', '--java=/path/to/java']); | 
 |  | 
 |     expect( | 
 |         processRunner.recordedCalls, | 
 |         orderedEquals(<ProcessCall>[ | 
 |           const ProcessCall('/path/to/java', <String>['--version'], null), | 
 |           ProcessCall( | 
 |               '/path/to/java', | 
 |               <String>[ | 
 |                 '-jar', | 
 |                 javaFormatPath, | 
 |                 '--replace', | 
 |                 ..._getPackagesDirRelativePaths(plugin, files) | 
 |               ], | 
 |               packagesDir.path), | 
 |         ])); | 
 |   }); | 
 |  | 
 |   test('formats c-ish files', () async { | 
 |     const List<String> files = <String>[ | 
 |       'ios/Classes/Foo.h', | 
 |       'ios/Classes/Foo.m', | 
 |       'linux/foo_plugin.cc', | 
 |       'macos/Classes/Foo.h', | 
 |       'macos/Classes/Foo.mm', | 
 |       'windows/foo_plugin.cpp', | 
 |     ]; | 
 |     final RepositoryPackage plugin = createFakePlugin( | 
 |       'a_plugin', | 
 |       packagesDir, | 
 |       extraFiles: files, | 
 |     ); | 
 |  | 
 |     await runCapturingPrint(runner, <String>['format']); | 
 |  | 
 |     expect( | 
 |         processRunner.recordedCalls, | 
 |         orderedEquals(<ProcessCall>[ | 
 |           const ProcessCall('clang-format', <String>['--version'], null), | 
 |           ProcessCall( | 
 |               'clang-format', | 
 |               <String>[ | 
 |                 '-i', | 
 |                 '--style=file', | 
 |                 ..._getPackagesDirRelativePaths(plugin, files) | 
 |               ], | 
 |               packagesDir.path), | 
 |         ])); | 
 |   }); | 
 |  | 
 |   test('fails with a clear message if clang-format is not in the path', | 
 |       () async { | 
 |     const List<String> files = <String>[ | 
 |       'linux/foo_plugin.cc', | 
 |       'macos/Classes/Foo.h', | 
 |     ]; | 
 |     createFakePlugin('a_plugin', packagesDir, extraFiles: files); | 
 |  | 
 |     processRunner.mockProcessesForExecutable['clang-format'] = <io.Process>[ | 
 |       MockProcess(exitCode: 1) | 
 |     ]; | 
 |     Error? commandError; | 
 |     final List<String> output = await runCapturingPrint( | 
 |         runner, <String>['format'], errorHandler: (Error e) { | 
 |       commandError = e; | 
 |     }); | 
 |  | 
 |     expect(commandError, isA<ToolExit>()); | 
 |     expect( | 
 |         output, | 
 |         containsAllInOrder(<Matcher>[ | 
 |           contains('Unable to run "clang-format". Make sure that it is in your ' | 
 |               'path, or provide a full path with --clang-format.'), | 
 |         ])); | 
 |   }); | 
 |  | 
 |   test('honors --clang-format flag', () async { | 
 |     const List<String> files = <String>[ | 
 |       'windows/foo_plugin.cpp', | 
 |     ]; | 
 |     final RepositoryPackage plugin = createFakePlugin( | 
 |       'a_plugin', | 
 |       packagesDir, | 
 |       extraFiles: files, | 
 |     ); | 
 |  | 
 |     await runCapturingPrint( | 
 |         runner, <String>['format', '--clang-format=/path/to/clang-format']); | 
 |  | 
 |     expect( | 
 |         processRunner.recordedCalls, | 
 |         orderedEquals(<ProcessCall>[ | 
 |           const ProcessCall( | 
 |               '/path/to/clang-format', <String>['--version'], null), | 
 |           ProcessCall( | 
 |               '/path/to/clang-format', | 
 |               <String>[ | 
 |                 '-i', | 
 |                 '--style=file', | 
 |                 ..._getPackagesDirRelativePaths(plugin, files) | 
 |               ], | 
 |               packagesDir.path), | 
 |         ])); | 
 |   }); | 
 |  | 
 |   test('fails if clang-format fails', () async { | 
 |     const List<String> files = <String>[ | 
 |       'linux/foo_plugin.cc', | 
 |       'macos/Classes/Foo.h', | 
 |     ]; | 
 |     createFakePlugin('a_plugin', packagesDir, extraFiles: files); | 
 |  | 
 |     processRunner.mockProcessesForExecutable['clang-format'] = <io.Process>[ | 
 |       MockProcess(), // check for working clang-format | 
 |       MockProcess(exitCode: 1), // format | 
 |     ]; | 
 |     Error? commandError; | 
 |     final List<String> output = await runCapturingPrint( | 
 |         runner, <String>['format'], errorHandler: (Error e) { | 
 |       commandError = e; | 
 |     }); | 
 |  | 
 |     expect(commandError, isA<ToolExit>()); | 
 |     expect( | 
 |         output, | 
 |         containsAllInOrder(<Matcher>[ | 
 |           contains( | 
 |               'Failed to format C, C++, and Objective-C files: exit code 1.'), | 
 |         ])); | 
 |   }); | 
 |  | 
 |   test('skips known non-repo files', () async { | 
 |     const List<String> skipFiles = <String>[ | 
 |       '/example/build/SomeFramework.framework/Headers/SomeFramework.h', | 
 |       '/example/Pods/APod.framework/Headers/APod.h', | 
 |       '.dart_tool/internals/foo.cc', | 
 |       '.dart_tool/internals/Bar.java', | 
 |       '.dart_tool/internals/baz.dart', | 
 |     ]; | 
 |     const List<String> clangFiles = <String>['ios/Classes/Foo.h']; | 
 |     const List<String> dartFiles = <String>['lib/a.dart']; | 
 |     const List<String> javaFiles = <String>[ | 
 |       'android/src/main/java/io/flutter/plugins/a_plugin/a.java' | 
 |     ]; | 
 |     final RepositoryPackage plugin = createFakePlugin( | 
 |       'a_plugin', | 
 |       packagesDir, | 
 |       extraFiles: <String>[ | 
 |         ...skipFiles, | 
 |         // Include some files that should be formatted to validate that it's | 
 |         // correctly filtering even when running the commands. | 
 |         ...clangFiles, | 
 |         ...dartFiles, | 
 |         ...javaFiles, | 
 |       ], | 
 |     ); | 
 |  | 
 |     await runCapturingPrint(runner, <String>['format']); | 
 |  | 
 |     expect( | 
 |         processRunner.recordedCalls, | 
 |         containsAll(<ProcessCall>[ | 
 |           ProcessCall( | 
 |               'clang-format', | 
 |               <String>[ | 
 |                 '-i', | 
 |                 '--style=file', | 
 |                 ..._getPackagesDirRelativePaths(plugin, clangFiles) | 
 |               ], | 
 |               packagesDir.path), | 
 |           ProcessCall( | 
 |               getFlutterCommand(mockPlatform), | 
 |               <String>[ | 
 |                 'format', | 
 |                 ..._getPackagesDirRelativePaths(plugin, dartFiles) | 
 |               ], | 
 |               packagesDir.path), | 
 |           ProcessCall( | 
 |               'java', | 
 |               <String>[ | 
 |                 '-jar', | 
 |                 javaFormatPath, | 
 |                 '--replace', | 
 |                 ..._getPackagesDirRelativePaths(plugin, javaFiles) | 
 |               ], | 
 |               packagesDir.path), | 
 |         ])); | 
 |   }); | 
 |  | 
 |   test('fails if files are changed with --fail-on-change', () async { | 
 |     const List<String> files = <String>[ | 
 |       'linux/foo_plugin.cc', | 
 |       'macos/Classes/Foo.h', | 
 |     ]; | 
 |     createFakePlugin('a_plugin', packagesDir, extraFiles: files); | 
 |  | 
 |     const String changedFilePath = 'packages/a_plugin/linux/foo_plugin.cc'; | 
 |     processRunner.mockProcessesForExecutable['git'] = <io.Process>[ | 
 |       MockProcess(stdout: changedFilePath), | 
 |     ]; | 
 |  | 
 |     Error? commandError; | 
 |     final List<String> output = | 
 |         await runCapturingPrint(runner, <String>['format', '--fail-on-change'], | 
 |             errorHandler: (Error e) { | 
 |       commandError = e; | 
 |     }); | 
 |  | 
 |     expect(commandError, isA<ToolExit>()); | 
 |     expect( | 
 |         output, | 
 |         containsAllInOrder(<Matcher>[ | 
 |           contains('These files are not formatted correctly'), | 
 |           contains(changedFilePath), | 
 |           contains('patch -p1 <<DONE'), | 
 |         ])); | 
 |   }); | 
 |  | 
 |   test('fails if git ls-files fails', () async { | 
 |     const List<String> files = <String>[ | 
 |       'linux/foo_plugin.cc', | 
 |       'macos/Classes/Foo.h', | 
 |     ]; | 
 |     createFakePlugin('a_plugin', packagesDir, extraFiles: files); | 
 |  | 
 |     processRunner.mockProcessesForExecutable['git'] = <io.Process>[ | 
 |       MockProcess(exitCode: 1) | 
 |     ]; | 
 |     Error? commandError; | 
 |     final List<String> output = | 
 |         await runCapturingPrint(runner, <String>['format', '--fail-on-change'], | 
 |             errorHandler: (Error e) { | 
 |       commandError = e; | 
 |     }); | 
 |  | 
 |     expect(commandError, isA<ToolExit>()); | 
 |     expect( | 
 |         output, | 
 |         containsAllInOrder(<Matcher>[ | 
 |           contains('Unable to determine changed files.'), | 
 |         ])); | 
 |   }); | 
 |  | 
 |   test('reports git diff failures', () async { | 
 |     const List<String> files = <String>[ | 
 |       'linux/foo_plugin.cc', | 
 |       'macos/Classes/Foo.h', | 
 |     ]; | 
 |     createFakePlugin('a_plugin', packagesDir, extraFiles: files); | 
 |  | 
 |     const String changedFilePath = 'packages/a_plugin/linux/foo_plugin.cc'; | 
 |     processRunner.mockProcessesForExecutable['git'] = <io.Process>[ | 
 |       MockProcess(stdout: changedFilePath), // ls-files | 
 |       MockProcess(exitCode: 1), // diff | 
 |     ]; | 
 |  | 
 |     Error? commandError; | 
 |     final List<String> output = | 
 |         await runCapturingPrint(runner, <String>['format', '--fail-on-change'], | 
 |             errorHandler: (Error e) { | 
 |       commandError = e; | 
 |     }); | 
 |  | 
 |     expect(commandError, isA<ToolExit>()); | 
 |     expect( | 
 |         output, | 
 |         containsAllInOrder(<Matcher>[ | 
 |           contains('These files are not formatted correctly'), | 
 |           contains(changedFilePath), | 
 |           contains('Unable to determine diff.'), | 
 |         ])); | 
 |   }); | 
 |  | 
 |   test('Batches moderately long file lists on Windows', () async { | 
 |     mockPlatform.isWindows = true; | 
 |  | 
 |     const String pluginName = 'a_plugin'; | 
 |     // -1 since the command itself takes some length. | 
 |     const int batchSize = (windowsCommandLineMax ~/ 100) - 1; | 
 |  | 
 |     // Make the file list one file longer than would fit in the batch. | 
 |     final List<String> batch1 = | 
 |         _get99CharacterPathExtraFiles(pluginName, batchSize + 1); | 
 |     final String extraFile = batch1.removeLast(); | 
 |  | 
 |     createFakePlugin( | 
 |       pluginName, | 
 |       packagesDir, | 
 |       extraFiles: <String>[...batch1, extraFile], | 
 |     ); | 
 |  | 
 |     await runCapturingPrint(runner, <String>['format']); | 
 |  | 
 |     // Ensure that it was batched... | 
 |     expect(processRunner.recordedCalls.length, 2); | 
 |     // ... and that the spillover into the second batch was only one file. | 
 |     expect( | 
 |         processRunner.recordedCalls, | 
 |         contains( | 
 |           ProcessCall( | 
 |               getFlutterCommand(mockPlatform), | 
 |               <String>[ | 
 |                 'format', | 
 |                 '$pluginName\\$extraFile', | 
 |               ], | 
 |               packagesDir.path), | 
 |         )); | 
 |   }); | 
 |  | 
 |   // Validates that the Windows limit--which is much lower than the limit on | 
 |   // other platforms--isn't being used on all platforms, as that would make | 
 |   // formatting slower on Linux and macOS. | 
 |   test('Does not batch moderately long file lists on non-Windows', () async { | 
 |     const String pluginName = 'a_plugin'; | 
 |     // -1 since the command itself takes some length. | 
 |     const int batchSize = (windowsCommandLineMax ~/ 100) - 1; | 
 |  | 
 |     // Make the file list one file longer than would fit in a Windows batch. | 
 |     final List<String> batch = | 
 |         _get99CharacterPathExtraFiles(pluginName, batchSize + 1); | 
 |  | 
 |     createFakePlugin( | 
 |       pluginName, | 
 |       packagesDir, | 
 |       extraFiles: batch, | 
 |     ); | 
 |  | 
 |     await runCapturingPrint(runner, <String>['format']); | 
 |  | 
 |     expect(processRunner.recordedCalls.length, 1); | 
 |   }); | 
 |  | 
 |   test('Batches extremely long file lists on non-Windows', () async { | 
 |     const String pluginName = 'a_plugin'; | 
 |     // -1 since the command itself takes some length. | 
 |     const int batchSize = (nonWindowsCommandLineMax ~/ 100) - 1; | 
 |  | 
 |     // Make the file list one file longer than would fit in the batch. | 
 |     final List<String> batch1 = | 
 |         _get99CharacterPathExtraFiles(pluginName, batchSize + 1); | 
 |     final String extraFile = batch1.removeLast(); | 
 |  | 
 |     createFakePlugin( | 
 |       pluginName, | 
 |       packagesDir, | 
 |       extraFiles: <String>[...batch1, extraFile], | 
 |     ); | 
 |  | 
 |     await runCapturingPrint(runner, <String>['format']); | 
 |  | 
 |     // Ensure that it was batched... | 
 |     expect(processRunner.recordedCalls.length, 2); | 
 |     // ... and that the spillover into the second batch was only one file. | 
 |     expect( | 
 |         processRunner.recordedCalls, | 
 |         contains( | 
 |           ProcessCall( | 
 |               getFlutterCommand(mockPlatform), | 
 |               <String>[ | 
 |                 'format', | 
 |                 '$pluginName/$extraFile', | 
 |               ], | 
 |               packagesDir.path), | 
 |         )); | 
 |   }); | 
 | } |