Files
packages/script/tool/test/format_command_test.dart
engine-flutter-autoroll 5cda4c673a Manual roll Flutter from ead6b0d17c89 to 6bba08cbcc51 (37 revisions) (#7809)
Manual roll Flutter from ead6b0d17c89 to 6bba08cbcc51 (37 revisions)

Manual roll requested by stuartmorgan@google.com

ead6b0d17c...6bba08cbcc

2024-10-01 a.a.ustinoff@gmail.com  Feat: Add opportunity to change CupertinoTextField suffix alignment (flutter/flutter#154601)
2024-10-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from da28db8ff41d to 3fdb546bf595 (2 revisions) (flutter/flutter#155993)
2024-10-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from 6b21b796cc94 to da28db8ff41d (1 revision) (flutter/flutter#155985)
2024-10-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from 259f56c6e91b to 6b21b796cc94 (1 revision) (flutter/flutter#155983)
2024-10-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from 6ee04ed763d9 to 259f56c6e91b (1 revision) (flutter/flutter#155981)
2024-10-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from bfb6dddb2b30 to 6ee04ed763d9 (3 revisions) (flutter/flutter#155978)
2024-10-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from e61bc853acb2 to bfb6dddb2b30 (8 revisions) (flutter/flutter#155967)
2024-10-01 katelovett@google.com Disable flaky menu test  (flutter/flutter#155968)
2024-10-01 robert.ancell@canonical.com Fix crash in Linux platform channel example. (flutter/flutter#155735)
2024-09-30 polinach@google.com Fix leak in input_decorator [prod-leak-fix] (flutter/flutter#155885)
2024-09-30 rmolivares@renzo-olivares.dev Move platform specific text selection behavior out of styled TextField classes (flutter/flutter#155774)
2024-09-30 engine-flutter-autoroll@skia.org Roll Flutter Engine from b466a0dd7834 to e61bc853acb2 (5 revisions) (flutter/flutter#155952)
2024-09-30 rmolivares@renzo-olivares.dev `RenderParagraph`s `_SelectableFragment.boundingBoxes` should consider max line height (flutter/flutter#155892)
2024-09-30 engine-flutter-autoroll@skia.org Roll Packages from 0321757447bf to 27c98537ccac (3 revisions) (flutter/flutter#155945)
2024-09-30 engine-flutter-autoroll@skia.org Roll Flutter Engine from f4507e7a4beb to b466a0dd7834 (1 revision) (flutter/flutter#155944)
2024-09-30 andrewrkolos@gmail.com when `ResidentRunner.tryInitLogReader` fails, only log warning on Android (flutter/flutter#155800)
2024-09-30 gordin.dan@gmail.com Move FlutterLogo from material to widget (flutter/flutter#155864)
2024-09-30 lizhuo.huang@outlook.com fix: support android 15 16k page size for template plugin_ffi  (flutter/flutter#155508)
2024-09-30 engine-flutter-autoroll@skia.org Roll Flutter Engine from daf126b38b8f to f4507e7a4beb (1 revision) (flutter/flutter#155932)
2024-09-30 engine-flutter-autoroll@skia.org Roll Flutter Engine from 734205fbcd62 to daf126b38b8f (1 revision) (flutter/flutter#155929)
2024-09-30 engine-flutter-autoroll@skia.org Roll Flutter Engine from 338f09c4ea72 to 734205fbcd62 (1 revision) (flutter/flutter#155923)
2024-09-30 engine-flutter-autoroll@skia.org Roll Flutter Engine from 897f5caffe2d to 338f09c4ea72 (2 revisions) (flutter/flutter#155917)
2024-09-30 engine-flutter-autoroll@skia.org Roll Flutter Engine from 569abc4044b8 to 897f5caffe2d (1 revision) (flutter/flutter#155912)
2024-09-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from c4784aa7eade to 569abc4044b8 (2 revisions) (flutter/flutter#155894)
2024-09-28 engine-flutter-autoroll@skia.org Roll Flutter Engine from ff4541712df4 to c4784aa7eade (2 revisions) (flutter/flutter#155889)
2024-09-28 50643541+Mairramer@users.noreply.github.com Fixes column text width calculation in CupertinoDatePicker (flutter/flutter#151128)
2024-09-28 engine-flutter-autoroll@skia.org Roll Flutter Engine from 380fd814448c to ff4541712df4 (1 revision) (flutter/flutter#155886)
2024-09-28 tessertaha@gmail.com Optimize `Overlay` sample to avoid overflow (flutter/flutter#155861)
2024-09-28 engine-flutter-autoroll@skia.org Roll Flutter Engine from f3b11bcd9c37 to 380fd814448c (1 revision) (flutter/flutter#155876)
2024-09-28 engine-flutter-autoroll@skia.org Roll Flutter Engine from 9c8e5cb226e4 to f3b11bcd9c37 (3 revisions) (flutter/flutter#155865)
2024-09-28 engine-flutter-autoroll@skia.org Roll Flutter Engine from f9e4ed28f103 to 9c8e5cb226e4 (1 revision) (flutter/flutter#155857)
2024-09-27 engine-flutter-autoroll@skia.org Roll Flutter Engine from f21f2b232b8a to f9e4ed28f103 (2 revisions) (flutter/flutter#155855)
2024-09-27 45459898+RamonFarizel@users.noreply.github.com Add  magnificationScale to CupertinoMagnifier for Zoom Effect (flutter/flutter#155276)
2024-09-27 iNoles@users.noreply.github.com Fix typo on theme_data (flutter/flutter#155644)
2024-09-27 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7c603de2dca7 to f21f2b232b8a (6 revisions) (flutter/flutter#155843)
2024-09-27 34871572+gmackall@users.noreply.github.com Turn the packages roller bot back on (flutter/flutter#155842)
2024-09-27 engine-flutter-autoroll@skia.org Roll Packages from f38b780f530f to 0321757447bf (4 revisions) (flutter/flutter#155832)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC camillesimon@google.com,stuartmorgan@google.com on the revert to ensure that a human
is aware of the problem.

...
2024-10-08 09:43:38 +00:00

987 lines
31 KiB
Dart

// 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 '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;
late String kotlinFormatPath;
setUp(() {
fileSystem = MemoryFileSystem();
mockPlatform = MockPlatform();
packagesDir = createPackagesDirectory(fileSystem: fileSystem);
processRunner = RecordingProcessRunner();
analyzeCommand = FormatCommand(
packagesDir,
processRunner: processRunner,
platform: mockPlatform,
);
// Create the Java and Kotlin formatter files 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);
kotlinFormatPath = path.join(
path.dirname(path.fromUri(mockPlatform.script)),
'ktfmt-0.46-jar-with-dependencies.jar');
fileSystem.file(kotlinFormatPath).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]
/// or [createFakePackage] with name [packageName] 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 packageName, int count) {
final int padding = 99 -
packageName.length -
1 - // the path separator after the package 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(
'dart',
<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(
'dart',
<String>[
'format',
...getPackagesDirRelativePaths(plugin, formattedFiles)
],
packagesDir.path),
]));
});
test('fails if dart 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['dart'] = <FakeProcessInfo>[
FakeProcessInfo(MockProcess(exitCode: 1), <String>['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 Dart files: exit code 1.'),
]));
});
test('skips dart if --no-dart flag is provided', () async {
const List<String> files = <String>[
'lib/a.dart',
];
createFakePlugin('a_plugin', packagesDir, extraFiles: files);
await runCapturingPrint(runner, <String>['format', '--no-dart']);
expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
});
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'] = <FakeProcessInfo>[
FakeProcessInfo(MockProcess(exitCode: 1), <String>['-version'])
];
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-path.'),
]));
});
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'] = <FakeProcessInfo>[
FakeProcessInfo(
MockProcess(), <String>['-version']), // check for working java
FakeProcessInfo(MockProcess(exitCode: 1), <String>['-jar']), // 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-path 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=/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('skips Java if --no-java flag is provided', () async {
const List<String> files = <String>[
'android/src/main/java/io/flutter/plugins/a_plugin/a.java',
];
createFakePlugin('a_plugin', packagesDir, extraFiles: files);
await runCapturingPrint(runner, <String>['format', '--no-java']);
expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
});
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'] =
<FakeProcessInfo>[FakeProcessInfo(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-path.'),
]));
});
test('falls back to working clang-format in the path', () async {
const List<String> files = <String>[
'linux/foo_plugin.cc',
'macos/Classes/Foo.h',
];
final RepositoryPackage plugin = createFakePlugin(
'a_plugin',
packagesDir,
extraFiles: files,
);
processRunner.mockProcessesForExecutable['clang-format'] =
<FakeProcessInfo>[FakeProcessInfo(MockProcess(exitCode: 1))];
processRunner.mockProcessesForExecutable['which'] = <FakeProcessInfo>[
FakeProcessInfo(
MockProcess(
stdout:
'/usr/local/bin/clang-format\n/path/to/working-clang-format'),
<String>['-a', 'clang-format'])
];
processRunner.mockProcessesForExecutable['/usr/local/bin/clang-format'] =
<FakeProcessInfo>[FakeProcessInfo(MockProcess(exitCode: 1))];
await runCapturingPrint(runner, <String>['format']);
expect(
processRunner.recordedCalls,
containsAll(<ProcessCall>[
const ProcessCall(
'/path/to/working-clang-format', <String>['--version'], null),
ProcessCall(
'/path/to/working-clang-format',
<String>[
'-i',
'--style=file',
...getPackagesDirRelativePaths(plugin, files)
],
packagesDir.path),
]));
});
test('honors --clang-format-path 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=/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'] =
<FakeProcessInfo>[
FakeProcessInfo(MockProcess(),
<String>['--version']), // check for working clang-format
FakeProcessInfo(MockProcess(exitCode: 1), <String>['-i']), // 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 clang-format if --no-clang-format flag is provided', () async {
const List<String> files = <String>[
'linux/foo_plugin.cc',
];
createFakePlugin('a_plugin', packagesDir, extraFiles: files);
await runCapturingPrint(runner, <String>['format', '--no-clang-format']);
expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
});
group('kotlin-format', () {
test('formats .kt files', () async {
const List<String> files = <String>[
'android/src/main/kotlin/io/flutter/plugins/a_plugin/a.kt',
'android/src/main/kotlin/io/flutter/plugins/a_plugin/b.kt',
];
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',
kotlinFormatPath,
...getPackagesDirRelativePaths(plugin, files)
],
packagesDir.path),
]));
});
test('fails if Kotlin formatter fails', () async {
const List<String> files = <String>[
'android/src/main/kotlin/io/flutter/plugins/a_plugin/a.kt',
'android/src/main/kotlin/io/flutter/plugins/a_plugin/b.kt',
];
createFakePlugin('a_plugin', packagesDir, extraFiles: files);
processRunner.mockProcessesForExecutable['java'] = <FakeProcessInfo>[
FakeProcessInfo(
MockProcess(), <String>['-version']), // check for working java
FakeProcessInfo(MockProcess(exitCode: 1), <String>['-jar']), // 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 Kotlin files: exit code 1.'),
]));
});
test('skips Kotlin if --no-kotlin flag is provided', () async {
const List<String> files = <String>[
'android/src/main/kotlin/io/flutter/plugins/a_plugin/a.kt',
];
createFakePlugin('a_plugin', packagesDir, extraFiles: files);
await runCapturingPrint(runner, <String>['format', '--no-kotlin']);
expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
});
});
group('swift-format', () {
test('formats Swift if --swift-format flag is provided', () async {
const List<String> files = <String>[
'macos/foo.swift',
];
final RepositoryPackage plugin = createFakePlugin(
'a_plugin',
packagesDir,
extraFiles: files,
);
await runCapturingPrint(runner, <String>[
'format',
'--swift',
'--swift-format-path=/path/to/swift-format'
]);
expect(
processRunner.recordedCalls,
orderedEquals(<ProcessCall>[
const ProcessCall(
'/path/to/swift-format',
<String>['--version'],
null,
),
ProcessCall(
'/path/to/swift-format',
<String>['-i', ...getPackagesDirRelativePaths(plugin, files)],
packagesDir.path,
),
ProcessCall(
'/path/to/swift-format',
<String>[
'lint',
'--parallel',
'--strict',
...getPackagesDirRelativePaths(plugin, files),
],
packagesDir.path,
),
]));
});
test('skips Swift if --no-swift flag is provided', () async {
const List<String> files = <String>[
'macos/foo.swift',
];
createFakePlugin(
'a_plugin',
packagesDir,
extraFiles: files,
);
await runCapturingPrint(runner, <String>['format', '--no-swift']);
expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[]));
});
test('fails with a clear message if swift-format is not in the path',
() async {
const List<String> files = <String>[
'macos/foo.swift',
];
createFakePlugin('a_plugin', packagesDir, extraFiles: files);
processRunner.mockProcessesForExecutable['swift-format'] =
<FakeProcessInfo>[
FakeProcessInfo(MockProcess(exitCode: 1), <String>['--version']),
];
Error? commandError;
final List<String> output = await runCapturingPrint(
runner, <String>['format', '--swift'], errorHandler: (Error e) {
commandError = e;
});
expect(commandError, isA<ToolExit>());
expect(
output,
containsAllInOrder(<Matcher>[
contains(
'Unable to run "swift-format". Make sure that it is in your path, or '
'provide a full path with --swift-format-path.'),
]));
});
test('fails if swift-format lint fails', () async {
const List<String> files = <String>[
'macos/foo.swift',
];
createFakePlugin('a_plugin', packagesDir, extraFiles: files);
processRunner.mockProcessesForExecutable['swift-format'] =
<FakeProcessInfo>[
FakeProcessInfo(MockProcess(),
<String>['--version']), // check for working swift-format
FakeProcessInfo(MockProcess(), <String>['-i']),
FakeProcessInfo(MockProcess(exitCode: 1), <String>[
'lint',
'--parallel',
'--strict',
]),
];
Error? commandError;
final List<String> output = await runCapturingPrint(runner, <String>[
'format',
'--swift',
'--swift-format-path=swift-format'
], errorHandler: (Error e) {
commandError = e;
});
expect(commandError, isA<ToolExit>());
expect(
output,
containsAllInOrder(<Matcher>[
contains('Failed to lint Swift files: exit code 1.'),
]));
});
test('fails if swift-format fails', () async {
const List<String> files = <String>[
'macos/foo.swift',
];
createFakePlugin('a_plugin', packagesDir, extraFiles: files);
processRunner.mockProcessesForExecutable['swift-format'] =
<FakeProcessInfo>[
FakeProcessInfo(MockProcess(),
<String>['--version']), // check for working swift-format
FakeProcessInfo(MockProcess(exitCode: 1), <String>['-i']),
];
Error? commandError;
final List<String> output = await runCapturingPrint(runner, <String>[
'format',
'--swift',
'--swift-format-path=swift-format'
], errorHandler: (Error e) {
commandError = e;
});
expect(commandError, isA<ToolExit>());
expect(
output,
containsAllInOrder(<Matcher>[
contains('Failed to format Swift 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(
'dart',
<String>[
'format',
...getPackagesDirRelativePaths(plugin, dartFiles)
],
packagesDir.path),
ProcessCall(
'java',
<String>[
'-jar',
javaFormatPath,
'--replace',
...getPackagesDirRelativePaths(plugin, javaFiles)
],
packagesDir.path),
]));
});
test('skips GeneratedPluginRegistrant.swift', () async {
const String sourceFile = 'macos/Classes/Foo.swift';
final RepositoryPackage plugin = createFakePlugin(
'a_plugin',
packagesDir,
extraFiles: <String>[
sourceFile,
'example/macos/Flutter/GeneratedPluginRegistrant.swift',
],
);
await runCapturingPrint(runner, <String>[
'format',
'--swift',
'--swift-format-path=/path/to/swift-format'
]);
expect(
processRunner.recordedCalls,
orderedEquals(<ProcessCall>[
const ProcessCall(
'/path/to/swift-format',
<String>['--version'],
null,
),
ProcessCall(
'/path/to/swift-format',
<String>[
'-i',
...getPackagesDirRelativePaths(plugin, <String>[sourceFile])
],
packagesDir.path,
),
ProcessCall(
'/path/to/swift-format',
<String>[
'lint',
'--parallel',
'--strict',
...getPackagesDirRelativePaths(plugin, <String>[sourceFile]),
],
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'] = <FakeProcessInfo>[
FakeProcessInfo(
MockProcess(stdout: changedFilePath), <String>['ls-files']),
];
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),
// Ensure the error message links to instructions.
contains(
'https://github.com/flutter/packages/blob/main/script/tool/README.md#format-code'),
contains('patch -p1 <<DONE'),
]));
// Ensure that both packages and third_party/packages are checked.
final Directory thirdPartyDir = packagesDir.parent
.childDirectory('third_party')
.childDirectory('packages');
expect(
processRunner.recordedCalls,
containsAllInOrder(<ProcessCall>[
ProcessCall(
'git',
<String>[
'ls-files',
'--modified',
packagesDir.path,
thirdPartyDir.path
],
packagesDir.parent.path,
),
]));
});
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'] = <FakeProcessInfo>[
FakeProcessInfo(MockProcess(exitCode: 1), <String>['ls-files'])
];
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'] = <FakeProcessInfo>[
FakeProcessInfo(
MockProcess(stdout: changedFilePath), <String>['ls-files']),
FakeProcessInfo(MockProcess(exitCode: 1), <String>['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(
'dart',
<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(
'dart',
<String>[
'format',
'$pluginName/$extraFile',
],
packagesDir.path),
));
});
}