Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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.md file. | 3 // BSD-style license that can be found in the LICENSE.md file. |
| 4 | 4 |
| 5 library testing.analyze; | 5 library testing.analyze; |
| 6 | 6 |
| 7 import 'dart:async' show Stream, Future; | 7 import 'dart:async' show Stream, Future; |
| 8 | 8 |
| 9 import 'dart:convert' show LineSplitter, UTF8; | 9 import 'dart:convert' show LineSplitter, UTF8; |
| 10 | 10 |
| 11 import 'dart:io' show File, Process; | 11 import 'dart:io' show Directory, File, FileSystemEntity, Process, ProcessResult; |
| 12 | 12 |
| 13 import '../testing.dart' show startDart; | 13 import '../testing.dart' show startDart; |
| 14 | 14 |
| 15 import 'log.dart' show isVerbose; | 15 import 'log.dart' show isVerbose, splitLines; |
| 16 | 16 |
| 17 import 'suite.dart' show Suite; | 17 import 'suite.dart' show Suite; |
| 18 | 18 |
| 19 class Analyze extends Suite { | 19 class Analyze extends Suite { |
| 20 final Uri analysisOptions; | 20 final Uri analysisOptions; |
| 21 | 21 |
| 22 final List<Uri> uris; | 22 final List<Uri> uris; |
| 23 | 23 |
| 24 final List<RegExp> exclude; | 24 final List<RegExp> exclude; |
| 25 | 25 |
| 26 Analyze(this.analysisOptions, this.uris, this.exclude) | 26 final List<String> gitGrepPathspecs; |
| 27 | |
| 28 final List<String> gitGrepPatterns; | |
| 29 | |
| 30 Analyze(this.analysisOptions, this.uris, this.exclude, this.gitGrepPathspecs, | |
| 31 this.gitGrepPatterns) | |
| 27 : super("analyze", "analyze", null); | 32 : super("analyze", "analyze", null); |
| 28 | 33 |
| 29 Future<Null> run(Uri packages, List<Uri> extraUris) { | 34 Future<Null> run(Uri packages, List<Uri> extraUris) { |
| 30 List<Uri> allUris = new List<Uri>.from(uris); | 35 List<Uri> allUris = new List<Uri>.from(uris); |
| 31 if (extraUris != null) { | 36 if (extraUris != null) { |
| 32 allUris.addAll(extraUris); | 37 allUris.addAll(extraUris); |
| 33 } | 38 } |
| 34 return analyzeUris(analysisOptions, packages, allUris, exclude); | 39 return analyzeUris(analysisOptions, packages, allUris, exclude, |
| 40 gitGrepPathspecs, gitGrepPatterns); | |
| 35 } | 41 } |
| 36 | 42 |
| 37 static Future<Analyze> fromJsonMap( | 43 static Future<Analyze> fromJsonMap( |
| 38 Uri base, Map json, List<Suite> suites) async { | 44 Uri base, Map json, List<Suite> suites) async { |
| 39 String optionsPath = json["options"]; | 45 String optionsPath = json["options"]; |
| 40 Uri optionsUri = optionsPath == null ? null : base.resolve(optionsPath); | 46 Uri optionsUri = optionsPath == null ? null : base.resolve(optionsPath); |
| 41 | 47 |
| 42 List<Uri> uris = new List<Uri>.from( | 48 List<Uri> uris = new List<Uri>.from( |
| 43 json["uris"].map((String relative) => base.resolve(relative))); | 49 json["uris"].map((String relative) => base.resolve(relative))); |
| 44 | 50 |
| 45 List<RegExp> exclude = | 51 List<RegExp> exclude = |
| 46 new List<RegExp>.from(json["exclude"].map((String p) => new RegExp(p))); | 52 new List<RegExp>.from(json["exclude"].map((String p) => new RegExp(p))); |
| 47 | 53 |
| 48 return new Analyze(optionsUri, uris, exclude); | 54 Map gitGrep = json["git grep"]; |
| 55 List<String> gitGrepPathspecs; | |
| 56 List<String> gitGrepPatterns; | |
| 57 if (gitGrep != null) { | |
| 58 gitGrepPathspecs = gitGrep["pathspecs"] ?? const <String>["."]; | |
| 59 gitGrepPatterns = gitGrep["patterns"]; | |
| 60 } | |
| 61 | |
| 62 return new Analyze( | |
| 63 optionsUri, uris, exclude, gitGrepPathspecs, gitGrepPatterns); | |
| 49 } | 64 } |
| 50 | 65 |
| 51 String toString() => "Analyze($uris, $exclude)"; | 66 String toString() => "Analyze($uris, $exclude)"; |
| 52 } | 67 } |
| 53 | 68 |
| 54 class AnalyzerDiagnostic { | 69 class AnalyzerDiagnostic { |
| 55 final String kind; | 70 final String kind; |
| 56 | 71 |
| 57 final String detailedKind; | 72 final String detailedKind; |
| 58 | 73 |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 124 Stream<AnalyzerDiagnostic> parseAnalyzerOutput( | 139 Stream<AnalyzerDiagnostic> parseAnalyzerOutput( |
| 125 Stream<List<int>> stream) async* { | 140 Stream<List<int>> stream) async* { |
| 126 Stream<String> lines = | 141 Stream<String> lines = |
| 127 stream.transform(UTF8.decoder).transform(new LineSplitter()); | 142 stream.transform(UTF8.decoder).transform(new LineSplitter()); |
| 128 await for (String line in lines) { | 143 await for (String line in lines) { |
| 129 yield new AnalyzerDiagnostic.fromLine(line); | 144 yield new AnalyzerDiagnostic.fromLine(line); |
| 130 } | 145 } |
| 131 } | 146 } |
| 132 | 147 |
| 133 /// Run dartanalyzer on all tests in [uris]. | 148 /// Run dartanalyzer on all tests in [uris]. |
| 134 Future<Null> analyzeUris(Uri analysisOptions, Uri packages, List<Uri> uris, | 149 Future<Null> analyzeUris( |
| 135 List<RegExp> exclude) async { | 150 Uri analysisOptions, |
| 151 Uri packages, | |
| 152 List<Uri> uris, | |
| 153 List<RegExp> exclude, | |
| 154 List<String> gitGrepPathspecs, | |
| 155 List<String> gitGrepPatterns) async { | |
| 136 if (uris.isEmpty) return; | 156 if (uris.isEmpty) return; |
| 157 String topLevel; | |
| 158 try { | |
| 159 topLevel = Uri | |
| 160 .directory(await git("rev-parse", <String>["--show-toplevel"])) | |
| 161 .toFilePath(); | |
| 162 } catch (e) { | |
| 163 topLevel = Uri.base.toFilePath(); | |
| 164 } | |
| 165 | |
| 166 String toFilePath(Uri uri) { | |
| 167 String path = uri.toFilePath(); | |
| 168 return path.startsWith(topLevel) ? path.substring(topLevel.length) : path; | |
| 169 } | |
| 170 | |
| 171 Set<String> filesToAnalyze = new Set<String>(); | |
| 172 | |
| 173 for (Uri uri in uris) { | |
| 174 if (await new Directory.fromUri(uri).exists()) { | |
| 175 await for (FileSystemEntity entity in new Directory.fromUri(uri) | |
| 176 .list(recursive: true, followLinks: false)) { | |
| 177 if (entity is File && entity.path.endsWith(".dart")) { | |
| 178 filesToAnalyze.add(toFilePath(entity.uri)); | |
| 179 } | |
| 180 } | |
| 181 } else if (await new File.fromUri(uri).exists()) { | |
| 182 filesToAnalyze.add(toFilePath(uri)); | |
| 183 } else { | |
| 184 throw "File not found: ${uri}"; | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 if (gitGrepPatterns != null) { | |
| 189 print("Top level git dir is $topLevel"); | |
| 190 List<String> arguments = <String>["-l"]; | |
| 191 arguments.addAll( | |
| 192 gitGrepPatterns.expand((String pattern) => <String>["-e", pattern])); | |
| 193 arguments.add("--"); | |
| 194 arguments.addAll(gitGrepPathspecs); | |
| 195 filesToAnalyze.addAll(splitLines(await git("grep", arguments)) | |
| 196 .map((String line) => line.trimRight())); | |
| 197 } | |
| 198 | |
| 137 const String analyzerPath = "pkg/analyzer_cli/bin/analyzer.dart"; | 199 const String analyzerPath = "pkg/analyzer_cli/bin/analyzer.dart"; |
| 138 Uri analyzer = Uri.base.resolve(analyzerPath); | 200 Uri analyzer = Uri.base.resolve(analyzerPath); |
| 139 if (!await new File.fromUri(analyzer).exists()) { | 201 if (!await new File.fromUri(analyzer).exists()) { |
| 140 throw "Couldn't find '$analyzerPath' in '${Uri.base.toFilePath()}'"; | 202 throw "Couldn't find '$analyzerPath' in '${toFilePath(Uri.base)}'"; |
| 141 } | 203 } |
| 142 List<String> arguments = <String>[ | 204 List<String> arguments = <String>[ |
| 143 "--packages=${packages.toFilePath()}", | 205 "--packages=${toFilePath(packages)}", |
| 144 "--package-warnings", | |
| 145 "--format=machine", | 206 "--format=machine", |
| 146 "--dart-sdk=${Uri.base.resolve('sdk/').toFilePath()}", | 207 "--dart-sdk=${Uri.base.resolve('sdk/').toFilePath()}", |
| 147 ]; | 208 ]; |
| 148 if (analysisOptions != null) { | 209 if (analysisOptions != null) { |
| 149 arguments.add("--options=${analysisOptions.toFilePath()}"); | 210 arguments.add("--options=${toFilePath(analysisOptions)}"); |
| 150 } | 211 } |
| 151 arguments.addAll(uris.map((Uri uri) => uri.toFilePath())); | 212 |
| 213 arguments.addAll(filesToAnalyze); | |
| 152 if (isVerbose) { | 214 if (isVerbose) { |
| 153 print("Running:\n ${analyzer.toFilePath()} ${arguments.join(' ')}"); | 215 print("Running:\n ${toFilePath(analyzer)} ${arguments.join(' ')}"); |
| 154 } else { | 216 } else { |
| 155 print("Running dartanalyzer."); | 217 print("Running dartanalyzer."); |
| 156 } | 218 } |
| 157 Stopwatch sw = new Stopwatch()..start(); | 219 Stopwatch sw = new Stopwatch()..start(); |
| 158 Process process = await startDart(analyzer, arguments); | 220 Process process = await startDart(analyzer, arguments); |
| 159 process.stdin.close(); | 221 process.stdin.close(); |
| 160 Future stdoutFuture = parseAnalyzerOutput(process.stdout).toList(); | 222 |
| 161 Future stderrFuture = parseAnalyzerOutput(process.stderr).toList(); | |
| 162 await process.exitCode; | |
| 163 List<AnalyzerDiagnostic> diagnostics = <AnalyzerDiagnostic>[]; | |
| 164 diagnostics.addAll(await stdoutFuture); | |
| 165 diagnostics.addAll(await stderrFuture); | |
| 166 bool hasOutput = false; | 223 bool hasOutput = false; |
| 167 Set<String> seen = new Set<String>(); | 224 Set<String> seen = new Set<String>(); |
| 168 for (AnalyzerDiagnostic diagnostic in diagnostics) { | 225 |
| 169 String path = diagnostic.uri?.path; | 226 processAnalyzerOutput(Stream<AnalyzerDiagnostic> diagnostics) async { |
| 170 if (path != null && exclude.any((RegExp r) => path.contains(r))) { | 227 await for (AnalyzerDiagnostic diagnostic in diagnostics) { |
| 171 continue; | 228 if (diagnostic.uri != null) { |
| 172 } | 229 String path = toFilePath(diagnostic.uri); |
| 173 String message = "$diagnostic"; | 230 if (diagnostic.code.startsWith("STRONG_MODE") && |
| 174 if (seen.add(message)) { | 231 (path.startsWith("pkg/compiler/") || |
| 175 hasOutput = true; | 232 path.startsWith("tests/compiler/dart2js/"))) { |
| 176 print(message); | 233 // Hack to work around dart2js not being strong-mode clean. |
| 234 continue; | |
| 235 } | |
| 236 if (!filesToAnalyze.contains(path)) continue; | |
| 237 if (exclude.any((RegExp r) => path.contains(r))) { | |
| 238 continue; | |
| 239 } | |
| 240 } | |
| 241 String message = "$diagnostic"; | |
| 242 if (seen.add(message)) { | |
| 243 hasOutput = true; | |
| 244 print(message); | |
| 245 } | |
| 177 } | 246 } |
| 178 } | 247 } |
| 248 | |
| 249 Future stderrFuture = | |
| 250 processAnalyzerOutput(parseAnalyzerOutput(process.stderr)); | |
| 251 Future stdoutFuture = | |
| 252 processAnalyzerOutput(parseAnalyzerOutput(process.stdout)); | |
| 253 await process.exitCode; | |
| 254 await stdoutFuture; | |
| 255 await stderrFuture; | |
| 256 sw.stop(); | |
| 257 print("Running analyzer took: ${sw.elapsed}."); | |
| 179 if (hasOutput) { | 258 if (hasOutput) { |
| 180 throw "Non-empty output from analyzer."; | 259 throw "Non-empty output from analyzer."; |
| 181 } | 260 } |
| 182 sw.stop(); | |
| 183 print("Running analyzer took: ${sw.elapsed}."); | |
| 184 } | 261 } |
| 262 | |
| 263 Future<String> git(String command, Iterable<String> arguments, | |
| 264 {String workingDirectory}) async { | |
| 265 ProcessResult result = await Process.run( | |
| 266 "git", <String>[command]..addAll(arguments), | |
|
Johnni Winther
2017/05/23 14:02:53
This doesn't work on Windows (missing .exe on git)
ahe
2017/05/24 09:12:53
Done.
| |
| 267 workingDirectory: workingDirectory); | |
| 268 if (result.exitCode != 0) { | |
| 269 throw "Non-zero exit code from git $command (${result.exitCode})\n" | |
| 270 "${result.stdout}\n${result.stderr}"; | |
| 271 } | |
| 272 return result.stdout; | |
| 273 } | |
| OLD | NEW |