This repository has been archived by the owner on Feb 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[flutter_plugin_tool] Add custom-test command (#5058)
- Loading branch information
1 parent
04f64ec
commit fef72e0
Showing
5 changed files
with
363 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// 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:file/file.dart'; | ||
import 'package:platform/platform.dart'; | ||
|
||
import 'common/package_looping_command.dart'; | ||
import 'common/process_runner.dart'; | ||
import 'common/repository_package.dart'; | ||
|
||
const String _scriptName = 'run_tests.dart'; | ||
const String _legacyScriptName = 'run_tests.sh'; | ||
|
||
/// A command to run custom, package-local tests on packages. | ||
/// | ||
/// This is an escape hatch for adding tests that this tooling doesn't support. | ||
/// It should be used sparingly; prefer instead to add functionality to this | ||
/// tooling to eliminate the need for bespoke tests. | ||
class CustomTestCommand extends PackageLoopingCommand { | ||
/// Creates a custom test command instance. | ||
CustomTestCommand( | ||
Directory packagesDir, { | ||
ProcessRunner processRunner = const ProcessRunner(), | ||
Platform platform = const LocalPlatform(), | ||
}) : super(packagesDir, processRunner: processRunner, platform: platform); | ||
|
||
@override | ||
final String name = 'custom-test'; | ||
|
||
@override | ||
final String description = 'Runs package-specific custom tests defined in ' | ||
'a package\'s tool/$_scriptName file.\n\n' | ||
'This command requires "dart" to be in your path.'; | ||
|
||
@override | ||
Future<PackageResult> runForPackage(RepositoryPackage package) async { | ||
final File script = | ||
package.directory.childDirectory('tool').childFile(_scriptName); | ||
final File legacyScript = package.directory.childFile(_legacyScriptName); | ||
String? customSkipReason; | ||
bool ranTests = false; | ||
|
||
// Run the custom Dart script if presest. | ||
if (script.existsSync()) { | ||
final int exitCode = await processRunner.runAndStream( | ||
'dart', <String>['run', 'tool/$_scriptName'], | ||
workingDir: package.directory); | ||
if (exitCode != 0) { | ||
return PackageResult.fail(); | ||
} | ||
ranTests = true; | ||
} | ||
|
||
// Run the legacy script if present. | ||
if (legacyScript.existsSync()) { | ||
if (platform.isWindows) { | ||
customSkipReason = '$_legacyScriptName is not supported on Windows. ' | ||
'Please migrate to $_scriptName.'; | ||
} else { | ||
final int exitCode = await processRunner.runAndStream( | ||
legacyScript.path, <String>[], | ||
workingDir: package.directory); | ||
if (exitCode != 0) { | ||
return PackageResult.fail(); | ||
} | ||
ranTests = true; | ||
} | ||
} | ||
|
||
if (!ranTests) { | ||
return PackageResult.skip(customSkipReason ?? 'No custom tests'); | ||
} | ||
|
||
return PackageResult.success(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,281 @@ | ||
// 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/custom_test_command.dart'; | ||
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 CommandRunner<void> runner; | ||
|
||
group('posix', () { | ||
setUp(() { | ||
fileSystem = MemoryFileSystem(); | ||
mockPlatform = MockPlatform(); | ||
packagesDir = createPackagesDirectory(fileSystem: fileSystem); | ||
processRunner = RecordingProcessRunner(); | ||
final CustomTestCommand analyzeCommand = CustomTestCommand( | ||
packagesDir, | ||
processRunner: processRunner, | ||
platform: mockPlatform, | ||
); | ||
|
||
runner = CommandRunner<void>( | ||
'custom_test_command', 'Test for custom_test_command'); | ||
runner.addCommand(analyzeCommand); | ||
}); | ||
|
||
test('runs both new and legacy when both are present', () async { | ||
final Directory package = | ||
createFakePlugin('a_package', packagesDir, extraFiles: <String>[ | ||
'tool/run_tests.dart', | ||
'run_tests.sh', | ||
]); | ||
|
||
final List<String> output = | ||
await runCapturingPrint(runner, <String>['custom-test']); | ||
|
||
expect( | ||
processRunner.recordedCalls, | ||
containsAll(<ProcessCall>[ | ||
ProcessCall(package.childFile('run_tests.sh').path, | ||
const <String>[], package.path), | ||
ProcessCall('dart', const <String>['run', 'tool/run_tests.dart'], | ||
package.path), | ||
])); | ||
|
||
expect( | ||
output, | ||
containsAllInOrder(<Matcher>[ | ||
contains('Ran for 1 package(s)'), | ||
])); | ||
}); | ||
|
||
test('runs when only new is present', () async { | ||
final Directory package = createFakePlugin('a_package', packagesDir, | ||
extraFiles: <String>['tool/run_tests.dart']); | ||
|
||
final List<String> output = | ||
await runCapturingPrint(runner, <String>['custom-test']); | ||
|
||
expect( | ||
processRunner.recordedCalls, | ||
containsAll(<ProcessCall>[ | ||
ProcessCall('dart', const <String>['run', 'tool/run_tests.dart'], | ||
package.path), | ||
])); | ||
|
||
expect( | ||
output, | ||
containsAllInOrder(<Matcher>[ | ||
contains('Ran for 1 package(s)'), | ||
])); | ||
}); | ||
|
||
test('runs when only legacy is present', () async { | ||
final Directory package = createFakePlugin('a_package', packagesDir, | ||
extraFiles: <String>['run_tests.sh']); | ||
|
||
final List<String> output = | ||
await runCapturingPrint(runner, <String>['custom-test']); | ||
|
||
expect( | ||
processRunner.recordedCalls, | ||
containsAll(<ProcessCall>[ | ||
ProcessCall(package.childFile('run_tests.sh').path, | ||
const <String>[], package.path), | ||
])); | ||
|
||
expect( | ||
output, | ||
containsAllInOrder(<Matcher>[ | ||
contains('Ran for 1 package(s)'), | ||
])); | ||
}); | ||
|
||
test('skips when neither is present', () async { | ||
createFakePlugin('a_package', packagesDir); | ||
|
||
final List<String> output = | ||
await runCapturingPrint(runner, <String>['custom-test']); | ||
|
||
expect(processRunner.recordedCalls, isEmpty); | ||
|
||
expect( | ||
output, | ||
containsAllInOrder(<Matcher>[ | ||
contains('Skipped 1 package(s)'), | ||
])); | ||
}); | ||
|
||
test('fails if new fails', () async { | ||
createFakePlugin('a_package', packagesDir, extraFiles: <String>[ | ||
'tool/run_tests.dart', | ||
'run_tests.sh', | ||
]); | ||
|
||
processRunner.mockProcessesForExecutable['dart'] = <io.Process>[ | ||
MockProcess(exitCode: 1), | ||
]; | ||
|
||
Error? commandError; | ||
final List<String> output = await runCapturingPrint( | ||
runner, <String>['custom-test'], errorHandler: (Error e) { | ||
commandError = e; | ||
}); | ||
|
||
expect(commandError, isA<ToolExit>()); | ||
expect( | ||
output, | ||
containsAllInOrder(<Matcher>[ | ||
contains('The following packages had errors:'), | ||
contains('a_package') | ||
])); | ||
}); | ||
|
||
test('fails if legacy fails', () async { | ||
final Directory package = | ||
createFakePlugin('a_package', packagesDir, extraFiles: <String>[ | ||
'tool/run_tests.dart', | ||
'run_tests.sh', | ||
]); | ||
|
||
processRunner.mockProcessesForExecutable[ | ||
package.childFile('run_tests.sh').path] = <io.Process>[ | ||
MockProcess(exitCode: 1), | ||
]; | ||
|
||
Error? commandError; | ||
final List<String> output = await runCapturingPrint( | ||
runner, <String>['custom-test'], errorHandler: (Error e) { | ||
commandError = e; | ||
}); | ||
|
||
expect(commandError, isA<ToolExit>()); | ||
expect( | ||
output, | ||
containsAllInOrder(<Matcher>[ | ||
contains('The following packages had errors:'), | ||
contains('a_package') | ||
])); | ||
}); | ||
}); | ||
|
||
group('Windows', () { | ||
setUp(() { | ||
fileSystem = MemoryFileSystem(style: FileSystemStyle.windows); | ||
mockPlatform = MockPlatform(isWindows: true); | ||
packagesDir = createPackagesDirectory(fileSystem: fileSystem); | ||
processRunner = RecordingProcessRunner(); | ||
final CustomTestCommand analyzeCommand = CustomTestCommand( | ||
packagesDir, | ||
processRunner: processRunner, | ||
platform: mockPlatform, | ||
); | ||
|
||
runner = CommandRunner<void>( | ||
'custom_test_command', 'Test for custom_test_command'); | ||
runner.addCommand(analyzeCommand); | ||
}); | ||
|
||
test('runs new and skips old when both are present', () async { | ||
final Directory package = | ||
createFakePlugin('a_package', packagesDir, extraFiles: <String>[ | ||
'tool/run_tests.dart', | ||
'run_tests.sh', | ||
]); | ||
|
||
final List<String> output = | ||
await runCapturingPrint(runner, <String>['custom-test']); | ||
|
||
expect( | ||
processRunner.recordedCalls, | ||
containsAll(<ProcessCall>[ | ||
ProcessCall('dart', const <String>['run', 'tool/run_tests.dart'], | ||
package.path), | ||
])); | ||
|
||
expect( | ||
output, | ||
containsAllInOrder(<Matcher>[ | ||
contains('Ran for 1 package(s)'), | ||
])); | ||
}); | ||
|
||
test('runs when only new is present', () async { | ||
final Directory package = createFakePlugin('a_package', packagesDir, | ||
extraFiles: <String>['tool/run_tests.dart']); | ||
|
||
final List<String> output = | ||
await runCapturingPrint(runner, <String>['custom-test']); | ||
|
||
expect( | ||
processRunner.recordedCalls, | ||
containsAll(<ProcessCall>[ | ||
ProcessCall('dart', const <String>['run', 'tool/run_tests.dart'], | ||
package.path), | ||
])); | ||
|
||
expect( | ||
output, | ||
containsAllInOrder(<Matcher>[ | ||
contains('Ran for 1 package(s)'), | ||
])); | ||
}); | ||
|
||
test('skips package when only legacy is present', () async { | ||
createFakePlugin('a_package', packagesDir, | ||
extraFiles: <String>['run_tests.sh']); | ||
|
||
final List<String> output = | ||
await runCapturingPrint(runner, <String>['custom-test']); | ||
|
||
expect(processRunner.recordedCalls, isEmpty); | ||
|
||
expect( | ||
output, | ||
containsAllInOrder(<Matcher>[ | ||
contains('run_tests.sh is not supported on Windows'), | ||
contains('Skipped 1 package(s)'), | ||
])); | ||
}); | ||
|
||
test('fails if new fails', () async { | ||
createFakePlugin('a_package', packagesDir, extraFiles: <String>[ | ||
'tool/run_tests.dart', | ||
'run_tests.sh', | ||
]); | ||
|
||
processRunner.mockProcessesForExecutable['dart'] = <io.Process>[ | ||
MockProcess(exitCode: 1), | ||
]; | ||
|
||
Error? commandError; | ||
final List<String> output = await runCapturingPrint( | ||
runner, <String>['custom-test'], errorHandler: (Error e) { | ||
commandError = e; | ||
}); | ||
|
||
expect(commandError, isA<ToolExit>()); | ||
expect( | ||
output, | ||
containsAllInOrder(<Matcher>[ | ||
contains('The following packages had errors:'), | ||
contains('a_package') | ||
])); | ||
}); | ||
}); | ||
} |