mirror of
https://github.com/flutter/packages.git
synced 2025-05-24 12:16:44 +08:00

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
352 lines
10 KiB
Dart
352 lines
10 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 '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/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 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,
|
|
platform: mockPlatform,
|
|
);
|
|
|
|
// Create the java formatter file that the command checks for, to avoid
|
|
// a download.
|
|
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);
|
|
});
|
|
|
|
List<String> _getAbsolutePaths(
|
|
Directory package, List<String> relativePaths) {
|
|
return relativePaths
|
|
.map((String relativePath) => path.join(package.path, relativePath))
|
|
.toList();
|
|
}
|
|
|
|
test('formats .dart files', () async {
|
|
const List<String> files = <String>[
|
|
'lib/a.dart',
|
|
'lib/src/b.dart',
|
|
'lib/src/c.dart',
|
|
];
|
|
final Directory pluginDir = createFakePlugin(
|
|
'a_plugin',
|
|
packagesDir,
|
|
extraFiles: files,
|
|
);
|
|
|
|
await runCapturingPrint(runner, <String>['format']);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
'flutter',
|
|
<String>['format', ..._getAbsolutePaths(pluginDir, files)],
|
|
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['flutter'] = <io.Process>[
|
|
MockProcess.failing()
|
|
];
|
|
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 Directory pluginDir = createFakePlugin(
|
|
'a_plugin',
|
|
packagesDir,
|
|
extraFiles: files,
|
|
);
|
|
|
|
await runCapturingPrint(runner, <String>['format']);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
'java',
|
|
<String>[
|
|
'-jar',
|
|
javaFormatPath,
|
|
'--replace',
|
|
..._getAbsolutePaths(pluginDir, files)
|
|
],
|
|
packagesDir.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'] = <io.Process>[
|
|
MockProcess.failing()
|
|
];
|
|
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('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 Directory pluginDir = createFakePlugin(
|
|
'a_plugin',
|
|
packagesDir,
|
|
extraFiles: files,
|
|
);
|
|
|
|
await runCapturingPrint(runner, <String>['format']);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
'clang-format',
|
|
<String>[
|
|
'-i',
|
|
'--style=Google',
|
|
..._getAbsolutePaths(pluginDir, 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.failing()
|
|
];
|
|
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 Directory pluginDir = 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=Google',
|
|
..._getAbsolutePaths(pluginDir, clangFiles)
|
|
],
|
|
packagesDir.path),
|
|
ProcessCall(
|
|
'flutter',
|
|
<String>['format', ..._getAbsolutePaths(pluginDir, dartFiles)],
|
|
packagesDir.path),
|
|
ProcessCall(
|
|
'java',
|
|
<String>[
|
|
'-jar',
|
|
javaFormatPath,
|
|
'--replace',
|
|
..._getAbsolutePaths(pluginDir, javaFiles)
|
|
],
|
|
packagesDir.path),
|
|
]));
|
|
});
|
|
|
|
test('fails if files are changed with --file-on-change', () 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.succeeding(),
|
|
];
|
|
const String changedFilePath = 'packages/a_plugin/linux/foo_plugin.cc';
|
|
processRunner.resultStdout = 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.failing()
|
|
];
|
|
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);
|
|
|
|
processRunner.mockProcessesForExecutable['git'] = <io.Process>[
|
|
MockProcess.succeeding(), // ls-files
|
|
MockProcess.failing(), // diff
|
|
];
|
|
const String changedFilePath = 'packages/a_plugin/linux/foo_plugin.cc';
|
|
processRunner.resultStdout = 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('Unable to determine diff.'),
|
|
]));
|
|
});
|
|
}
|