Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library analyzer_cli.test.driver; | 5 library analyzer_cli.test.driver; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:io'; | 8 import 'dart:io'; |
| 9 | 9 |
| 10 import 'package:analyzer/error/error.dart'; | 10 import 'package:analyzer/error/error.dart'; |
| 11 import 'package:analyzer/source/analysis_options_provider.dart'; | 11 import 'package:analyzer/source/analysis_options_provider.dart'; |
| 12 import 'package:analyzer/source/error_processor.dart'; | 12 import 'package:analyzer/source/error_processor.dart'; |
| 13 import 'package:analyzer/src/error/codes.dart'; | 13 import 'package:analyzer/src/error/codes.dart'; |
| 14 import 'package:analyzer/src/generated/engine.dart'; | 14 import 'package:analyzer/src/generated/engine.dart'; |
| 15 import 'package:analyzer/src/generated/source.dart'; | 15 import 'package:analyzer/src/generated/source.dart'; |
| 16 import 'package:analyzer/src/services/lint.dart'; | 16 import 'package:analyzer/src/services/lint.dart'; |
| 17 import 'package:analyzer/src/summary/idl.dart'; | 17 import 'package:analyzer/src/summary/idl.dart'; |
| 18 import 'package:analyzer/src/util/sdk.dart'; | 18 import 'package:analyzer/src/util/sdk.dart'; |
| 19 import 'package:analyzer_cli/src/ansi.dart' as ansi; | 19 import 'package:analyzer_cli/src/ansi.dart' as ansi; |
| 20 import 'package:analyzer_cli/src/driver.dart'; | 20 import 'package:analyzer_cli/src/driver.dart'; |
| 21 import 'package:analyzer_cli/src/options.dart'; | 21 import 'package:analyzer_cli/src/options.dart'; |
| 22 import 'package:path/path.dart' as path; | 22 import 'package:path/path.dart' as path; |
| 23 import 'package:test/test.dart'; | 23 import 'package:test/test.dart'; |
| 24 import 'package:test_reflective_loader/test_reflective_loader.dart'; | |
| 24 import 'package:yaml/src/yaml_node.dart'; | 25 import 'package:yaml/src/yaml_node.dart'; |
| 25 | 26 |
| 26 import 'utils.dart'; | 27 import 'utils.dart'; |
| 27 | 28 |
| 28 main() { | 29 main() { |
| 29 StringSink savedOutSink, savedErrorSink; | 30 defineReflectiveSuite(() { |
| 30 int savedExitCode; | 31 defineReflectiveTests(BuildModeTest); |
| 31 ExitHandler savedExitHandler; | 32 defineReflectiveTests(ExitCodesTest); |
| 32 | 33 defineReflectiveTests(LinterTest); |
| 33 /// Base setup. | 34 defineReflectiveTests(OptionsTest); |
| 34 _setUp() { | 35 }, name: 'Driver'); |
| 35 ansi.runningTests = true; | 36 } |
| 36 savedOutSink = outSink; | 37 |
| 37 savedErrorSink = errorSink; | 38 @reflectiveTest |
| 38 savedExitHandler = exitHandler; | 39 class BuildModeTest extends _BaseTest { |
| 39 savedExitCode = exitCode; | 40 test_buildLinked() async { |
| 40 exitHandler = (code) => exitCode = code; | 41 await withTempDirAsync((tempDir) async { |
| 41 outSink = new StringBuffer(); | 42 var outputPath = path.join(tempDir, 'test_file.dart.sum'); |
| 42 errorSink = new StringBuffer(); | 43 await _doDrive(path.join('data', 'test_file.dart'), additionalArgs: [ |
| 43 } | 44 '--build-summary-only', |
| 44 | 45 '--build-summary-output=$outputPath' |
| 45 /// Base teardown. | 46 ]); |
| 46 _tearDown() { | 47 var output = new File(outputPath); |
| 47 outSink = savedOutSink; | 48 expect(output.existsSync(), isTrue); |
| 48 errorSink = savedErrorSink; | 49 PackageBundle bundle = |
| 49 exitCode = savedExitCode; | 50 new PackageBundle.fromBuffer(await output.readAsBytes()); |
| 50 exitHandler = savedExitHandler; | 51 var testFileUri = 'file:///test_file.dart'; |
| 51 ansi.runningTests = false; | 52 expect(bundle.unlinkedUnitUris, equals([testFileUri])); |
| 52 } | 53 expect(bundle.linkedLibraryUris, equals([testFileUri])); |
| 53 | 54 expect(exitCode, 0); |
| 54 setUp(() => _setUp()); | |
| 55 | |
| 56 tearDown(() => _tearDown()); | |
| 57 | |
| 58 group('Driver', () { | |
| 59 group('options', () { | |
| 60 test('todos', () async { | |
| 61 await drive('data/file_with_todo.dart'); | |
| 62 expect(outSink.toString().contains('[info]'), isFalse); | |
| 63 }); | |
| 64 }); | 55 }); |
| 65 | 56 } |
| 66 _test_exitCodes(); | 57 |
| 67 _test_linter(); | 58 test_buildSuppressExitCode_fail_whenFileNotFound() async { |
| 68 _test_optionsProcessing(); | 59 await _doDrive(path.join('data', 'non_existent_file.dart'), |
| 69 _test_buildMode(); | 60 additionalArgs: ['--build-suppress-exit-code']); |
| 70 | 61 expect(exitCode, isNot(0)); |
| 71 //TODO(pq): fix to be bot-friendly (sdk#25258). | 62 } |
|
Brian Wilkerson
2017/07/07 16:25:30
Did we loose these tests?
scheglov
2017/07/07 16:37:53
Yes, we did.
Comments is a good long-term code arc
| |
| 72 // group('in temp directory', () { | 63 |
| 73 // Directory savedCurrentDirectory; | 64 test_buildSuppressExitCode_success_evenIfHasError() async { |
| 74 // Directory tempDir; | 65 await _doDrive(path.join('data', 'file_with_error.dart'), |
| 75 // setUp(() { | 66 additionalArgs: ['--build-suppress-exit-code']); |
| 76 // // Call base setUp. | 67 expect(exitCode, 0); |
| 77 // _setUp(); | 68 } |
| 78 // savedCurrentDirectory = Directory.current; | 69 |
| 79 // tempDir = Directory.systemTemp.createTempSync('analyzer_'); | 70 test_buildUnlinked() async { |
| 80 // }); | 71 await withTempDirAsync((tempDir) async { |
| 81 // tearDown(() { | 72 var outputPath = path.join(tempDir, 'test_file.dart.sum'); |
| 82 // Directory.current = savedCurrentDirectory; | 73 await _doDrive(path.join('data', 'test_file.dart'), additionalArgs: [ |
| 83 // tempDir.deleteSync(recursive: true); | 74 '--build-summary-only', |
| 84 // // Call base tearDown. | 75 '--build-summary-only-unlinked', |
| 85 // _tearDown(); | 76 '--build-summary-output=$outputPath' |
| 86 // }); | 77 ]); |
| 87 // | 78 var output = new File(outputPath); |
| 88 // test('packages folder', () { | 79 expect(output.existsSync(), isTrue); |
| 89 // Directory.current = tempDir; | 80 PackageBundle bundle = |
| 90 // new File(path.join(tempDir.path, 'test.dart')).writeAsStringSync(''' | 81 new PackageBundle.fromBuffer(await output.readAsBytes()); |
| 91 //import 'package:foo/bar.dart'; | 82 var testFileUri = 'file:///test_file.dart'; |
| 92 //main() { | 83 expect(bundle.unlinkedUnits.length, 1); |
| 93 // baz(); | 84 expect(bundle.unlinkedUnitUris, equals([testFileUri])); |
| 94 //} | 85 expect(bundle.linkedLibraryUris, isEmpty); |
| 95 // '''); | 86 expect(exitCode, 0); |
| 96 // Directory packagesDir = | 87 }); |
| 97 // new Directory(path.join(tempDir.path, 'packages')); | 88 } |
| 98 // packagesDir.createSync(); | 89 |
| 99 // Directory fooDir = new Directory(path.join(packagesDir.path, 'foo')); | 90 test_consumeLinked() async { |
| 100 // fooDir.createSync(); | 91 await withTempDirAsync((tempDir) async { |
| 101 // new File(path.join(fooDir.path, 'bar.dart')).writeAsStringSync(''' | 92 var aDart = path.join(tempDir, 'a.dart'); |
| 102 //void baz() {} | 93 var bDart = path.join(tempDir, 'b.dart'); |
| 103 // '''); | 94 var cDart = path.join(tempDir, 'c.dart'); |
| 104 // new Driver().start(['test.dart']); | 95 |
| 105 // expect(exitCode, 0); | 96 var aUri = 'package:aaa/a.dart'; |
| 106 // }); | 97 var bUri = 'package:bbb/b.dart'; |
| 107 // | 98 var cUri = 'package:ccc/c.dart'; |
| 108 // test('no package resolution', () { | 99 |
| 109 // Directory.current = tempDir; | 100 var aSum = path.join(tempDir, 'a.sum'); |
| 110 // new File(path.join(tempDir.path, 'test.dart')).writeAsStringSync(''' | 101 var bSum = path.join(tempDir, 'b.sum'); |
| 111 //import 'package:path/path.dart'; | 102 var cSum = path.join(tempDir, 'c.sum'); |
| 112 //main() {} | 103 |
| 113 // '''); | 104 new File(aDart).writeAsStringSync('class A {}'); |
| 114 // new Driver().start(['test.dart']); | 105 new File(bDart).writeAsStringSync(''' |
| 115 // expect(exitCode, 3); | |
| 116 // String stdout = outSink.toString(); | |
| 117 // expect(stdout, contains('[error] Target of URI does not exist')); | |
| 118 // expect(stdout, contains('1 error found.')); | |
| 119 // expect(errorSink.toString(), ''); | |
| 120 // }); | |
| 121 // | |
| 122 // test('bad package root', () { | |
| 123 // new Driver().start(['--package-root', 'does/not/exist', 'test.dart']); | |
| 124 // String stdout = outSink.toString(); | |
| 125 // expect(exitCode, 3); | |
| 126 // expect( | |
| 127 // stdout, | |
| 128 // contains( | |
| 129 // 'Package root directory (does/not/exist) does not exist.')); | |
| 130 // }); | |
| 131 // }); | |
| 132 }); | |
| 133 } | |
| 134 | |
| 135 const emptyOptionsFile = 'data/empty_options.yaml'; | |
| 136 | |
| 137 /// Shared driver. | |
| 138 Driver driver; | |
| 139 | |
| 140 List<ErrorProcessor> get processors => | |
| 141 driver.context.analysisOptions.errorProcessors; | |
| 142 | |
| 143 /// Convert a file specification from a relative path to an absolute path. | |
| 144 /// Handles the case where the file specification is of the form "$uri|$path". | |
| 145 String adjustFileSpec(String fileSpec) { | |
| 146 int uriPrefixLength = fileSpec.indexOf('|') + 1; | |
| 147 String uriPrefix = fileSpec.substring(0, uriPrefixLength); | |
| 148 String relativePath = fileSpec.substring(uriPrefixLength); | |
| 149 return '$uriPrefix${path.join(testDirectory, relativePath)}'; | |
| 150 } | |
| 151 | |
| 152 /// Start a driver for the given [source], optionally providing additional | |
| 153 /// [args] and an [options] file path. The value of [options] defaults to | |
| 154 /// an empty options file to avoid unwanted configuration from an otherwise | |
| 155 /// discovered options file. | |
| 156 Future<Null> drive(String source, | |
| 157 {String options: emptyOptionsFile, | |
| 158 List<String> args: const <String>[]}) async { | |
| 159 driver = new Driver(isTesting: true); | |
| 160 var cmd = [ | |
| 161 '--options', | |
| 162 path.join(testDirectory, options), | |
| 163 adjustFileSpec(source) | |
| 164 ]..addAll(args); | |
| 165 await driver.start(cmd); | |
| 166 } | |
| 167 | |
| 168 /// Try to find a appropriate directory to pass to "--dart-sdk" that will | |
| 169 /// allow summaries to be found. | |
| 170 String findSdkDirForSummaries() { | |
| 171 Set<String> triedDirectories = new Set<String>(); | |
| 172 bool isSuitable(String sdkDir) { | |
| 173 triedDirectories.add(sdkDir); | |
| 174 return new File(path.join(sdkDir, 'lib', '_internal', 'spec.sum')) | |
| 175 .existsSync(); | |
| 176 } | |
| 177 | |
| 178 // Usually the sdk directory is the parent of the parent of the "dart" | |
| 179 // executable. | |
| 180 Directory executableParent = new File(Platform.executable).parent; | |
| 181 Directory executableGrandparent = executableParent.parent; | |
| 182 if (isSuitable(executableGrandparent.path)) { | |
| 183 return executableGrandparent.path; | |
| 184 } | |
| 185 // During buildbot execution, the sdk directory is simply the parent of the | |
| 186 // "dart" executable. | |
| 187 if (isSuitable(executableParent.path)) { | |
| 188 return executableParent.path; | |
| 189 } | |
| 190 // If neither of those are suitable, assume we are running locally within the | |
| 191 // SDK project (e.g. within an IDE). Find the build output directory and | |
| 192 // search all built configurations. | |
| 193 Directory sdkRootDir = | |
| 194 new File(Platform.script.toFilePath()).parent.parent.parent.parent; | |
| 195 for (String outDirName in ['out', 'xcodebuild']) { | |
| 196 Directory outDir = new Directory(path.join(sdkRootDir.path, outDirName)); | |
| 197 if (outDir.existsSync()) { | |
| 198 for (FileSystemEntity subdir in outDir.listSync()) { | |
| 199 if (subdir is Directory) { | |
| 200 String candidateSdkDir = path.join(subdir.path, 'dart-sdk'); | |
| 201 if (isSuitable(candidateSdkDir)) { | |
| 202 return candidateSdkDir; | |
| 203 } | |
| 204 } | |
| 205 } | |
| 206 } | |
| 207 } | |
| 208 throw new Exception('Could not find an SDK directory containing summaries.' | |
| 209 ' Tried: ${triedDirectories.toList()}'); | |
| 210 } | |
| 211 | |
| 212 Map<String, YamlNode> parseOptions(String src) => | |
| 213 new AnalysisOptionsProvider().getOptionsFromString(src); | |
| 214 | |
| 215 ErrorProcessor processorFor(AnalysisError error) => | |
| 216 processors.firstWhere((p) => p.appliesTo(error)); | |
| 217 | |
| 218 /// Normalize text with bullets. | |
| 219 String _bulletToDash(item) => '$item'.replaceAll('•', '-'); | |
| 220 | |
| 221 void _test_buildMode() { | |
| 222 void createTests(String designator, String optionsFileName) { | |
| 223 group('build-mode - $designator', () { | |
| 224 // Shared driver command. | |
| 225 Future<Null> doDrive(String path, | |
| 226 {String uri, List<String> additionalArgs: const []}) async { | |
| 227 uri ??= 'file:///test_file.dart'; | |
| 228 await drive('$uri|$path', | |
| 229 args: [ | |
| 230 '--dart-sdk', | |
| 231 findSdkDirForSummaries(), | |
| 232 '--build-mode', | |
| 233 '--format=machine' | |
| 234 ]..addAll(additionalArgs), | |
| 235 options: 'data/options_tests_project/$optionsFileName'); | |
| 236 } | |
| 237 | |
| 238 test('no stats', () async { | |
| 239 await doDrive(path.join('data', 'test_file.dart')); | |
| 240 // Should not print stat summary. | |
| 241 expect(outSink.toString(), isEmpty); | |
| 242 expect(errorSink.toString(), isEmpty); | |
| 243 expect(exitCode, 0); | |
| 244 }); | |
| 245 | |
| 246 test( | |
| 247 'Fails if file not found, even when --build-suppress-exit-code is give n', | |
| 248 () async { | |
| 249 await doDrive(path.join('data', 'non_existent_file.dart'), | |
| 250 additionalArgs: ['--build-suppress-exit-code']); | |
| 251 expect(exitCode, isNot(0)); | |
| 252 }); | |
| 253 | |
| 254 test('Fails if there are errors', () async { | |
| 255 await doDrive(path.join('data', 'file_with_error.dart')); | |
| 256 expect(exitCode, isNot(0)); | |
| 257 }); | |
| 258 | |
| 259 test( | |
| 260 'Succeeds if there are errors, when --build-suppress-exit-code is give n', | |
| 261 () async { | |
| 262 await doDrive(path.join('data', 'file_with_error.dart'), | |
| 263 additionalArgs: ['--build-suppress-exit-code']); | |
| 264 expect(exitCode, 0); | |
| 265 }); | |
| 266 | |
| 267 test('Consume summaries', () async { | |
| 268 await withTempDirAsync((tempDir) async { | |
| 269 var aDart = path.join(tempDir, 'a.dart'); | |
| 270 var bDart = path.join(tempDir, 'b.dart'); | |
| 271 var cDart = path.join(tempDir, 'c.dart'); | |
| 272 | |
| 273 var aUri = 'package:aaa/a.dart'; | |
| 274 var bUri = 'package:bbb/b.dart'; | |
| 275 var cUri = 'package:ccc/c.dart'; | |
| 276 | |
| 277 var aSum = path.join(tempDir, 'a.sum'); | |
| 278 var bSum = path.join(tempDir, 'b.sum'); | |
| 279 var cSum = path.join(tempDir, 'c.sum'); | |
| 280 | |
| 281 new File(aDart).writeAsStringSync('class A {}'); | |
| 282 new File(bDart).writeAsStringSync(''' | |
| 283 export 'package:aaa/a.dart'; | 106 export 'package:aaa/a.dart'; |
| 284 class B {} | 107 class B {} |
| 285 '''); | 108 '''); |
| 286 new File(cDart).writeAsStringSync(''' | 109 new File(cDart).writeAsStringSync(''' |
| 287 import 'package:bbb/b.dart'; | 110 import 'package:bbb/b.dart'; |
| 288 var a = new A(); | 111 var a = new A(); |
| 289 var b = new B(); | 112 var b = new B(); |
| 290 '''); | 113 '''); |
| 291 | 114 |
| 292 // Analyze package:aaa/a.dart and compute summary. | 115 // Analyze package:aaa/a.dart and compute summary. |
| 293 { | 116 { |
| 294 await doDrive(aDart, | 117 await _doDrive(aDart, |
| 295 uri: aUri, additionalArgs: ['--build-summary-output=$aSum']); | 118 uri: aUri, additionalArgs: ['--build-summary-output=$aSum']); |
| 296 expect(exitCode, 0); | 119 expect(exitCode, 0); |
| 297 var bytes = new File(aSum).readAsBytesSync(); | 120 var bytes = new File(aSum).readAsBytesSync(); |
| 298 var bundle = new PackageBundle.fromBuffer(bytes); | 121 var bundle = new PackageBundle.fromBuffer(bytes); |
| 299 expect(bundle.unlinkedUnitUris, equals([aUri])); | 122 expect(bundle.unlinkedUnitUris, equals([aUri])); |
| 300 expect(bundle.linkedLibraryUris, equals([aUri])); | 123 expect(bundle.linkedLibraryUris, equals([aUri])); |
| 124 } | |
| 125 | |
| 126 // Analyze package:bbb/b.dart and compute summary. | |
| 127 { | |
| 128 await _doDrive(bDart, uri: bUri, additionalArgs: [ | |
| 129 '--build-summary-input=$aSum', | |
| 130 '--build-summary-output=$bSum' | |
| 131 ]); | |
| 132 expect(exitCode, 0); | |
| 133 var bytes = new File(bSum).readAsBytesSync(); | |
| 134 var bundle = new PackageBundle.fromBuffer(bytes); | |
| 135 expect(bundle.unlinkedUnitUris, equals([bUri])); | |
| 136 expect(bundle.linkedLibraryUris, equals([bUri])); | |
| 137 } | |
| 138 | |
| 139 // Analyze package:ccc/c.dart and compute summary. | |
| 140 { | |
| 141 await _doDrive(cDart, uri: cUri, additionalArgs: [ | |
| 142 '--build-summary-input=$aSum,$bSum', | |
| 143 '--build-summary-output=$cSum' | |
| 144 ]); | |
| 145 expect(exitCode, 0); | |
| 146 var bytes = new File(cSum).readAsBytesSync(); | |
| 147 var bundle = new PackageBundle.fromBuffer(bytes); | |
| 148 expect(bundle.unlinkedUnitUris, equals([cUri])); | |
| 149 expect(bundle.linkedLibraryUris, equals([cUri])); | |
| 150 } | |
| 151 }); | |
| 152 } | |
| 153 | |
| 154 test_error_linkedAsUnlinked() async { | |
| 155 await withTempDirAsync((tempDir) async { | |
| 156 var aDart = path.join(tempDir, 'a.dart'); | |
| 157 var bDart = path.join(tempDir, 'b.dart'); | |
| 158 | |
| 159 var aUri = 'package:aaa/a.dart'; | |
| 160 var bUri = 'package:bbb/b.dart'; | |
| 161 | |
| 162 var aSum = path.join(tempDir, 'a.sum'); | |
| 163 var bSum = path.join(tempDir, 'b.sum'); | |
| 164 | |
| 165 new File(aDart).writeAsStringSync('class A {}'); | |
| 166 | |
| 167 // Build linked a.sum | |
| 168 await _doDrive(aDart, uri: aUri, additionalArgs: [ | |
| 169 '--build-summary-only', | |
| 170 '--build-summary-output=$aSum' | |
| 171 ]); | |
| 172 expect(new File(aSum).existsSync(), isTrue); | |
| 173 | |
| 174 // Try to consume linked a.sum as unlinked. | |
| 175 try { | |
| 176 await _doDrive(bDart, uri: bUri, additionalArgs: [ | |
| 177 '--build-summary-unlinked-input=$aSum', | |
| 178 '--build-summary-output=$bSum' | |
| 179 ]); | |
| 180 fail('ArgumentError expected.'); | |
| 181 } on ArgumentError catch (e) { | |
| 182 expect( | |
| 183 e.message, | |
| 184 contains( | |
| 185 'Got a linked summary for --build-summary-input-unlinked')); | |
| 186 } | |
| 187 }); | |
| 188 } | |
| 189 | |
| 190 test_error_unlinkedAsLinked() async { | |
| 191 await withTempDirAsync((tempDir) async { | |
| 192 var aDart = path.join(tempDir, 'a.dart'); | |
| 193 var bDart = path.join(tempDir, 'b.dart'); | |
| 194 | |
| 195 var aUri = 'package:aaa/a.dart'; | |
| 196 var bUri = 'package:bbb/b.dart'; | |
| 197 | |
| 198 var aSum = path.join(tempDir, 'a.sum'); | |
| 199 var bSum = path.join(tempDir, 'b.sum'); | |
| 200 | |
| 201 new File(aDart).writeAsStringSync('class A {}'); | |
| 202 | |
| 203 // Build unlinked a.sum | |
| 204 await _doDrive(aDart, uri: aUri, additionalArgs: [ | |
| 205 '--build-summary-only', | |
| 206 '--build-summary-only-unlinked', | |
| 207 '--build-summary-output=$aSum' | |
| 208 ]); | |
| 209 expect(new File(aSum).existsSync(), isTrue); | |
| 210 | |
| 211 // Try to consume unlinked a.sum as linked. | |
| 212 try { | |
| 213 await _doDrive(bDart, uri: bUri, additionalArgs: [ | |
| 214 '--build-summary-input=$aSum', | |
| 215 '--build-summary-output=$bSum' | |
| 216 ]); | |
| 217 fail('ArgumentError expected.'); | |
| 218 } on ArgumentError catch (e) { | |
| 219 expect(e.message, | |
| 220 contains('Got an unlinked summary for --build-summary-input')); | |
| 221 } | |
| 222 }); | |
| 223 } | |
| 224 | |
| 225 test_fail_whenHasError() async { | |
| 226 await _doDrive(path.join('data', 'file_with_error.dart')); | |
| 227 expect(exitCode, isNot(0)); | |
| 228 } | |
| 229 | |
| 230 test_noStatistics() async { | |
| 231 await _doDrive(path.join('data', 'test_file.dart')); | |
| 232 // Should not print statistics summary. | |
| 233 expect(outSink.toString(), isEmpty); | |
| 234 expect(errorSink.toString(), isEmpty); | |
| 235 expect(exitCode, 0); | |
| 236 } | |
| 237 | |
| 238 Future<Null> _doDrive(String path, | |
| 239 {String uri, List<String> additionalArgs: const []}) async { | |
| 240 uri ??= 'file:///test_file.dart'; | |
| 241 var optionsFileName = AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE; | |
| 242 await drive('$uri|$path', | |
| 243 args: [ | |
| 244 '--dart-sdk', | |
| 245 _findSdkDirForSummaries(), | |
| 246 '--build-mode', | |
| 247 '--format=machine' | |
| 248 ]..addAll(additionalArgs), | |
| 249 options: 'data/options_tests_project/$optionsFileName'); | |
| 250 } | |
| 251 | |
| 252 /// Try to find a appropriate directory to pass to "--dart-sdk" that will | |
| 253 /// allow summaries to be found. | |
| 254 String _findSdkDirForSummaries() { | |
| 255 Set<String> triedDirectories = new Set<String>(); | |
| 256 bool isSuitable(String sdkDir) { | |
| 257 triedDirectories.add(sdkDir); | |
| 258 return new File(path.join(sdkDir, 'lib', '_internal', 'spec.sum')) | |
| 259 .existsSync(); | |
| 260 } | |
| 261 | |
| 262 // Usually the sdk directory is the parent of the parent of the "dart" | |
| 263 // executable. | |
| 264 Directory executableParent = new File(Platform.executable).parent; | |
| 265 Directory executableGrandparent = executableParent.parent; | |
| 266 if (isSuitable(executableGrandparent.path)) { | |
| 267 return executableGrandparent.path; | |
| 268 } | |
| 269 // During build bot execution, the sdk directory is simply the parent of the | |
| 270 // "dart" executable. | |
| 271 if (isSuitable(executableParent.path)) { | |
| 272 return executableParent.path; | |
| 273 } | |
| 274 // If neither of those are suitable, assume we are running locally within th e | |
| 275 // SDK project (e.g. within an IDE). Find the build output directory and | |
| 276 // search all built configurations. | |
| 277 Directory sdkRootDir = | |
| 278 new File(Platform.script.toFilePath()).parent.parent.parent.parent; | |
| 279 for (String outDirName in ['out', 'xcodebuild']) { | |
| 280 Directory outDir = new Directory(path.join(sdkRootDir.path, outDirName)); | |
| 281 if (outDir.existsSync()) { | |
| 282 for (FileSystemEntity subdir in outDir.listSync()) { | |
| 283 if (subdir is Directory) { | |
| 284 String candidateSdkDir = path.join(subdir.path, 'dart-sdk'); | |
| 285 if (isSuitable(candidateSdkDir)) { | |
| 286 return candidateSdkDir; | |
| 287 } | |
| 301 } | 288 } |
| 302 | 289 } |
| 303 // Analyze package:bbb/b.dart and compute summary. | 290 } |
| 304 { | 291 } |
| 305 await doDrive(bDart, uri: bUri, additionalArgs: [ | 292 throw new Exception('Could not find an SDK directory containing summaries.' |
| 306 '--build-summary-input=$aSum', | 293 ' Tried: ${triedDirectories.toList()}'); |
| 307 '--build-summary-output=$bSum' | 294 } |
| 308 ]); | 295 } |
| 309 expect(exitCode, 0); | 296 |
| 310 var bytes = new File(bSum).readAsBytesSync(); | 297 @reflectiveTest |
| 311 var bundle = new PackageBundle.fromBuffer(bytes); | 298 class ExitCodesTest extends _BaseTest { |
| 312 expect(bundle.unlinkedUnitUris, equals([bUri])); | 299 test_bazelWorkspace_relativePath() async { |
| 313 expect(bundle.linkedLibraryUris, equals([bUri])); | 300 // Copy to temp dir so that existing analysis options |
| 314 } | 301 // in the test directory hierarchy do not interfere |
| 315 | 302 await withTempDirAsync((String tempDirPath) async { |
| 316 // Analyze package:ccc/c.dart and compute summary. | 303 String dartSdkPath = path.absolute(getSdkPath()); |
| 317 { | 304 await recursiveCopy( |
| 318 await doDrive(cDart, uri: cUri, additionalArgs: [ | 305 new Directory(path.join(testDirectory, 'data', 'bazel')), |
| 319 '--build-summary-input=$aSum,$bSum', | 306 tempDirPath); |
| 320 '--build-summary-output=$cSum' | 307 Directory origWorkingDir = Directory.current; |
| 321 ]); | 308 try { |
| 322 expect(exitCode, 0); | 309 Directory.current = path.join(tempDirPath, 'proj'); |
| 323 var bytes = new File(cSum).readAsBytesSync(); | 310 Driver driver = new Driver(isTesting: true); |
| 324 var bundle = new PackageBundle.fromBuffer(bytes); | 311 try { |
| 325 expect(bundle.unlinkedUnitUris, equals([cUri])); | 312 await driver.start([ |
| 326 expect(bundle.linkedLibraryUris, equals([cUri])); | 313 path.join('lib', 'file.dart'), |
| 327 } | 314 '--dart-sdk', |
| 328 }); | 315 dartSdkPath, |
| 329 }); | |
| 330 | |
| 331 test('Error - unlinked summary as linked', () async { | |
| 332 await withTempDirAsync((tempDir) async { | |
| 333 var aDart = path.join(tempDir, 'a.dart'); | |
| 334 var bDart = path.join(tempDir, 'b.dart'); | |
| 335 | |
| 336 var aUri = 'package:aaa/a.dart'; | |
| 337 var bUri = 'package:bbb/b.dart'; | |
| 338 | |
| 339 var aSum = path.join(tempDir, 'a.sum'); | |
| 340 var bSum = path.join(tempDir, 'b.sum'); | |
| 341 | |
| 342 new File(aDart).writeAsStringSync('class A {}'); | |
| 343 | |
| 344 // Build unlinked a.sum | |
| 345 await doDrive(aDart, uri: aUri, additionalArgs: [ | |
| 346 '--build-summary-only', | |
| 347 '--build-summary-only-unlinked', | |
| 348 '--build-summary-output=$aSum' | |
| 349 ]); | 316 ]); |
| 350 expect(new File(aSum).existsSync(), isTrue); | 317 } catch (e) { |
| 351 | 318 print('=== debug info ==='); |
| 352 // Try to consume unlinked a.sum as linked. | 319 print('dartSdkPath: $dartSdkPath'); |
| 353 try { | 320 print('stderr:\n${errorSink.toString()}'); |
| 354 await doDrive(bDart, uri: bUri, additionalArgs: [ | 321 rethrow; |
| 355 '--build-summary-input=$aSum', | 322 } |
| 356 '--build-summary-output=$bSum' | 323 expect(errorSink.toString(), isEmpty); |
| 357 ]); | 324 expect(outSink.toString(), contains('No issues found')); |
| 358 fail('ArgumentError expected.'); | 325 expect(exitCode, 0); |
| 359 } on ArgumentError catch (e) { | 326 } finally { |
| 360 expect(e.message, | 327 Directory.current = origWorkingDir; |
| 361 contains('Got an unlinked summary for --build-summary-input')); | 328 } |
| 362 } | |
| 363 }); | |
| 364 }); | |
| 365 | |
| 366 test('Error - linked summary as unlinked', () async { | |
| 367 await withTempDirAsync((tempDir) async { | |
| 368 var aDart = path.join(tempDir, 'a.dart'); | |
| 369 var bDart = path.join(tempDir, 'b.dart'); | |
| 370 | |
| 371 var aUri = 'package:aaa/a.dart'; | |
| 372 var bUri = 'package:bbb/b.dart'; | |
| 373 | |
| 374 var aSum = path.join(tempDir, 'a.sum'); | |
| 375 var bSum = path.join(tempDir, 'b.sum'); | |
| 376 | |
| 377 new File(aDart).writeAsStringSync('class A {}'); | |
| 378 | |
| 379 // Build linked a.sum | |
| 380 await doDrive(aDart, uri: aUri, additionalArgs: [ | |
| 381 '--build-summary-only', | |
| 382 '--build-summary-output=$aSum' | |
| 383 ]); | |
| 384 expect(new File(aSum).existsSync(), isTrue); | |
| 385 | |
| 386 // Try to consume linked a.sum as unlinked. | |
| 387 try { | |
| 388 await doDrive(bDart, uri: bUri, additionalArgs: [ | |
| 389 '--build-summary-unlinked-input=$aSum', | |
| 390 '--build-summary-output=$bSum' | |
| 391 ]); | |
| 392 fail('ArgumentError expected.'); | |
| 393 } on ArgumentError catch (e) { | |
| 394 expect( | |
| 395 e.message, | |
| 396 contains( | |
| 397 'Got a linked summary for --build-summary-input-unlinked')); | |
| 398 } | |
| 399 }); | |
| 400 }); | |
| 401 | |
| 402 test('Linked summary', () async { | |
| 403 await withTempDirAsync((tempDir) async { | |
| 404 var outputPath = path.join(tempDir, 'test_file.dart.sum'); | |
| 405 await doDrive(path.join('data', 'test_file.dart'), additionalArgs: [ | |
| 406 '--build-summary-only', | |
| 407 '--build-summary-output=$outputPath' | |
| 408 ]); | |
| 409 var output = new File(outputPath); | |
| 410 expect(output.existsSync(), isTrue); | |
| 411 PackageBundle bundle = | |
| 412 new PackageBundle.fromBuffer(await output.readAsBytes()); | |
| 413 var testFileUri = 'file:///test_file.dart'; | |
| 414 expect(bundle.unlinkedUnitUris, equals([testFileUri])); | |
| 415 expect(bundle.linkedLibraryUris, equals([testFileUri])); | |
| 416 expect(exitCode, 0); | |
| 417 }); | |
| 418 }); | |
| 419 | |
| 420 test('Unlinked summary only', () async { | |
| 421 await withTempDirAsync((tempDir) async { | |
| 422 var outputPath = path.join(tempDir, 'test_file.dart.sum'); | |
| 423 await doDrive(path.join('data', 'test_file.dart'), additionalArgs: [ | |
| 424 '--build-summary-only', | |
| 425 '--build-summary-only-unlinked', | |
| 426 '--build-summary-output=$outputPath' | |
| 427 ]); | |
| 428 var output = new File(outputPath); | |
| 429 expect(output.existsSync(), isTrue); | |
| 430 PackageBundle bundle = | |
| 431 new PackageBundle.fromBuffer(await output.readAsBytes()); | |
| 432 var testFileUri = 'file:///test_file.dart'; | |
| 433 expect(bundle.unlinkedUnits.length, 1); | |
| 434 expect(bundle.unlinkedUnitUris, equals([testFileUri])); | |
| 435 expect(bundle.linkedLibraryUris, isEmpty); | |
| 436 expect(exitCode, 0); | |
| 437 }); | |
| 438 }); | |
| 439 }); | 329 }); |
| 440 } | 330 } |
| 441 | 331 |
| 442 createTests('old', AnalysisEngine.ANALYSIS_OPTIONS_FILE); | 332 test_enableAssertInitializer() async { |
| 443 createTests('new', AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE); | 333 await drive('data/file_with_assert_initializers.dart', |
| 334 args: ['--enable-assert-initializers']); | |
| 335 expect(exitCode, 0); | |
| 336 } | |
| 337 | |
| 338 test_fatalErrors() async { | |
| 339 await drive('data/file_with_error.dart'); | |
| 340 expect(exitCode, 3); | |
| 341 } | |
| 342 | |
| 343 test_fatalHints() async { | |
| 344 await drive('data/file_with_hint.dart', args: ['--fatal-hints']); | |
| 345 expect(exitCode, 1); | |
| 346 } | |
| 347 | |
| 348 test_fatalWarnings() async { | |
| 349 await drive('data/file_with_warning.dart', args: ['--fatal-warnings']); | |
| 350 expect(exitCode, 2); | |
| 351 } | |
| 352 | |
| 353 test_missingDartFile() async { | |
| 354 await drive('data/NO_DART_FILE_HERE.dart'); | |
| 355 expect(exitCode, 3); | |
| 356 } | |
| 357 | |
| 358 test_missingOptionsFile() async { | |
| 359 await drive('data/test_file.dart', options: 'data/NO_OPTIONS_HERE'); | |
| 360 expect(exitCode, 3); | |
| 361 } | |
| 362 | |
| 363 test_notFatalHints() async { | |
| 364 await drive('data/file_with_hint.dart'); | |
| 365 expect(exitCode, 0); | |
| 366 } | |
| 367 | |
| 368 test_notFatalWarnings() async { | |
| 369 await drive('data/file_with_warning.dart'); | |
| 370 expect(exitCode, 0); | |
| 371 } | |
| 372 | |
| 373 test_partFile() async { | |
| 374 Driver driver = new Driver(isTesting: true); | |
| 375 await driver.start([ | |
| 376 path.join(testDirectory, 'data/library_and_parts/lib.dart'), | |
| 377 path.join(testDirectory, 'data/library_and_parts/part1.dart') | |
| 378 ]); | |
| 379 expect(exitCode, 0); | |
| 380 } | |
| 381 | |
| 382 test_partFile_dangling() async { | |
| 383 await drive('data/library_and_parts/part2.dart'); | |
| 384 expect(exitCode, 3); | |
| 385 } | |
| 386 | |
| 387 test_partFile_extra() async { | |
| 388 Driver driver = new Driver(isTesting: true); | |
| 389 await driver.start([ | |
| 390 path.join(testDirectory, 'data/library_and_parts/lib.dart'), | |
| 391 path.join(testDirectory, 'data/library_and_parts/part1.dart'), | |
| 392 path.join(testDirectory, 'data/library_and_parts/part2.dart') | |
| 393 ]); | |
| 394 expect(exitCode, 3); | |
| 395 } | |
| 444 } | 396 } |
| 445 | 397 |
| 446 void _test_exitCodes() { | 398 @reflectiveTest |
| 447 group('exit codes', () { | 399 class LinterTest extends _BaseTest { |
| 448 test('fatal hints', () async { | 400 String get optionsFileName => AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE; |
| 449 await drive('data/file_with_hint.dart', args: ['--fatal-hints']); | 401 |
| 450 expect(exitCode, 1); | 402 test_containsLintRuleEntry() async { |
| 451 }); | |
| 452 | |
| 453 test('not fatal hints', () async { | |
| 454 await drive('data/file_with_hint.dart'); | |
| 455 expect(exitCode, 0); | |
| 456 }); | |
| 457 | |
| 458 test('fatal errors', () async { | |
| 459 await drive('data/file_with_error.dart'); | |
| 460 expect(exitCode, 3); | |
| 461 }); | |
| 462 | |
| 463 test('not fatal warnings', () async { | |
| 464 await drive('data/file_with_warning.dart'); | |
| 465 expect(exitCode, 0); | |
| 466 }); | |
| 467 | |
| 468 test('fatal warnings', () async { | |
| 469 await drive('data/file_with_warning.dart', args: ['--fatal-warnings']); | |
| 470 expect(exitCode, 2); | |
| 471 }); | |
| 472 | |
| 473 test('not parse enableAssertInitializer', () async { | |
| 474 await drive('data/file_with_assert_initializers.dart', | |
| 475 args: ['--enable-assert-initializers']); | |
| 476 expect(exitCode, 0); | |
| 477 }); | |
| 478 | |
| 479 test('missing options file', () async { | |
| 480 await drive('data/test_file.dart', options: 'data/NO_OPTIONS_HERE'); | |
| 481 expect(exitCode, 3); | |
| 482 }); | |
| 483 | |
| 484 test('missing dart file', () async { | |
| 485 await drive('data/NO_DART_FILE_HERE.dart'); | |
| 486 expect(exitCode, 3); | |
| 487 }); | |
| 488 | |
| 489 test('part file', () async { | |
| 490 await drive('data/library_and_parts/part2.dart'); | |
| 491 expect(exitCode, 3); | |
| 492 }); | |
| 493 | |
| 494 test('non-dangling part file', () async { | |
| 495 Driver driver = new Driver(isTesting: true); | |
| 496 await driver.start([ | |
| 497 path.join(testDirectory, 'data/library_and_parts/lib.dart'), | |
| 498 path.join(testDirectory, 'data/library_and_parts/part1.dart') | |
| 499 ]); | |
| 500 expect(exitCode, 0); | |
| 501 }); | |
| 502 | |
| 503 test('extra part file', () async { | |
| 504 Driver driver = new Driver(isTesting: true); | |
| 505 await driver.start([ | |
| 506 path.join(testDirectory, 'data/library_and_parts/lib.dart'), | |
| 507 path.join(testDirectory, 'data/library_and_parts/part1.dart'), | |
| 508 path.join(testDirectory, 'data/library_and_parts/part2.dart') | |
| 509 ]); | |
| 510 expect(exitCode, 3); | |
| 511 }); | |
| 512 | |
| 513 test('bazel workspace relative path', () async { | |
| 514 // Copy to temp dir so that existing analysis options | |
| 515 // in the test directory hierarchy do not interfere | |
| 516 await withTempDirAsync((String tempDirPath) async { | |
| 517 String dartSdkPath = path.absolute(getSdkPath()); | |
| 518 await recursiveCopy( | |
| 519 new Directory(path.join(testDirectory, 'data', 'bazel')), | |
| 520 tempDirPath); | |
| 521 Directory origWorkingDir = Directory.current; | |
| 522 try { | |
| 523 Directory.current = path.join(tempDirPath, 'proj'); | |
| 524 Driver driver = new Driver(isTesting: true); | |
| 525 try { | |
| 526 await driver.start([ | |
| 527 path.join('lib', 'file.dart'), | |
| 528 '--dart-sdk', | |
| 529 dartSdkPath, | |
| 530 ]); | |
| 531 } catch (e) { | |
| 532 print('=== debug info ==='); | |
| 533 print('dartSdkPath: $dartSdkPath'); | |
| 534 print('stderr:\n${errorSink.toString()}'); | |
| 535 rethrow; | |
| 536 } | |
| 537 expect(errorSink.toString(), isEmpty); | |
| 538 expect(outSink.toString(), contains('No issues found')); | |
| 539 expect(exitCode, 0); | |
| 540 } finally { | |
| 541 Directory.current = origWorkingDir; | |
| 542 } | |
| 543 }); | |
| 544 }); | |
| 545 }); | |
| 546 } | |
| 547 | |
| 548 void _test_linter() { | |
| 549 test('containsLintRuleEntry', () { | |
| 550 Map<String, YamlNode> options; | 403 Map<String, YamlNode> options; |
| 551 options = parseOptions(''' | 404 options = _parseOptions(''' |
| 552 linter: | 405 linter: |
| 553 rules: | 406 rules: |
| 554 - foo | 407 - foo |
| 555 '''); | 408 '''); |
| 556 expect(containsLintRuleEntry(options), true); | 409 expect(containsLintRuleEntry(options), true); |
| 557 options = parseOptions(''' | 410 options = _parseOptions(''' |
| 558 '''); | 411 '''); |
| 559 expect(containsLintRuleEntry(options), false); | 412 expect(containsLintRuleEntry(options), false); |
| 560 options = parseOptions(''' | 413 options = _parseOptions(''' |
| 561 linter: | 414 linter: |
| 562 rules: | 415 rules: |
| 563 # - foo | 416 # - foo |
| 564 '''); | 417 '''); |
| 565 expect(containsLintRuleEntry(options), true); | 418 expect(containsLintRuleEntry(options), true); |
| 566 options = parseOptions(''' | 419 options = _parseOptions(''' |
| 567 linter: | 420 linter: |
| 568 # rules: | 421 # rules: |
| 569 # - foo | 422 # - foo |
| 570 '''); | 423 '''); |
| 571 expect(containsLintRuleEntry(options), false); | 424 expect(containsLintRuleEntry(options), false); |
| 572 }); | 425 } |
| 573 | 426 |
| 574 group('linter', () { | 427 test_defaultLints_generatedLints() async { |
| 575 void createTests(String designator, String optionsFileName) { | 428 await _runLinter_defaultLints(); |
| 576 group('lints in options - $designator', () { | 429 expect(_bulletToDash(outSink), |
| 577 // Shared lint command. | 430 contains('lint - Name types using UpperCamelCase')); |
| 578 Future<Null> runLinter() async { | 431 } |
| 579 return await drive('data/linter_project/test_file.dart', | 432 |
| 580 options: 'data/linter_project/$optionsFileName', | 433 test_defaultLints_getsDefaultLints() async { |
| 581 args: ['--lints']); | 434 await _runLinter_defaultLints(); |
| 582 } | 435 |
| 583 | 436 /// Lints should be enabled. |
| 584 test('gets analysis options', () async { | 437 expect(driver.context.analysisOptions.lint, isTrue); |
| 585 await runLinter(); | 438 |
| 586 | 439 /// Default list should include camel_case_types. |
| 587 /// Lints should be enabled. | 440 var lintNames = getLints(driver.context).map((r) => r.name); |
| 588 expect(driver.context.analysisOptions.lint, isTrue); | 441 expect(lintNames, contains('camel_case_types')); |
| 589 | 442 } |
| 590 /// The analysis options file only specifies 'camel_case_types'. | 443 |
| 591 var lintNames = getLints(driver.context).map((r) => r.name); | 444 test_lintsInOptions_generatedLints() async { |
| 592 expect(lintNames, orderedEquals(['camel_case_types'])); | 445 await _runLinter_lintsInOptions(); |
| 593 }); | 446 expect(_bulletToDash(outSink), |
| 594 | 447 contains('lint - Name types using UpperCamelCase')); |
| 595 test('generates lints', () async { | 448 } |
| 596 await runLinter(); | 449 |
| 597 expect(_bulletToDash(outSink), | 450 test_lintsInOptions_getAnalysisOptions() async { |
| 598 contains('lint - Name types using UpperCamelCase')); | 451 await _runLinter_lintsInOptions(); |
| 599 }); | 452 |
| 600 }); | 453 /// Lints should be enabled. |
| 601 | 454 expect(driver.context.analysisOptions.lint, isTrue); |
| 602 group('default lints - $designator', () { | 455 |
| 603 // Shared lint command. | 456 /// The analysis options file only specifies 'camel_case_types'. |
| 604 Future<Null> runLinter() async { | 457 var lintNames = getLints(driver.context).map((r) => r.name); |
| 605 return await drive('data/linter_project/test_file.dart', | 458 expect(lintNames, orderedEquals(['camel_case_types'])); |
| 606 options: 'data/linter_project/$optionsFileName', | 459 } |
| 607 args: ['--lints']); | 460 |
| 608 } | 461 test_noLints_lintsDisabled() async { |
| 609 | 462 await _runLinter_noLintsFlag(); |
| 610 test('gets default lints', () async { | 463 expect(driver.context.analysisOptions.lint, isFalse); |
| 611 await runLinter(); | 464 } |
| 612 | 465 |
| 613 /// Lints should be enabled. | 466 test_noLints_noGeneratedWarnings() async { |
| 614 expect(driver.context.analysisOptions.lint, isTrue); | 467 await _runLinter_noLintsFlag(); |
| 615 | 468 expect(outSink.toString(), contains('No issues found')); |
| 616 /// Default list should include camel_case_types. | 469 } |
| 617 var lintNames = getLints(driver.context).map((r) => r.name); | 470 |
| 618 expect(lintNames, contains('camel_case_types')); | 471 test_noLints_noRegisteredLints() async { |
| 619 }); | 472 await _runLinter_noLintsFlag(); |
| 620 | 473 expect(getLints(driver.context), isEmpty); |
| 621 test('generates lints', () async { | 474 } |
| 622 await runLinter(); | 475 |
| 623 expect(_bulletToDash(outSink), | 476 Map<String, YamlNode> _parseOptions(String src) => |
| 624 contains('lint - Name types using UpperCamelCase')); | 477 new AnalysisOptionsProvider().getOptionsFromString(src); |
| 625 }); | 478 |
| 626 }); | 479 Future<Null> _runLinter_defaultLints() async { |
| 627 | 480 await drive('data/linter_project/test_file.dart', |
| 628 group('no `--lints` flag (none in options) - $designator', () { | 481 options: 'data/linter_project/$optionsFileName', args: ['--lints']); |
| 629 // Shared lint command. | 482 } |
| 630 Future<Null> runLinter() async { | 483 |
| 631 return await drive('data/no_lints_project/test_file.dart', | 484 Future<Null> _runLinter_lintsInOptions() async { |
| 632 options: 'data/no_lints_project/$optionsFileName'); | 485 await drive('data/linter_project/test_file.dart', |
| 633 } | 486 options: 'data/linter_project/$optionsFileName', args: ['--lints']); |
| 634 | 487 } |
| 635 test('lints disabled', () async { | 488 |
| 636 await runLinter(); | 489 Future<Null> _runLinter_noLintsFlag() async { |
| 637 expect(driver.context.analysisOptions.lint, isFalse); | 490 await drive('data/no_lints_project/test_file.dart', |
| 638 }); | 491 options: 'data/no_lints_project/$optionsFileName'); |
| 639 | 492 } |
| 640 test('no registered lints', () async { | |
| 641 await runLinter(); | |
| 642 expect(getLints(driver.context), isEmpty); | |
| 643 }); | |
| 644 | |
| 645 test('no generated warnings', () async { | |
| 646 await runLinter(); | |
| 647 expect(outSink.toString(), contains('No issues found')); | |
| 648 }); | |
| 649 }); | |
| 650 } | |
| 651 | |
| 652 createTests('old', AnalysisEngine.ANALYSIS_OPTIONS_FILE); | |
| 653 createTests('new', AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE); | |
| 654 }); | |
| 655 } | 493 } |
| 656 | 494 |
| 657 void _test_optionsProcessing() { | 495 @reflectiveTest |
| 658 group('options processing', () { | 496 class OptionsTest extends _BaseTest { |
| 659 void createTests(String designator, String optionsFileName) { | 497 String get optionsFileName => AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE; |
| 660 group('basic config - $designator', () { | 498 |
| 661 // Shared driver command. | 499 List<ErrorProcessor> get processors => |
| 662 Future<Null> doDrive() async { | 500 driver.context.analysisOptions.errorProcessors; |
| 663 await drive('data/options_tests_project/test_file.dart', | 501 |
| 664 options: 'data/options_tests_project/$optionsFileName'); | 502 ErrorProcessor processorFor(AnalysisError error) => |
| 665 } | 503 processors.firstWhere((p) => p.appliesTo(error)); |
| 666 | 504 |
| 667 test('filters', () async { | 505 test_basic_filters() async { |
| 668 await doDrive(); | 506 await _driveBasic(); |
| 669 expect(processors, hasLength(3)); | 507 expect(processors, hasLength(3)); |
| 670 | 508 |
| 671 // unused_local_variable: ignore | 509 // unused_local_variable: ignore |
| 672 var unused_local_variable = new AnalysisError( | 510 var unused_local_variable = new AnalysisError( |
| 673 new TestSource(), 0, 1, HintCode.UNUSED_LOCAL_VARIABLE, [ | 511 new TestSource(), 0, 1, HintCode.UNUSED_LOCAL_VARIABLE, [ |
| 674 ['x'] | 512 ['x'] |
| 675 ]); | 513 ]); |
| 676 expect(processorFor(unused_local_variable).severity, isNull); | 514 expect(processorFor(unused_local_variable).severity, isNull); |
| 677 | 515 |
| 678 // missing_return: error | 516 // missing_return: error |
| 679 var missing_return = new AnalysisError( | 517 var missing_return = |
| 680 new TestSource(), 0, 1, HintCode.MISSING_RETURN, [ | 518 new AnalysisError(new TestSource(), 0, 1, HintCode.MISSING_RETURN, [ |
| 681 ['x'] | 519 ['x'] |
| 682 ]); | 520 ]); |
| 683 expect(processorFor(missing_return).severity, ErrorSeverity.ERROR); | 521 expect(processorFor(missing_return).severity, ErrorSeverity.ERROR); |
| 684 expect( | 522 expect(_bulletToDash(outSink), |
| 685 _bulletToDash(outSink), | 523 contains("error - This function declares a return type of 'int'")); |
| 686 contains( | 524 expect(outSink.toString(), contains("1 error and 1 warning found.")); |
| 687 "error - This function declares a return type of 'int'")); | 525 } |
| 688 expect(outSink.toString(), contains("1 error and 1 warning found.")); | 526 |
| 689 }); | 527 test_basic_language() async { |
| 690 | 528 await _driveBasic(); |
| 691 test('language', () async { | 529 expect(driver.context.analysisOptions.enableSuperMixins, isTrue); |
| 692 await doDrive(); | 530 } |
| 693 expect(driver.context.analysisOptions.enableSuperMixins, isTrue); | 531 |
| 694 }); | 532 test_basic_strongMode() async { |
| 695 | 533 await _driveBasic(); |
| 696 test('strongMode', () async { | 534 expect(driver.context.analysisOptions.strongMode, isTrue); |
| 697 await doDrive(); | 535 // https://github.com/dart-lang/sdk/issues/26129 |
| 698 expect(driver.context.analysisOptions.strongMode, isTrue); | 536 AnalysisContext sdkContext = driver.context.sourceFactory.dartSdk.context; |
| 699 //https://github.com/dart-lang/sdk/issues/26129 | 537 expect(sdkContext.analysisOptions.strongMode, isTrue); |
| 700 AnalysisContext sdkContext = | 538 } |
| 701 driver.context.sourceFactory.dartSdk.context; | 539 |
| 702 expect(sdkContext.analysisOptions.strongMode, isTrue); | 540 test_includeDirective() async { |
| 703 }); | 541 String testDir = path.join( |
| 704 }); | 542 testDirectory, 'data', 'options_include_directive_tests_project'); |
| 705 | 543 await drive( |
| 706 group('with flags - $designator', () { | 544 path.join(testDir, 'lib', 'test_file.dart'), |
| 707 // Shared driver command. | 545 args: [ |
| 708 Future<Null> doDrive() async { | 546 '--fatal-warnings', |
| 709 await drive('data/options_tests_project/test_file.dart', | 547 '--packages', |
| 710 args: ['--fatal-warnings'], | 548 path.join(testDir, '_packages'), |
| 711 options: 'data/options_tests_project/$optionsFileName'); | 549 ], |
| 712 } | 550 options: path.join(testDir, 'analysis_options.yaml'), |
| 713 | 551 ); |
| 714 test('override fatal warning', () async { | 552 expect(exitCode, 3); |
| 715 await doDrive(); | 553 expect(outSink.toString(), |
| 716 // missing_return: error | 554 contains('but doesn\'t end with a return statement')); |
| 717 var undefined_function = new AnalysisError(new TestSource(), 0, 1, | 555 expect(outSink.toString(), contains('isn\'t defined')); |
| 718 StaticTypeWarningCode.UNDEFINED_FUNCTION, [ | 556 expect(outSink.toString(), contains('Avoid empty else statements')); |
| 719 ['x'] | 557 } |
| 720 ]); | 558 |
| 721 expect( | 559 test_strongSdk() async { |
| 722 processorFor(undefined_function).severity, ErrorSeverity.WARNING); | 560 String testDir = path.join(testDirectory, 'data', 'strong_sdk'); |
| 723 // Should not be made fatal by `--fatal-warnings`. | 561 await drive(path.join(testDir, 'main.dart'), args: ['--strong']); |
| 724 expect(_bulletToDash(outSink), | 562 expect(driver.context.analysisOptions.strongMode, isTrue); |
| 725 contains("warning - The function 'baz' isn't defined")); | 563 expect(outSink.toString(), contains('No issues found')); |
| 726 expect(outSink.toString(), contains("1 error and 1 warning found.")); | 564 expect(exitCode, 0); |
| 727 }); | 565 } |
| 728 }); | 566 |
| 729 } | 567 test_todo() async { |
| 730 | 568 await drive('data/file_with_todo.dart'); |
| 731 createTests('old', AnalysisEngine.ANALYSIS_OPTIONS_FILE); | 569 expect(outSink.toString().contains('[info]'), isFalse); |
| 732 createTests('new', AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE); | 570 } |
| 733 | 571 |
| 734 test('include directive', () async { | 572 test_withFlags_overrideFatalWarning() async { |
| 735 String testDir = path.join( | 573 await drive('data/options_tests_project/test_file.dart', |
| 736 testDirectory, 'data', 'options_include_directive_tests_project'); | 574 args: ['--fatal-warnings'], |
| 737 await drive( | 575 options: 'data/options_tests_project/$optionsFileName'); |
| 738 path.join(testDir, 'lib', 'test_file.dart'), | 576 |
| 739 args: [ | 577 // missing_return: error |
| 740 '--fatal-warnings', | 578 var undefined_function = new AnalysisError( |
| 741 '--packages', | 579 new TestSource(), 0, 1, StaticTypeWarningCode.UNDEFINED_FUNCTION, [ |
| 742 path.join(testDir, '_packages'), | 580 ['x'] |
| 743 ], | 581 ]); |
| 744 options: path.join(testDir, 'analysis_options.yaml'), | 582 expect(processorFor(undefined_function).severity, ErrorSeverity.WARNING); |
| 745 ); | 583 // Should not be made fatal by `--fatal-warnings`. |
| 746 expect(exitCode, 3); | 584 expect(_bulletToDash(outSink), |
| 747 expect(outSink.toString(), | 585 contains("warning - The function 'baz' isn't defined")); |
| 748 contains('but doesn\'t end with a return statement')); | 586 expect(outSink.toString(), contains("1 error and 1 warning found.")); |
| 749 expect(outSink.toString(), contains('isn\'t defined')); | 587 } |
| 750 expect(outSink.toString(), contains('Avoid empty else statements')); | 588 |
| 751 }); | 589 Future<Null> _driveBasic() async { |
| 752 | 590 await drive('data/options_tests_project/test_file.dart', |
| 753 test('test strong SDK', () async { | 591 options: 'data/options_tests_project/$optionsFileName'); |
| 754 String testDir = path.join(testDirectory, 'data', 'strong_sdk'); | 592 } |
| 755 await drive(path.join(testDir, 'main.dart'), args: ['--strong']); | |
| 756 expect(driver.context.analysisOptions.strongMode, isTrue); | |
| 757 expect(outSink.toString(), contains('No issues found')); | |
| 758 expect(exitCode, 0); | |
| 759 }); | |
| 760 }); | |
| 761 } | 593 } |
| 762 | 594 |
| 763 class TestSource implements Source { | 595 class TestSource implements Source { |
| 764 TestSource(); | 596 TestSource(); |
| 765 | 597 |
| 766 @override | 598 @override |
| 767 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); | 599 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); |
| 768 } | 600 } |
| 601 | |
| 602 class _BaseTest { | |
| 603 static const emptyOptionsFile = 'data/empty_options.yaml'; | |
| 604 | |
| 605 StringSink _savedOutSink, _savedErrorSink; | |
| 606 int _savedExitCode; | |
| 607 ExitHandler _savedExitHandler; | |
| 608 | |
| 609 Driver driver; | |
| 610 | |
| 611 /// Start a driver for the given [source], optionally providing additional | |
| 612 /// [args] and an [options] file path. The value of [options] defaults to | |
| 613 /// an empty options file to avoid unwanted configuration from an otherwise | |
| 614 /// discovered options file. | |
| 615 Future<Null> drive(String source, | |
| 616 {String options: emptyOptionsFile, | |
| 617 List<String> args: const <String>[]}) async { | |
| 618 driver = new Driver(isTesting: true); | |
| 619 var cmd = [ | |
| 620 '--options', | |
| 621 path.join(testDirectory, options), | |
| 622 _adjustFileSpec(source) | |
| 623 ]..addAll(args); | |
| 624 await driver.start(cmd); | |
| 625 } | |
| 626 | |
| 627 void setUp() { | |
| 628 ansi.runningTests = true; | |
| 629 _savedOutSink = outSink; | |
| 630 _savedErrorSink = errorSink; | |
| 631 _savedExitHandler = exitHandler; | |
| 632 _savedExitCode = exitCode; | |
| 633 exitHandler = (code) => exitCode = code; | |
| 634 outSink = new StringBuffer(); | |
| 635 errorSink = new StringBuffer(); | |
| 636 } | |
| 637 | |
| 638 void tearDown() { | |
| 639 outSink = _savedOutSink; | |
| 640 errorSink = _savedErrorSink; | |
| 641 exitCode = _savedExitCode; | |
| 642 exitHandler = _savedExitHandler; | |
| 643 ansi.runningTests = false; | |
| 644 } | |
| 645 | |
| 646 /// Convert a file specification from a relative path to an absolute path. | |
| 647 /// Handles the case where the file specification is of the form "$uri|$path". | |
| 648 String _adjustFileSpec(String fileSpec) { | |
| 649 int uriPrefixLength = fileSpec.indexOf('|') + 1; | |
| 650 String uriPrefix = fileSpec.substring(0, uriPrefixLength); | |
| 651 String relativePath = fileSpec.substring(uriPrefixLength); | |
| 652 return '$uriPrefix${path.join(testDirectory, relativePath)}'; | |
| 653 } | |
| 654 | |
| 655 /// Normalize text with bullets. | |
| 656 String _bulletToDash(item) => '$item'.replaceAll('•', '-'); | |
| 657 } | |
| OLD | NEW |