Unverified Commit d5c842c0 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] make analyze once an integration test (#85336)

parent 025397ae
...@@ -4,75 +4,41 @@ ...@@ -4,75 +4,41 @@
// @dart = 2.8 // @dart = 2.8
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/error_handling_io.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart'; import '../src/common.dart';
import 'package:flutter_tools/src/base/os.dart'; import 'test_utils.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/terminal.dart'; final String analyzerSeparator = platform.isWindows ? '-' : '•';
import 'package:flutter_tools/src/base/user_messages.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/analyze.dart';
import 'package:flutter_tools/src/flutter_cache.dart';
import 'package:flutter_tools/src/globals_null_migrated.dart' as globals;
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:process/process.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fakes.dart';
import '../../src/test_flutter_command_runner.dart';
final Platform _kNoColorTerminalPlatform = FakePlatform(stdoutSupportsAnsi: false);
void main() { void main() {
String analyzerSeparator;
FileSystem fileSystem;
Platform platform;
BufferLogger logger;
AnsiTerminal terminal;
ProcessManager processManager;
Directory tempDir; Directory tempDir;
String projectPath; String projectPath;
File libMain; File libMain;
Artifacts artifacts;
Future<void> runCommand({ Future<void> runCommand({
FlutterCommand command,
List<String> arguments, List<String> arguments,
List<String> statusTextContains, List<String> statusTextContains = const <String>[],
List<String> errorTextContains, List<String> errorTextContains = const <String>[],
bool toolExit = false, String exitMessageContains = '',
String exitMessageContains,
int exitCode = 0, int exitCode = 0,
}) async { }) async {
try { final ProcessResult result = await processManager.run(<String>[
await createTestCommandRunner(command).run(arguments); fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter'),
expect(toolExit, isFalse, reason: 'Expected ToolExit exception'); '--no-color',
} on ToolExit catch (e) { ...arguments,
if (!toolExit) { ], workingDirectory: projectPath);
testLogger.clear(); print(result.stdout);
rethrow; print(result.stderr);
} expect(result.exitCode, exitCode, reason: 'Expected to exit with non-zero exit code.');
if (exitMessageContains != null) { assertContains(result.stdout.toString(), statusTextContains);
expect(e.message, contains(exitMessageContains)); assertContains(result.stdout.toString(), errorTextContains);
// May not analyzer exception the `exitCode` is `null`. expect(result.stderr, contains(exitMessageContains));
expect(e.exitCode ?? 0, exitCode);
}
}
assertContains(logger.statusText, statusTextContains);
assertContains(logger.errorText, errorTextContains);
logger.clear();
} }
void _createDotPackages(String projectPath, [bool nullSafe = false]) { void _createDotPackages(String projectPath, [bool nullSafe = false]) {
final StringBuffer flutterRootUri = StringBuffer('file://'); final StringBuffer flutterRootUri = StringBuffer('file://');
final String canonicalizedFlutterRootPath = fileSystem.path.canonicalize(Cache.flutterRoot); final String canonicalizedFlutterRootPath = fileSystem.path.canonicalize(getFlutterRoot());
if (platform.isWindows) { if (platform.isWindows) {
flutterRootUri flutterRootUri
..write('/') ..write('/')
...@@ -100,7 +66,7 @@ void main() { ...@@ -100,7 +66,7 @@ void main() {
"name": "flutter_project", "name": "flutter_project",
"rootUri": "../", "rootUri": "../",
"packageUri": "lib/", "packageUri": "lib/",
"languageVersion": "${nullSafe ? "2.10" : "2.7"}" "languageVersion": "${nullSafe ? "2.12" : "2.7"}"
} }
] ]
} }
...@@ -111,33 +77,6 @@ void main() { ...@@ -111,33 +77,6 @@ void main() {
..writeAsStringSync(dotPackagesSrc); ..writeAsStringSync(dotPackagesSrc);
} }
setUpAll(() {
Cache.disableLocking();
processManager = const LocalProcessManager();
platform = const LocalPlatform();
terminal = AnsiTerminal(platform: platform, stdio: Stdio());
fileSystem = globals.localFileSystem;
logger = BufferLogger.test();
analyzerSeparator = platform.isWindows ? '-' : '•';
final OperatingSystemUtils operatingSystemUtils = FakeOperatingSystemUtils();
artifacts = CachedArtifacts(
cache: FlutterCache(
fileSystem: fileSystem,
logger: logger,
platform: platform,
osUtils: operatingSystemUtils,
),
fileSystem: fileSystem,
platform: platform,
operatingSystemUtils: operatingSystemUtils,
);
Cache.flutterRoot = Cache.defaultFlutterRoot(
fileSystem: fileSystem,
platform: platform,
userMessages: UserMessages(),
);
});
setUp(() { setUp(() {
tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analyze_once_test_1.').absolute; tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analyze_once_test_1.').absolute;
projectPath = fileSystem.path.join(tempDir.path, 'flutter_project'); projectPath = fileSystem.path.join(tempDir.path, 'flutter_project');
...@@ -155,41 +94,24 @@ void main() { ...@@ -155,41 +94,24 @@ void main() {
}); });
// Analyze in the current directory - no arguments // Analyze in the current directory - no arguments
testUsingContext('working directory', () async { testWithoutContext('working directory', () async {
await runCommand( await runCommand(
command: AnalyzeCommand(
workingDirectory: fileSystem.directory(projectPath),
fileSystem: fileSystem,
logger: logger,
platform: platform,
processManager: processManager,
terminal: terminal,
artifacts: artifacts,
),
arguments: <String>['analyze', '--no-pub'], arguments: <String>['analyze', '--no-pub'],
statusTextContains: <String>['No issues found!'], statusTextContains: <String>['No issues found!'],
); );
}); });
// Analyze a specific file outside the current directory // testWithoutContext a specific file outside the current directory
testUsingContext('passing one file throws', () async { testWithoutContext('passing one file throws', () async {
await runCommand( await runCommand(
command: AnalyzeCommand(
platform: platform,
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
terminal: terminal,
artifacts: artifacts,
),
arguments: <String>['analyze', '--no-pub', libMain.path], arguments: <String>['analyze', '--no-pub', libMain.path],
toolExit: true,
exitMessageContains: 'is not a directory', exitMessageContains: 'is not a directory',
exitCode: 1,
); );
}); });
// Analyze in the current directory - no arguments // Analyze in the current directory - no arguments
testUsingContext('working directory with errors', () async { testWithoutContext('working directory with errors', () async {
// Break the code to produce the "Avoid empty else" hint // Break the code to produce the "Avoid empty else" hint
// that is upgraded to a warning in package:flutter/analysis_options_user.yaml // that is upgraded to a warning in package:flutter/analysis_options_user.yaml
// to assert that we are using the default Flutter analysis options. // to assert that we are using the default Flutter analysis options.
...@@ -212,15 +134,6 @@ void main() { ...@@ -212,15 +134,6 @@ void main() {
// Analyze in the current directory - no arguments // Analyze in the current directory - no arguments
await runCommand( await runCommand(
command: AnalyzeCommand(
workingDirectory: fileSystem.directory(projectPath),
platform: platform,
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
terminal: terminal,
artifacts: artifacts,
),
arguments: <String>['analyze', '--no-pub'], arguments: <String>['analyze', '--no-pub'],
statusTextContains: <String>[ statusTextContains: <String>[
'Analyzing', 'Analyzing',
...@@ -230,301 +143,171 @@ void main() { ...@@ -230,301 +143,171 @@ void main() {
"warning $analyzerSeparator The parameter 'onPressed' is required", "warning $analyzerSeparator The parameter 'onPressed' is required",
], ],
exitMessageContains: '4 issues found.', exitMessageContains: '4 issues found.',
toolExit: true,
exitCode: 1, exitCode: 1,
); );
}); });
// Analyze in the current directory - no arguments // Analyze in the current directory - no arguments
testUsingContext('working directory with local options', () async { testWithoutContext('working directory with local options', () async {
// Insert an analysis_options.yaml file in the project // Insert an analysis_options.yaml file in the project
// which will trigger a lint for broken code that was inserted earlier // which will trigger a lint for broken code that was inserted earlier
final File optionsFile = fileSystem.file(fileSystem.path.join(projectPath, 'analysis_options.yaml')); final File optionsFile = fileSystem.file(fileSystem.path.join(projectPath, 'analysis_options.yaml'));
try {
optionsFile.writeAsStringSync(''' optionsFile.writeAsStringSync('''
include: package:flutter/analysis_options_user.yaml include: package:flutter/analysis_options_user.yaml
linter: linter:
rules: rules:
- only_throw_errors - only_throw_errors
'''); ''');
String source = libMain.readAsStringSync(); String source = libMain.readAsStringSync();
source = source.replaceFirst( source = source.replaceFirst(
'onPressed: _incrementCounter,', 'onPressed: _incrementCounter,',
'// onPressed: _incrementCounter,', '// onPressed: _incrementCounter,',
); );
source = source.replaceFirst( source = source.replaceFirst(
'_counter++;', '_counter++;',
'_counter++; throw "an error message";', '_counter++; throw "an error message";',
); );
libMain.writeAsStringSync(source); libMain.writeAsStringSync(source);
// Analyze in the current directory - no arguments
await runCommand(
command: AnalyzeCommand(
workingDirectory: fileSystem.directory(projectPath),
platform: platform,
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
terminal: terminal,
artifacts: artifacts,
),
arguments: <String>['analyze', '--no-pub'],
statusTextContains: <String>[
'Analyzing',
"info $analyzerSeparator The declaration '_incrementCounter' isn't",
'info $analyzerSeparator Only throw instances of classes extending either Exception or Error',
"warning $analyzerSeparator The parameter 'onPressed' is required",
],
exitMessageContains: '3 issues found.',
toolExit: true,
exitCode: 1,
);
} finally {
ErrorHandlingFileSystem.deleteIfExists(optionsFile);
}
});
testUsingContext('analyze once no duplicate issues', () async { // Analyze in the current directory - no arguments
final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analyze_once_test_2.').absolute; await runCommand(
_createDotPackages(tempDir.path); arguments: <String>['analyze', '--no-pub'],
statusTextContains: <String>[
'Analyzing',
"info $analyzerSeparator The declaration '_incrementCounter' isn't",
'info $analyzerSeparator Only throw instances of classes extending either Exception or Error',
"warning $analyzerSeparator The parameter 'onPressed' is required",
],
exitMessageContains: '3 issues found.',
exitCode: 1,
);
});
try { testWithoutContext('analyze once no duplicate issues', () async {
final File foo = fileSystem.file(fileSystem.path.join(tempDir.path, 'foo.dart')); final File foo = fileSystem.file(fileSystem.path.join(projectPath, 'foo.dart'));
foo.writeAsStringSync(''' foo.writeAsStringSync('''
import 'bar.dart'; import 'bar.dart';
void foo() => bar(); void foo() => bar();
'''); ''');
final File bar = fileSystem.file(fileSystem.path.join(tempDir.path, 'bar.dart')); final File bar = fileSystem.file(fileSystem.path.join(projectPath, 'bar.dart'));
bar.writeAsStringSync(''' bar.writeAsStringSync('''
import 'dart:async'; // unused import 'dart:async'; // unused
void bar() { void bar() {
} }
'''); ''');
// Analyze in the current directory - no arguments // Analyze in the current directory - no arguments
await runCommand( await runCommand(
command: AnalyzeCommand( arguments: <String>['analyze', '--no-pub'],
workingDirectory: tempDir, statusTextContains: <String>[
platform: platform, 'Analyzing',
fileSystem: fileSystem, ],
logger: logger, exitMessageContains: '1 issue found.',
processManager: processManager, exitCode: 1
terminal: terminal, );
artifacts: artifacts,
),
arguments: <String>['analyze', '--no-pub'],
statusTextContains: <String>[
'Analyzing',
],
exitMessageContains: '1 issue found.',
toolExit: true,
exitCode: 1
);
} finally {
tryToDelete(tempDir);
}
}); });
testUsingContext('analyze once returns no issues when source is error-free', () async { testWithoutContext('analyze once returns no issues when source is error-free', () async {
const String contents = ''' const String contents = '''
StringBuffer bar = StringBuffer('baz'); StringBuffer bar = StringBuffer('baz');
'''; ''';
final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analyze_once_test_3.');
_createDotPackages(tempDir.path); fileSystem.directory(projectPath).childFile('main.dart').writeAsStringSync(contents);
await runCommand(
tempDir.childFile('main.dart').writeAsStringSync(contents); arguments: <String>['analyze', '--no-pub'],
try { statusTextContains: <String>['No issues found!'],
await runCommand( );
command: AnalyzeCommand(
workingDirectory: fileSystem.directory(tempDir),
platform: _kNoColorTerminalPlatform,
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
terminal: terminal,
artifacts: artifacts,
),
arguments: <String>['analyze', '--no-pub'],
statusTextContains: <String>['No issues found!'],
);
} finally {
tryToDelete(tempDir);
}
}); });
testUsingContext('analyze once returns no issues for todo comments', () async { testWithoutContext('analyze once returns no issues for todo comments', () async {
const String contents = ''' const String contents = '''
// TODO(foobar): // TODO(foobar):
StringBuffer bar = StringBuffer('baz'); StringBuffer bar = StringBuffer('baz');
'''; ''';
final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analyze_once_test_4.');
_createDotPackages(tempDir.path);
tempDir.childFile('main.dart').writeAsStringSync(contents);
try {
await runCommand(
command: AnalyzeCommand(
workingDirectory: fileSystem.directory(tempDir),
platform: _kNoColorTerminalPlatform,
terminal: terminal,
processManager: processManager,
logger: logger,
fileSystem: fileSystem,
artifacts: artifacts,
),
arguments: <String>['analyze', '--no-pub'],
statusTextContains: <String>['No issues found!'],
);
} finally {
tryToDelete(tempDir);
}
});
testUsingContext('analyze once with default options has info issue finally exit code 1.', () async { fileSystem.directory(projectPath).childFile('main.dart').writeAsStringSync(contents);
final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analyze_once_default_options_info_issue_exit_code_1.'); await runCommand(
_createDotPackages(tempDir.path); arguments: <String>['analyze', '--no-pub'],
statusTextContains: <String>['No issues found!'],
);
});
testWithoutContext('analyze once with default options has info issue finally exit code 1.', () async {
const String infoSourceCode = ''' const String infoSourceCode = '''
int analyze() {} int analyze() {}
'''; ''';
tempDir.childFile('main.dart').writeAsStringSync(infoSourceCode); fileSystem.directory(projectPath).childFile('main.dart').writeAsStringSync(infoSourceCode);
try { await runCommand(
await runCommand( arguments: <String>['analyze', '--no-pub'],
command: AnalyzeCommand( statusTextContains: <String>[
workingDirectory: fileSystem.directory(tempDir), 'info',
platform: _kNoColorTerminalPlatform, 'missing_return',
terminal: terminal, ],
processManager: processManager, exitMessageContains: '1 issue found.',
logger: logger, exitCode: 1,
fileSystem: fileSystem, );
artifacts: artifacts,
),
arguments: <String>['analyze', '--no-pub'],
statusTextContains: <String>[
'info',
'missing_return',
],
exitMessageContains: '1 issue found.',
toolExit: true,
exitCode: 1,
);
} finally {
tryToDelete(tempDir);
}
}); });
testUsingContext('analyze once with no-fatal-infos has info issue finally exit code 0.', () async { testWithoutContext('analyze once with no-fatal-infos has info issue finally exit code 0.', () async {
final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analyze_once_no_fatal_infos_info_issue_exit_code_0.');
_createDotPackages(tempDir.path);
const String infoSourceCode = ''' const String infoSourceCode = '''
int analyze() {} int analyze() {}
'''; ''';
tempDir.childFile('main.dart').writeAsStringSync(infoSourceCode); fileSystem.directory(projectPath).childFile('main.dart').writeAsStringSync(infoSourceCode);
try { await runCommand(
await runCommand( arguments: <String>['analyze', '--no-pub', '--no-fatal-infos'],
command: AnalyzeCommand( statusTextContains: <String>[
workingDirectory: fileSystem.directory(tempDir), 'info',
platform: _kNoColorTerminalPlatform, 'missing_return',
terminal: terminal, ],
processManager: processManager, exitMessageContains: '1 issue found.',
logger: logger, exitCode: 0,
fileSystem: fileSystem, );
artifacts: artifacts,
),
arguments: <String>['analyze', '--no-pub', '--no-fatal-infos'],
statusTextContains: <String>[
'info',
'missing_return',
],
exitMessageContains: '1 issue found.',
toolExit: true,
exitCode: 0,
);
} finally {
tryToDelete(tempDir);
}
}); });
testUsingContext('analyze once only fatal-warnings has info issue finally exit code 0.', () async { testWithoutContext('analyze once only fatal-warnings has info issue finally exit code 0.', () async {
final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analyze_once_only_fatal_warnings_info_issue_exit_code_0.');
_createDotPackages(tempDir.path);
const String infoSourceCode = ''' const String infoSourceCode = '''
int analyze() {} int analyze() {}
'''; ''';
tempDir.childFile('main.dart').writeAsStringSync(infoSourceCode); fileSystem.directory(projectPath).childFile('main.dart').writeAsStringSync(infoSourceCode);
try { await runCommand(
await runCommand( arguments: <String>['analyze', '--no-pub', '--fatal-warnings', '--no-fatal-infos'],
command: AnalyzeCommand( statusTextContains: <String>[
workingDirectory: fileSystem.directory(tempDir), 'info',
platform: _kNoColorTerminalPlatform, 'missing_return',
terminal: terminal, ],
processManager: processManager, exitMessageContains: '1 issue found.',
logger: logger, exitCode: 0,
fileSystem: fileSystem, );
artifacts: artifacts,
),
arguments: <String>['analyze', '--no-pub', '--fatal-warnings', '--no-fatal-infos'],
statusTextContains: <String>[
'info',
'missing_return',
],
exitMessageContains: '1 issue found.',
toolExit: true,
exitCode: 0,
);
} finally {
tryToDelete(tempDir);
}
}); });
testUsingContext('analyze once only fatal-infos has warning issue finally exit code 1.', () async { testWithoutContext('analyze once only fatal-infos has warning issue finally exit code 1.', () async {
final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analyze_once_only_fatal_infos_warning_issue_exit_code_1.');
_createDotPackages(tempDir.path);
const String warningSourceCode = ''' const String warningSourceCode = '''
int analyze() {} int analyze() {}
'''; ''';
final File optionsFile = fileSystem.file(fileSystem.path.join(tempDir.path, 'analysis_options.yaml')); final File optionsFile = fileSystem.file(fileSystem.path.join(projectPath, 'analysis_options.yaml'));
optionsFile.writeAsStringSync(''' optionsFile.writeAsStringSync('''
analyzer: analyzer:
errors: errors:
missing_return: warning missing_return: warning
'''); ''');
tempDir.childFile('main.dart').writeAsStringSync(warningSourceCode); fileSystem.directory(projectPath).childFile('main.dart').writeAsStringSync(warningSourceCode);
try { await runCommand(
await runCommand( arguments: <String>['analyze','--no-pub', '--fatal-infos', '--no-fatal-warnings'],
command: AnalyzeCommand( statusTextContains: <String>[
workingDirectory: fileSystem.directory(tempDir), 'warning',
platform: _kNoColorTerminalPlatform, 'missing_return',
terminal: terminal, ],
processManager: processManager, exitMessageContains: '1 issue found.',
logger: logger, exitCode: 1,
fileSystem: fileSystem, );
artifacts: artifacts,
),
arguments: <String>['analyze','--no-pub', '--fatal-infos', '--no-fatal-warnings'],
statusTextContains: <String>[
'warning',
'missing_return',
],
exitMessageContains: '1 issue found.',
toolExit: true,
exitCode: 1,
);
} finally {
tryToDelete(tempDir);
}
}); });
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment