// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file // for details. 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:async'; import 'dart:io'; import 'package:analyzer/error/error.dart'; import 'package:analyzer/source/error_processor.dart'; import 'package:analyzer/src/analysis_options/analysis_options_provider.dart'; import 'package:analyzer/src/error/codes.dart'; import 'package:analyzer/src/generated/engine.dart'; import 'package:analyzer/src/generated/source.dart'; import 'package:analyzer/src/services/lint.dart'; import 'package:analyzer/src/summary/idl.dart'; import 'package:analyzer/src/util/sdk.dart'; import 'package:analyzer_cli/src/ansi.dart' as ansi; import 'package:analyzer_cli/src/driver.dart'; import 'package:analyzer_cli/src/options.dart'; import 'package:path/path.dart' as path; import 'package:test/test.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; import 'package:yaml/src/yaml_node.dart'; import 'utils.dart'; main() { defineReflectiveSuite(() { defineReflectiveTests(BuildModeTest); defineReflectiveTests(ExitCodesTest); defineReflectiveTests(ExitCodesTest_PreviewDart2); // TODO(scheglov): Restore similar test coverage when the front-end API // allows it. See https://github.com/dart-lang/sdk/issues/32258. // defineReflectiveTests(ExitCodesTest_UseCFE); defineReflectiveTests(LinterTest); defineReflectiveTests(LinterTest_PreviewDart2); // Disabled until integration with the CFE has been restarted. // defineReflectiveTests(LinterTest_UseCFE); defineReflectiveTests(NonDartFilesTest); defineReflectiveTests(OptionsTest); defineReflectiveTests(OptionsTest_PreviewDart2); // TODO(scheglov): Restore similar test coverage when the front-end API // allows it. See https://github.com/dart-lang/sdk/issues/32258. // defineReflectiveTests(OptionsTest_UseCFE); }, name: 'Driver'); } class BaseTest { static const emptyOptionsFile = 'data/empty_options.yaml'; StringSink _savedOutSink, _savedErrorSink; int _savedExitCode; ExitHandler _savedExitHandler; Driver driver; bool get useCFE => false; bool get usePreviewDart2 => false; /// Normalize text with bullets. String bulletToDash(item) => '$item'.replaceAll('•', '-'); /// Start a driver for the given [source], optionally providing additional /// [args] and an [options] file path. The value of [options] defaults to an /// empty options file to avoid unwanted configuration from an otherwise /// discovered options file. Future drive( String source, { String options: emptyOptionsFile, List args: const [], }) { return driveMany([source], options: options, args: args); } /// Like [drive], but takes an array of sources. Future driveMany( List sources, { String options: emptyOptionsFile, List args: const [], }) async { driver = new Driver(isTesting: true); var cmd = []; if (options != null) { cmd = [ '--options', path.join(testDirectory, options), ]; } cmd..addAll(sources.map(_adjustFileSpec))..addAll(args); if (usePreviewDart2) { cmd.insert(0, '--preview-dart-2'); } if (useCFE) { cmd.insert(0, '--use-cfe'); } await driver.start(cmd); } void setUp() { ansi.runningTests = true; _savedOutSink = outSink; _savedErrorSink = errorSink; _savedExitHandler = exitHandler; _savedExitCode = exitCode; exitHandler = (code) => exitCode = code; outSink = new StringBuffer(); errorSink = new StringBuffer(); } void tearDown() { outSink = _savedOutSink; errorSink = _savedErrorSink; exitCode = _savedExitCode; exitHandler = _savedExitHandler; ansi.runningTests = false; } /// Convert a file specification from a relative path to an absolute path. /// Handles the case where the file specification is of the form "$uri|$path". String _adjustFileSpec(String fileSpec) { int uriPrefixLength = fileSpec.indexOf('|') + 1; String uriPrefix = fileSpec.substring(0, uriPrefixLength); String relativePath = fileSpec.substring(uriPrefixLength); return '$uriPrefix${path.join(testDirectory, relativePath)}'; } } @reflectiveTest class BuildModeTest extends BaseTest { test_buildLinked() async { await withTempDirAsync((tempDir) async { var outputPath = path.join(tempDir, 'test_file.dart.sum'); await _doDrive(path.join('data', 'test_file.dart'), additionalArgs: [ '--build-summary-only', '--build-summary-output=$outputPath' ]); var output = new File(outputPath); expect(output.existsSync(), isTrue); PackageBundle bundle = new PackageBundle.fromBuffer(await output.readAsBytes()); var testFileUri = 'file:///test_file.dart'; expect(bundle.unlinkedUnitUris, equals([testFileUri])); expect(bundle.linkedLibraryUris, equals([testFileUri])); expect(exitCode, 0); }); } test_buildLinked_buildSummaryOutputSemantic() async { await withTempDirAsync((tempDir) async { var testDart = path.join(tempDir, 'test.dart'); var testSumFull = path.join(tempDir, 'test.sum.full'); var testSumSemantic = path.join(tempDir, 'test.sum.sem'); new File(testDart).writeAsStringSync('var v = 42;'); await _doDrive(testDart, additionalArgs: [ '--build-summary-only', '--build-summary-output=$testSumFull', '--build-summary-output-semantic=$testSumSemantic', ]); expect(exitCode, 0); // The full summary is produced. { var file = new File(testSumFull); expect(file.existsSync(), isTrue); var bytes = file.readAsBytesSync(); var bundle = new PackageBundle.fromBuffer(bytes); var v = bundle.unlinkedUnits[0].variables[0]; expect(v.name, 'v'); expect(v.nameOffset, 4); } // The semantic summary is produced. { var file = new File(testSumSemantic); expect(file.existsSync(), isTrue); var bytes = file.readAsBytesSync(); var bundle = new PackageBundle.fromBuffer(bytes); var v = bundle.unlinkedUnits[0].variables[0]; expect(v.name, 'v'); expect(v.nameOffset, 0); } }); } test_buildLinked_fromUnlinked() async { await withTempDirAsync((tempDir) async { var aDart = path.join(tempDir, 'a.dart'); var bDart = path.join(tempDir, 'b.dart'); var aUri = 'package:aaa/a.dart'; var bUri = 'package:bbb/b.dart'; var aUnlinked = path.join(tempDir, 'a.unlinked'); var bUnlinked = path.join(tempDir, 'b.unlinked'); var abLinked = path.join(tempDir, 'ab.linked'); new File(aDart).writeAsStringSync('var a = 1;'); new File(bDart).writeAsStringSync(''' import 'package:aaa/a.dart'; var b = a; '''); Future buildUnlinked(String uri, String path, String output) async { await _doDrive(path, uri: uri, additionalArgs: [ '--build-summary-only', '--build-summary-only-unlinked', '--build-summary-output=$output' ]); expect(exitCode, 0); expect(new File(output).existsSync(), isTrue); } await buildUnlinked(aUri, aDart, aUnlinked); await buildUnlinked(bUri, bDart, bUnlinked); await new Driver(isTesting: true).start([ '--dart-sdk', _findSdkDirForSummaries(), '--strong', '--build-mode', '--build-summary-unlinked-input=$aUnlinked,$bUnlinked', '--build-summary-output=$abLinked' ]); expect(exitCode, 0); var bytes = new File(abLinked).readAsBytesSync(); var bundle = new PackageBundle.fromBuffer(bytes); // Only linked information. expect(bundle.unlinkedUnitUris, isEmpty); expect(bundle.linkedLibraryUris, unorderedEquals([aUri, bUri])); // Strong mode type inference was performed. expect(bundle.linkedLibraries[0].units[0].types, isNotEmpty); expect(bundle.linkedLibraries[1].units[0].types, isNotEmpty); }); } test_buildSuppressExitCode_fail_whenFileNotFound() async { await _doDrive(path.join('data', 'non_existent_file.dart'), additionalArgs: ['--build-suppress-exit-code']); expect(exitCode, isNot(0)); } test_buildSuppressExitCode_success_evenIfHasError() async { await _doDrive(path.join('data', 'file_with_error.dart'), additionalArgs: ['--build-suppress-exit-code']); expect(exitCode, 0); } test_buildUnlinked() async { await withTempDirAsync((tempDir) async { var outputPath = path.join(tempDir, 'test_file.dart.sum'); await _doDrive(path.join('data', 'test_file.dart'), additionalArgs: [ '--build-summary-only', '--build-summary-only-unlinked', '--build-summary-output=$outputPath' ]); var output = new File(outputPath); expect(output.existsSync(), isTrue); PackageBundle bundle = new PackageBundle.fromBuffer(await output.readAsBytes()); var testFileUri = 'file:///test_file.dart'; expect(bundle.unlinkedUnits.length, 1); expect(bundle.unlinkedUnitUris, equals([testFileUri])); expect(bundle.linkedLibraryUris, isEmpty); expect(exitCode, 0); }); } test_consumeLinked() async { await withTempDirAsync((tempDir) async { var aDart = path.join(tempDir, 'a.dart'); var bDart = path.join(tempDir, 'b.dart'); var cDart = path.join(tempDir, 'c.dart'); var aUri = 'package:aaa/a.dart'; var bUri = 'package:bbb/b.dart'; var cUri = 'package:ccc/c.dart'; var aSum = path.join(tempDir, 'a.sum'); var bSum = path.join(tempDir, 'b.sum'); var cSum = path.join(tempDir, 'c.sum'); new File(aDart).writeAsStringSync('class A {}'); new File(bDart).writeAsStringSync(''' export 'package:aaa/a.dart'; class B {} '''); new File(cDart).writeAsStringSync(''' import 'package:bbb/b.dart'; var a = new A(); var b = new B(); '''); // Analyze package:aaa/a.dart and compute summary. { await _doDrive(aDart, uri: aUri, additionalArgs: ['--build-summary-output=$aSum']); expect(exitCode, 0); var bytes = new File(aSum).readAsBytesSync(); var bundle = new PackageBundle.fromBuffer(bytes); expect(bundle.unlinkedUnitUris, equals([aUri])); expect(bundle.linkedLibraryUris, equals([aUri])); } // Analyze package:bbb/b.dart and compute summary. { await _doDrive(bDart, uri: bUri, additionalArgs: [ '--build-summary-input=$aSum', '--build-summary-output=$bSum' ]); expect(exitCode, 0); var bytes = new File(bSum).readAsBytesSync(); var bundle = new PackageBundle.fromBuffer(bytes); expect(bundle.unlinkedUnitUris, equals([bUri])); expect(bundle.linkedLibraryUris, equals([bUri])); } // Analyze package:ccc/c.dart and compute summary. { await _doDrive(cDart, uri: cUri, additionalArgs: [ '--build-summary-input=$aSum,$bSum', '--build-summary-output=$cSum' ]); expect(exitCode, 0); var bytes = new File(cSum).readAsBytesSync(); var bundle = new PackageBundle.fromBuffer(bytes); expect(bundle.unlinkedUnitUris, equals([cUri])); expect(bundle.linkedLibraryUris, equals([cUri])); } }); } test_dartSdkSummaryPath_strong() async { await withTempDirAsync((tempDir) async { String sdkPath = _findSdkDirForSummaries(); String strongSummaryPath = path.join(sdkPath, 'lib', '_internal', 'strong.sum'); var testDart = path.join(tempDir, 'test.dart'); var testSum = path.join(tempDir, 'test.sum'); new File(testDart).writeAsStringSync('var v = 42;'); await _doDrive(testDart, additionalArgs: [ '--strong', '--build-summary-only', '--build-summary-output=$testSum' ], dartSdkSummaryPath: strongSummaryPath); var output = new File(testSum); expect(output.existsSync(), isTrue); expect(exitCode, 0); }); } test_error_linkedAsUnlinked() async { await withTempDirAsync((tempDir) async { var aDart = path.join(tempDir, 'a.dart'); var bDart = path.join(tempDir, 'b.dart'); var aUri = 'package:aaa/a.dart'; var bUri = 'package:bbb/b.dart'; var aSum = path.join(tempDir, 'a.sum'); var bSum = path.join(tempDir, 'b.sum'); new File(aDart).writeAsStringSync('class A {}'); // Build linked a.sum await _doDrive(aDart, uri: aUri, additionalArgs: [ '--build-summary-only', '--build-summary-output=$aSum' ]); expect(new File(aSum).existsSync(), isTrue); // Try to consume linked a.sum as unlinked. try { await _doDrive(bDart, uri: bUri, additionalArgs: [ '--build-summary-unlinked-input=$aSum', '--build-summary-output=$bSum' ]); fail('ArgumentError expected.'); } on ArgumentError catch (e) { expect( e.message, contains( 'Got a linked summary for --build-summary-input-unlinked')); } }); } test_error_notUriPipePath() async { await withTempDirAsync((tempDir) async { var testDart = path.join(tempDir, 'test.dart'); new File(testDart).writeAsStringSync('var v = 42;'); // We pass just path, not "uri|path", this is a fatal error. await drive(testDart, args: ['--build-mode', '--format=machine']); expect(exitCode, ErrorSeverity.ERROR.ordinal); }); } test_error_unlinkedAsLinked() async { await withTempDirAsync((tempDir) async { var aDart = path.join(tempDir, 'a.dart'); var bDart = path.join(tempDir, 'b.dart'); var aUri = 'package:aaa/a.dart'; var bUri = 'package:bbb/b.dart'; var aSum = path.join(tempDir, 'a.sum'); var bSum = path.join(tempDir, 'b.sum'); new File(aDart).writeAsStringSync('class A {}'); // Build unlinked a.sum await _doDrive(aDart, uri: aUri, additionalArgs: [ '--build-summary-only', '--build-summary-only-unlinked', '--build-summary-output=$aSum' ]); expect(new File(aSum).existsSync(), isTrue); // Try to consume unlinked a.sum as linked. try { await _doDrive(bDart, uri: bUri, additionalArgs: [ '--build-summary-input=$aSum', '--build-summary-output=$bSum' ]); fail('ArgumentError expected.'); } on ArgumentError catch (e) { expect(e.message, contains('Got an unlinked summary for --build-summary-input')); } }); } test_fail_whenHasError() async { await _doDrive(path.join('data', 'file_with_error.dart')); expect(exitCode, isNot(0)); } test_noStatistics() async { await _doDrive(path.join('data', 'test_file.dart')); // Should not print statistics summary. expect(outSink.toString(), isEmpty); expect(errorSink.toString(), isEmpty); expect(exitCode, 0); } test_onlyErrors_partFirst() async { await withTempDirAsync((tempDir) async { var aDart = path.join(tempDir, 'a.dart'); var bDart = path.join(tempDir, 'b.dart'); var aUri = 'package:aaa/a.dart'; var bUri = 'package:aaa/b.dart'; new File(aDart).writeAsStringSync(r''' library lib; part 'b.dart'; class A {} '''); new File(bDart).writeAsStringSync(''' part of lib; class B {} var a = new A(); var b = new B(); '''); // Analyze b.dart (part) and then a.dart (its library). // No errors should be reported - the part should know its library. await _doDrive(bDart, uri: bUri, additionalArgs: ['$aUri|$aDart']); expect(errorSink, isEmpty); }); } Future _doDrive(String path, {String uri, List additionalArgs: const [], String dartSdkSummaryPath}) async { var optionsFileName = AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE; List args = []; if (dartSdkSummaryPath != null) { args.add('--dart-sdk-summary'); args.add(dartSdkSummaryPath); } else { String sdkPath = _findSdkDirForSummaries(); args.add('--dart-sdk'); args.add(sdkPath); } args.add('--build-mode'); args.add('--format=machine'); args.addAll(additionalArgs); uri ??= 'file:///test_file.dart'; String source = '$uri|$path'; await drive(source, args: args, options: 'data/options_tests_project/$optionsFileName'); } /// Try to find a appropriate directory to pass to "--dart-sdk" that will /// allow summaries to be found. String _findSdkDirForSummaries() { Set triedDirectories = new Set(); bool isSuitable(String sdkDir) { triedDirectories.add(sdkDir); return new File(path.join(sdkDir, 'lib', '_internal', 'spec.sum')) .existsSync(); } // Usually the sdk directory is the parent of the parent of the "dart" // executable. Directory executableParent = new File(Platform.executable).parent; Directory executableGrandparent = executableParent.parent; if (isSuitable(executableGrandparent.path)) { return executableGrandparent.path; } // During build bot execution, the sdk directory is simply the parent of the // "dart" executable. if (isSuitable(executableParent.path)) { return executableParent.path; } // If neither of those are suitable, assume we are running locally within the // SDK project (e.g. within an IDE). Find the build output directory and // search all built configurations. Directory sdkRootDir = new File(Platform.script.toFilePath()).parent.parent.parent.parent; for (String outDirName in ['out', 'xcodebuild']) { Directory outDir = new Directory(path.join(sdkRootDir.path, outDirName)); if (outDir.existsSync()) { for (FileSystemEntity subdir in outDir.listSync()) { if (subdir is Directory) { String candidateSdkDir = path.join(subdir.path, 'dart-sdk'); if (isSuitable(candidateSdkDir)) { return candidateSdkDir; } } } } } throw new Exception('Could not find an SDK directory containing summaries.' ' Tried: ${triedDirectories.toList()}'); } } @reflectiveTest class ExitCodesTest extends BaseTest { test_bazelWorkspace_relativePath() async { // Copy to temp dir so that existing analysis options // in the test directory hierarchy do not interfere await withTempDirAsync((String tempDirPath) async { String dartSdkPath = path.absolute(getSdkPath()); await recursiveCopy( new Directory(path.join(testDirectory, 'data', 'bazel')), tempDirPath); Directory origWorkingDir = Directory.current; try { Directory.current = path.join(tempDirPath, 'proj'); Driver driver = new Driver(isTesting: true); try { await driver.start([ path.join('lib', 'file.dart'), '--dart-sdk', dartSdkPath, ]); } catch (e) { print('=== debug info ==='); print('dartSdkPath: $dartSdkPath'); print('stderr:\n${errorSink.toString()}'); rethrow; } expect(errorSink.toString(), isEmpty); expect(outSink.toString(), contains('No issues found')); expect(exitCode, 0); } finally { Directory.current = origWorkingDir; } }); } test_enableAssertInitializer() async { await drive('data/file_with_assert_initializers.dart', args: ['--enable-assert-initializers']); expect(exitCode, 0); } test_fatalErrors() async { await drive('data/file_with_error.dart'); expect(exitCode, 3); } test_fatalHints() async { await drive('data/file_with_hint.dart', args: ['--fatal-hints']); expect(exitCode, 1); } test_fatalWarnings() async { await drive('data/file_with_warning.dart', args: ['--fatal-warnings']); expect(exitCode, 2); } test_missingDartFile() async { await drive('data/NO_DART_FILE_HERE.dart'); expect(exitCode, 3); } test_missingOptionsFile() async { await drive('data/test_file.dart', options: 'data/NO_OPTIONS_HERE'); expect(exitCode, 3); } test_notFatalHints() async { await drive('data/file_with_hint.dart'); expect(exitCode, 0); } test_notFatalWarnings() async { await drive('data/file_with_warning.dart'); expect(exitCode, 0); } test_partFile() async { await driveMany([ path.join(testDirectory, 'data/library_and_parts/lib.dart'), path.join(testDirectory, 'data/library_and_parts/part1.dart') ]); expect(exitCode, 0); } test_partFile_dangling() async { await drive('data/library_and_parts/part2.dart'); expect(exitCode, 3); } test_partFile_extra() async { await driveMany([ path.join(testDirectory, 'data/library_and_parts/lib.dart'), path.join(testDirectory, 'data/library_and_parts/part1.dart'), path.join(testDirectory, 'data/library_and_parts/part2.dart') ]); expect(exitCode, 3); } test_partFile_reversed() async { Driver driver = new Driver(isTesting: true); await driver.start([ path.join(testDirectory, 'data/library_and_parts/part1.dart'), path.join(testDirectory, 'data/library_and_parts/lib.dart') ]); expect(exitCode, 0); } } @reflectiveTest class ExitCodesTest_PreviewDart2 extends ExitCodesTest { @override bool get usePreviewDart2 => true; @override @failingTest test_fatalErrors() { // TODO(devoncarew): This test times out when used with @failingTest. return new Future.error('failing test'); } } @reflectiveTest class ExitCodesTest_UseCFE extends ExitCodesTest { @override bool get useCFE => true; @override @failingTest test_fatalErrors() { // TODO(devoncarew): This test times out when used with @failingTest. return new Future.error('failing test'); } @override @failingTest test_fatalWarnings() => super.test_fatalWarnings(); @override @failingTest test_notFatalWarnings() => super.test_notFatalWarnings(); } @reflectiveTest class LinterTest extends BaseTest { String get optionsFileName => AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE; test_containsLintRuleEntry() async { YamlMap options = _parseOptions(''' linter: rules: - foo '''); expect(containsLintRuleEntry(options), true); options = _parseOptions(''' '''); expect(containsLintRuleEntry(options), false); options = _parseOptions(''' linter: rules: # - foo '''); expect(containsLintRuleEntry(options), true); options = _parseOptions(''' linter: # rules: # - foo '''); expect(containsLintRuleEntry(options), false); } test_defaultLints_generatedLints() async { await _runLinter_defaultLints(); expect(bulletToDash(outSink), contains('lint - Name types using UpperCamelCase')); } test_defaultLints_getsDefaultLints() async { await _runLinter_defaultLints(); /// Lints should be enabled. expect(driver.context.analysisOptions.lint, isTrue); /// Default list should include camel_case_types. var lintNames = getLints(driver.context).map((r) => r.name); expect(lintNames, contains('camel_case_types')); } test_lintsInOptions_generatedLints() async { await _runLinter_lintsInOptions(); expect(bulletToDash(outSink), contains('lint - Name types using UpperCamelCase')); } test_lintsInOptions_getAnalysisOptions() async { await _runLinter_lintsInOptions(); /// Lints should be enabled. expect(driver.context.analysisOptions.lint, isTrue); /// The analysis options file only specifies 'camel_case_types'. var lintNames = getLints(driver.context).map((r) => r.name); expect(lintNames, orderedEquals(['camel_case_types'])); } test_noLints_lintsDisabled() async { await _runLinter_noLintsFlag(); expect(driver.context.analysisOptions.lint, isFalse); } test_noLints_noGeneratedWarnings() async { await _runLinter_noLintsFlag(); expect(outSink.toString(), contains('No issues found')); } test_noLints_noRegisteredLints() async { await _runLinter_noLintsFlag(); expect(getLints(driver.context), isEmpty); } YamlMap _parseOptions(String src) => new AnalysisOptionsProvider().getOptionsFromString(src); Future _runLinter_defaultLints() async { await drive('data/linter_project/test_file.dart', options: 'data/linter_project/$optionsFileName', args: ['--lints']); } Future _runLinter_lintsInOptions() async { await drive('data/linter_project/test_file.dart', options: 'data/linter_project/$optionsFileName', args: ['--lints']); } Future _runLinter_noLintsFlag() async { await drive('data/no_lints_project/test_file.dart', options: 'data/no_lints_project/$optionsFileName'); } } @reflectiveTest class LinterTest_PreviewDart2 extends LinterTest { @override bool get usePreviewDart2 => true; } @reflectiveTest class LinterTest_UseCFE extends LinterTest { @override bool get useCFE => true; } @reflectiveTest class NonDartFilesTest extends BaseTest { test_analysisOptionsYaml() async { await withTempDirAsync((tempDir) async { String filePath = path.join(tempDir, AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE); new File(filePath).writeAsStringSync(''' analyzer: string-mode: true '''); await drive(filePath); expect( bulletToDash(outSink), contains( "warning - The option 'string-mode' isn't supported by 'analyzer'")); expect(exitCode, 0); }); } test_pubspecYaml() async { await withTempDirAsync((tempDir) async { String filePath = path.join(tempDir, AnalysisEngine.PUBSPEC_YAML_FILE); new File(filePath).writeAsStringSync(''' name: foo flutter: assets: doesNotExist.gif '''); await drive(filePath); expect( bulletToDash(outSink), contains( "warning - The value of the 'asset' field is expected to be a list of relative file paths")); expect(exitCode, 0); }); } } @reflectiveTest class OptionsTest extends BaseTest { String get optionsFileName => AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE; List get processors => driver.context.analysisOptions.errorProcessors; ErrorProcessor processorFor(AnalysisError error) => processors.firstWhere((p) => p.appliesTo(error)); test_analysisOptions_excludes() async { await drive('data/exclude_test_project', options: 'data/exclude_test_project/$optionsFileName'); _expectUndefinedClassErrorsWithoutExclusions(); } test_analysisOptions_excludesRelativeToAnalysisOptions_explicit() async { // The exclude is relative to the project, not/ the analyzed path, and it // has to then understand that. await drive('data/exclude_test_project', options: 'data/exclude_test_project/$optionsFileName'); _expectUndefinedClassErrorsWithoutExclusions(); } test_analysisOptions_excludesRelativeToAnalysisOptions_inferred() async { // By passing no options, and the path `lib`, it should discover the // analysis_options above lib. The exclude is relative to the project, not // the analyzed path, and it has to then understand that. await drive('data/exclude_test_project/lib', options: null); _expectUndefinedClassErrorsWithoutExclusions(); } test_analyzeFilesInDifferentContexts() async { await driveMany([ 'data/linter_project/test_file.dart', 'data/no_lints_project/test_file.dart', ], options: null); // Should have the lint in the project with lint rules enabled. expect( bulletToDash(outSink), contains(path.join('linter_project', 'test_file.dart') + ':7:7 - camel_case_types')); // Should be just one lint in total. expect(outSink.toString(), contains('1 lint found.')); } test_basic_filters() async { await _driveBasic(); expect(processors, hasLength(3)); // unused_local_variable: ignore var unused_local_variable = new AnalysisError( new TestSource(), 0, 1, HintCode.UNUSED_LOCAL_VARIABLE, [ ['x'] ]); expect(processorFor(unused_local_variable).severity, isNull); // missing_return: error var missing_return = new AnalysisError(new TestSource(), 0, 1, HintCode.MISSING_RETURN, [ ['x'] ]); expect(processorFor(missing_return).severity, ErrorSeverity.ERROR); expect(bulletToDash(outSink), contains("error - This function declares a return type of 'int'")); expect(outSink.toString(), contains("1 error and 1 warning found.")); } test_basic_language() async { await _driveBasic(); expect(driver.context.analysisOptions.enableSuperMixins, isTrue); } test_basic_strongMode() async { await _driveBasic(); expect(driver.context.analysisOptions.strongMode, isTrue); // https://github.com/dart-lang/sdk/issues/26129 AnalysisContext sdkContext = driver.context.sourceFactory.dartSdk.context; expect(sdkContext.analysisOptions.strongMode, isTrue); } test_includeDirective() async { String testDir = path.join( testDirectory, 'data', 'options_include_directive_tests_project'); await drive( path.join(testDir, 'lib', 'test_file.dart'), args: [ '--fatal-warnings', '--packages', path.join(testDir, '_packages'), ], options: path.join(testDir, 'analysis_options.yaml'), ); expect(exitCode, 3); expect(outSink.toString(), contains('but doesn\'t end with a return statement')); expect(outSink.toString(), contains('isn\'t defined')); expect(outSink.toString(), contains('Avoid empty else statements')); } test_previewDart2() async { await drive('data/options_tests_project/test_file.dart', args: ['--preview-dart-2']); expect(driver.context.analysisOptions.useFastaParser, isFalse); } test_strongSdk() async { String testDir = path.join(testDirectory, 'data', 'strong_sdk'); await drive(path.join(testDir, 'main.dart'), args: ['--strong']); expect(driver.context.analysisOptions.strongMode, isTrue); expect(outSink.toString(), contains('No issues found')); } test_todo() async { await drive('data/file_with_todo.dart'); expect(outSink.toString().contains('[info]'), isFalse); } @failingTest test_useCFE() async { // Disabled until integration with the CFE has been restarted. fail('Times out when run on a VM with --preview-dart-2 enabled'); // await drive('data/options_tests_project/test_file.dart', // args: ['--use-cfe']); // expect(driver.context.analysisOptions.useFastaParser, isTrue); } test_withFlags_overrideFatalWarning() async { await drive('data/options_tests_project/test_file.dart', args: ['--fatal-warnings'], options: 'data/options_tests_project/$optionsFileName'); // missing_return: error var undefined_function = new AnalysisError( new TestSource(), 0, 1, StaticTypeWarningCode.UNDEFINED_FUNCTION, [ ['x'] ]); expect(processorFor(undefined_function).severity, ErrorSeverity.WARNING); // Should not be made fatal by `--fatal-warnings`. expect(bulletToDash(outSink), contains("warning - The function 'baz' isn't defined")); expect(outSink.toString(), contains("1 error and 1 warning found.")); } Future _driveBasic() async { await drive('data/options_tests_project/test_file.dart', options: 'data/options_tests_project/$optionsFileName'); } void _expectUndefinedClassErrorsWithoutExclusions() { bool isStrong = usePreviewDart2 || new AnalysisOptionsImpl().previewDart2; final String issueType = isStrong ? 'error' : 'warning'; expect(bulletToDash(outSink), contains("$issueType - Undefined class 'IncludedUndefinedClass'")); expect( bulletToDash(outSink), isNot( contains("$issueType - Undefined class 'ExcludedUndefinedClass'"))); expect(outSink.toString(), contains("1 $issueType found.")); } } @reflectiveTest class OptionsTest_PreviewDart2 extends OptionsTest { @override bool get usePreviewDart2 => true; } @reflectiveTest class OptionsTest_UseCFE extends OptionsTest { @override bool get useCFE => true; @override @failingTest test_analysisOptions_excludes() => super.test_analysisOptions_excludes(); @override @failingTest test_analysisOptions_excludesRelativeToAnalysisOptions_explicit() => super.test_analysisOptions_excludesRelativeToAnalysisOptions_explicit(); @override @failingTest test_analysisOptions_excludesRelativeToAnalysisOptions_inferred() => super.test_analysisOptions_excludesRelativeToAnalysisOptions_inferred(); @override @failingTest test_basic_filters() => super.test_basic_filters(); @override @failingTest test_basic_language() => super.test_basic_language(); @override @failingTest test_basic_strongMode() => super.test_basic_strongMode(); @override @failingTest test_includeDirective() => super.test_includeDirective(); @override @failingTest test_previewDart2() => super.test_previewDart2(); @override @failingTest test_withFlags_overrideFatalWarning() => super.test_withFlags_overrideFatalWarning(); } class TestSource implements Source { TestSource(); @override noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); }