Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(44)

Side by Side Diff: packages/analyzer/test/stress/for_git_repository.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « packages/analyzer/test/src/util/yaml_test.dart ('k') | packages/analyzer/test/test_all.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library analyzer.test.stress.limited_invalidation;
6
7 import 'dart:async';
8 import 'dart:io';
9
10 import 'package:analyzer/dart/ast/ast.dart';
11 import 'package:analyzer/dart/element/element.dart';
12 import 'package:analyzer/dart/element/type.dart';
13 import 'package:analyzer/error/error.dart';
14 import 'package:analyzer/file_system/file_system.dart' as fs;
15 import 'package:analyzer/file_system/physical_file_system.dart';
16 import 'package:analyzer/src/context/builder.dart';
17 import 'package:analyzer/src/context/cache.dart';
18 import 'package:analyzer/src/context/context.dart';
19 import 'package:analyzer/src/dart/ast/utilities.dart';
20 import 'package:analyzer/src/dart/element/member.dart';
21 import 'package:analyzer/src/dart/sdk/sdk.dart';
22 import 'package:analyzer/src/generated/engine.dart';
23 import 'package:analyzer/src/generated/sdk.dart';
24 import 'package:analyzer/src/generated/source.dart';
25 import 'package:analyzer/src/generated/utilities_collection.dart';
26 import 'package:analyzer/src/task/dart.dart';
27 import 'package:analyzer/task/general.dart';
28 import 'package:analyzer/task/model.dart';
29 import 'package:path/path.dart' as path;
30 import 'package:unittest/unittest.dart';
31
32 main() {
33 new StressTest().run();
34 }
35
36 void _failTypeMismatch(Object actual, Object expected, {String reason}) {
37 String message = 'Actual $actual is ${actual.runtimeType}, '
38 'but expected $expected is ${expected.runtimeType}';
39 if (reason != null) {
40 message += ' $reason';
41 }
42 fail(message);
43 }
44
45 void _logPrint(String message) {
46 DateTime time = new DateTime.now();
47 print('$time: $message');
48 }
49
50 class FileInfo {
51 final String path;
52 final int modification;
53
54 FileInfo(this.path, this.modification);
55 }
56
57 class FolderDiff {
58 final List<String> added;
59 final List<String> changed;
60 final List<String> removed;
61
62 FolderDiff(this.added, this.changed, this.removed);
63
64 bool get isEmpty => added.isEmpty && changed.isEmpty && removed.isEmpty;
65 bool get isNotEmpty => !isEmpty;
66
67 @override
68 String toString() {
69 return '[added=$added, changed=$changed, removed=$removed]';
70 }
71 }
72
73 class FolderInfo {
74 final String path;
75 final List<FileInfo> files = <FileInfo>[];
76
77 FolderInfo(this.path) {
78 List<FileSystemEntity> entities =
79 new Directory(path).listSync(recursive: true);
80 for (FileSystemEntity entity in entities) {
81 if (entity is File) {
82 String path = entity.path;
83 if (path.contains('packages') || path.contains('.pub')) {
84 continue;
85 }
86 if (path.endsWith('.dart')) {
87 files.add(new FileInfo(
88 path, entity.lastModifiedSync().millisecondsSinceEpoch));
89 }
90 }
91 }
92 }
93
94 FolderDiff diff(FolderInfo oldFolder) {
95 Map<String, FileInfo> toMap(FolderInfo folder) {
96 Map<String, FileInfo> map = <String, FileInfo>{};
97 folder.files.forEach((file) {
98 map[file.path] = file;
99 });
100 return map;
101 }
102
103 Map<String, FileInfo> newFiles = toMap(this);
104 Map<String, FileInfo> oldFiles = toMap(oldFolder);
105 Set<String> addedPaths = newFiles.keys.toSet()..removeAll(oldFiles.keys);
106 Set<String> removedPaths = oldFiles.keys.toSet()..removeAll(newFiles.keys);
107 List<String> changedPaths = <String>[];
108 newFiles.forEach((path, newFile) {
109 FileInfo oldFile = oldFiles[path];
110 if (oldFile != null && oldFile.modification != newFile.modification) {
111 changedPaths.add(path);
112 }
113 });
114 return new FolderDiff(
115 addedPaths.toList(), changedPaths, removedPaths.toList());
116 }
117 }
118
119 class GitException {
120 final String message;
121 final String stdout;
122 final String stderr;
123
124 GitException(this.message)
125 : stdout = null,
126 stderr = null;
127
128 GitException.forProcessResult(this.message, ProcessResult processResult)
129 : stdout = processResult.stdout,
130 stderr = processResult.stderr;
131
132 @override
133 String toString() => '$message\n$stdout\n$stderr\n';
134 }
135
136 class GitRepository {
137 final String path;
138
139 GitRepository(this.path);
140
141 Future checkout(String hash) async {
142 // TODO(scheglov) use for updating only some files
143 if (hash.endsWith('hash')) {
144 List<String> filePaths = <String>[
145 '/Users/user/full/path/one.dart',
146 '/Users/user/full/path/two.dart',
147 ];
148 for (var filePath in filePaths) {
149 await Process.run('git', <String>['checkout', '-f', hash, filePath],
150 workingDirectory: path);
151 }
152 return;
153 }
154 ProcessResult processResult = await Process
155 .run('git', <String>['checkout', '-f', hash], workingDirectory: path);
156 _throwIfNotSuccess(processResult);
157 }
158
159 Future<List<GitRevision>> getRevisions({String after}) async {
160 List<String> args = <String>['log', '--format=%ct %H %s'];
161 if (after != null) {
162 args.add('--after=$after');
163 }
164 ProcessResult processResult =
165 await Process.run('git', args, workingDirectory: path);
166 _throwIfNotSuccess(processResult);
167 String output = processResult.stdout;
168 List<String> logLines = output.split('\n');
169 List<GitRevision> revisions = <GitRevision>[];
170 for (String logLine in logLines) {
171 int index1 = logLine.indexOf(' ');
172 if (index1 != -1) {
173 int index2 = logLine.indexOf(' ', index1 + 1);
174 if (index2 != -1) {
175 int timestamp = int.parse(logLine.substring(0, index1));
176 String hash = logLine.substring(index1 + 1, index2);
177 String message = logLine.substring(index2).trim();
178 revisions.add(new GitRevision(timestamp, hash, message));
179 }
180 }
181 }
182 return revisions;
183 }
184
185 void removeIndexLock() {
186 File file = new File('$path/.git/index.lock');
187 if (file.existsSync()) {
188 file.deleteSync();
189 }
190 }
191
192 Future resetHard() async {
193 ProcessResult processResult = await Process
194 .run('git', <String>['reset', '--hard'], workingDirectory: path);
195 _throwIfNotSuccess(processResult);
196 }
197
198 void _throwIfNotSuccess(ProcessResult processResult) {
199 if (processResult.exitCode != 0) {
200 throw new GitException.forProcessResult(
201 'Unable to run "git log".', processResult);
202 }
203 }
204 }
205
206 class GitRevision {
207 final int timestamp;
208 final String hash;
209 final String message;
210
211 GitRevision(this.timestamp, this.hash, this.message);
212
213 @override
214 String toString() {
215 DateTime dateTime =
216 new DateTime.fromMillisecondsSinceEpoch(timestamp * 1000, isUtc: true)
217 .toLocal();
218 return '$dateTime|$hash|$message|';
219 }
220 }
221
222 class StressTest {
223 String repoPath = '/Users/scheglov/tmp/limited-invalidation/path';
224 String folderPath = '/Users/scheglov/tmp/limited-invalidation/path';
225 // String repoPath = '/Users/scheglov/tmp/limited-invalidation/async';
226 // String folderPath = '/Users/scheglov/tmp/limited-invalidation/async';
227 // String repoPath = '/Users/scheglov/tmp/limited-invalidation/sdk';
228 // String folderPath = '/Users/scheglov/tmp/limited-invalidation/sdk/pkg/analyz er';
229
230 fs.ResourceProvider resourceProvider;
231 path.Context pathContext;
232 DartSdkManager sdkManager;
233 ContentCache contentCache;
234
235 AnalysisContextImpl expectedContext;
236 AnalysisContextImpl actualContext;
237
238 Set<Element> currentRevisionValidatedElements = new Set<Element>();
239
240 void createContexts() {
241 assert(expectedContext == null);
242 assert(actualContext == null);
243 resourceProvider = PhysicalResourceProvider.INSTANCE;
244 pathContext = resourceProvider.pathContext;
245 fs.Folder sdkDirectory =
246 FolderBasedDartSdk.defaultSdkDirectory(resourceProvider);
247 sdkManager = new DartSdkManager(sdkDirectory.path, false,
248 (_) => new FolderBasedDartSdk(resourceProvider, sdkDirectory));
249 contentCache = new ContentCache();
250 ContextBuilder builder =
251 new ContextBuilder(resourceProvider, sdkManager, contentCache);
252 builder.defaultOptions = new AnalysisOptionsImpl();
253 expectedContext = builder.buildContext(folderPath);
254 actualContext = builder.buildContext(folderPath);
255 expectedContext.analysisOptions =
256 new AnalysisOptionsImpl.from(expectedContext.analysisOptions)
257 ..incremental = true;
258 actualContext.analysisOptions =
259 new AnalysisOptionsImpl.from(actualContext.analysisOptions)
260 ..incremental = true
261 ..finerGrainedInvalidation = true;
262 print('Created contexts');
263 }
264
265 run() async {
266 GitRepository repository = new GitRepository(repoPath);
267
268 // Recover.
269 repository.removeIndexLock();
270 await repository.resetHard();
271
272 await repository.checkout('master');
273 List<GitRevision> revisions =
274 await repository.getRevisions(after: '2016-01-01');
275 revisions = revisions.reversed.toList();
276 // TODO(scheglov) Use to compare two revisions.
277 // List<GitRevision> revisions = [
278 // new GitRevision(0, '99517a162cbabf3d3afbdb566df3fe2b18cd4877', 'aaa'),
279 // new GitRevision(0, '2ef00b0c3d0182b5e4ea5ca55fd00b9d038ae40d', 'bbb'),
280 // ];
281 FolderInfo oldFolder = null;
282 for (GitRevision revision in revisions) {
283 print(revision);
284 await repository.checkout(revision.hash);
285
286 // Run "pub get".
287 if (!new File('$folderPath/pubspec.yaml').existsSync()) {
288 continue;
289 }
290 {
291 ProcessResult processResult = await Process.run(
292 '/Users/scheglov/Applications/dart-sdk/bin/pub', <String>['get'],
293 workingDirectory: folderPath);
294 if (processResult.exitCode != 0) {
295 _logPrint('Pub get failed.');
296 _logPrint(processResult.stdout);
297 _logPrint(processResult.stderr);
298 continue;
299 }
300 _logPrint('\tpub get OK');
301 }
302 FolderInfo newFolder = new FolderInfo(folderPath);
303
304 if (expectedContext == null) {
305 createContexts();
306 _applyChanges(
307 newFolder.files.map((file) => file.path).toList(), [], []);
308 _analyzeContexts();
309 }
310
311 if (oldFolder != null) {
312 FolderDiff diff = newFolder.diff(oldFolder);
313 print(' $diff');
314 if (diff.isNotEmpty) {
315 _applyChanges(diff.added, diff.changed, diff.removed);
316 _analyzeContexts();
317 }
318 }
319 oldFolder = newFolder;
320 print('\n');
321 print('\n');
322 }
323 }
324
325 /**
326 * Perform analysis tasks up to 512 times and assert that it was enough.
327 */
328 void _analyzeAll_assertFinished(AnalysisContext context,
329 [int maxIterations = 1000000]) {
330 for (int i = 0; i < maxIterations; i++) {
331 List<ChangeNotice> notice = context.performAnalysisTask().changeNotices;
332 if (notice == null) {
333 return;
334 }
335 }
336 throw new StateError(
337 "performAnalysisTask failed to terminate after analyzing all sources");
338 }
339
340 void _analyzeContexts() {
341 {
342 Stopwatch sw = new Stopwatch()..start();
343 _analyzeAll_assertFinished(expectedContext);
344 print(' analyze(expected): ${sw.elapsedMilliseconds}');
345 }
346 {
347 Stopwatch sw = new Stopwatch()..start();
348 _analyzeAll_assertFinished(actualContext);
349 print(' analyze(actual): ${sw.elapsedMilliseconds}');
350 }
351 _validateContexts();
352 }
353
354 void _applyChanges(
355 List<String> added, List<String> changed, List<String> removed) {
356 ChangeSet changeSet = new ChangeSet();
357 added.map(_pathToSource).forEach(changeSet.addedSource);
358 removed.map(_pathToSource).forEach(changeSet.removedSource);
359 changed.map(_pathToSource).forEach(changeSet.changedSource);
360 changed.forEach((path) => new File(path).readAsStringSync());
361 {
362 Stopwatch sw = new Stopwatch()..start();
363 expectedContext.applyChanges(changeSet);
364 print(' apply(expected): ${sw.elapsedMilliseconds}');
365 }
366 {
367 Stopwatch sw = new Stopwatch()..start();
368 actualContext.applyChanges(changeSet);
369 print(' apply(actual): ${sw.elapsedMilliseconds}');
370 }
371 }
372
373 Source _pathToSource(String path) {
374 fs.File file = resourceProvider.getFile(path);
375 return _createSourceInContext(expectedContext, file);
376 }
377
378 void _validateContexts() {
379 currentRevisionValidatedElements.clear();
380 MapIterator<AnalysisTarget, CacheEntry> iterator =
381 expectedContext.privateAnalysisCachePartition.iterator();
382 while (iterator.moveNext()) {
383 AnalysisTarget target = iterator.key;
384 CacheEntry entry = iterator.value;
385 if (target is NonExistingSource) {
386 continue;
387 }
388 _validateEntry(target, entry);
389 }
390 }
391
392 void _validateElements(
393 Element actualValue, Element expectedValue, Set visited) {
394 if (actualValue == null && expectedValue == null) {
395 return;
396 }
397 if (!currentRevisionValidatedElements.add(expectedValue)) {
398 return;
399 }
400 if (!visited.add(expectedValue)) {
401 return;
402 }
403 List<Element> sortElements(List<Element> elements) {
404 elements = elements.toList();
405 elements.sort((a, b) {
406 if (a.nameOffset != b.nameOffset) {
407 return a.nameOffset - b.nameOffset;
408 }
409 return a.name.compareTo(b.name);
410 });
411 return elements;
412 }
413
414 void validateSortedElements(
415 List<Element> actualElements, List<Element> expectedElements) {
416 expect(actualElements, hasLength(expectedElements.length));
417 actualElements = sortElements(actualElements);
418 expectedElements = sortElements(expectedElements);
419 for (int i = 0; i < expectedElements.length; i++) {
420 _validateElements(actualElements[i], expectedElements[i], visited);
421 }
422 }
423
424 expect(actualValue?.runtimeType, expectedValue?.runtimeType);
425 expect(actualValue.nameOffset, expectedValue.nameOffset);
426 expect(actualValue.name, expectedValue.name);
427 if (expectedValue is ClassElement) {
428 var actualElement = actualValue as ClassElement;
429 validateSortedElements(actualElement.accessors, expectedValue.accessors);
430 validateSortedElements(
431 actualElement.constructors, expectedValue.constructors);
432 validateSortedElements(actualElement.fields, expectedValue.fields);
433 validateSortedElements(actualElement.methods, expectedValue.methods);
434 }
435 if (expectedValue is CompilationUnitElement) {
436 var actualElement = actualValue as CompilationUnitElement;
437 validateSortedElements(actualElement.accessors, expectedValue.accessors);
438 validateSortedElements(actualElement.functions, expectedValue.functions);
439 validateSortedElements(actualElement.types, expectedValue.types);
440 validateSortedElements(
441 actualElement.functionTypeAliases, expectedValue.functionTypeAliases);
442 validateSortedElements(
443 actualElement.topLevelVariables, expectedValue.topLevelVariables);
444 }
445 if (expectedValue is ExecutableElement) {
446 var actualElement = actualValue as ExecutableElement;
447 validateSortedElements(
448 actualElement.parameters, expectedValue.parameters);
449 _validateTypes(
450 actualElement.returnType, expectedValue.returnType, visited);
451 }
452 }
453
454 void _validateEntry(AnalysisTarget target, CacheEntry expectedEntry) {
455 CacheEntry actualEntry =
456 actualContext.privateAnalysisCachePartition.get(target);
457 if (actualEntry == null) {
458 return;
459 }
460 print(' (${target.runtimeType}) $target');
461 for (ResultDescriptor result in expectedEntry.nonInvalidResults) {
462 var expectedData = expectedEntry.getResultDataOrNull(result);
463 var actualData = actualEntry.getResultDataOrNull(result);
464 if (expectedData?.state == CacheState.INVALID) {
465 expectedData = null;
466 }
467 if (actualData?.state == CacheState.INVALID) {
468 actualData = null;
469 }
470 if (actualData == null) {
471 if (result != CONTENT &&
472 result != LIBRARY_ELEMENT4 &&
473 result != LIBRARY_ELEMENT5 &&
474 result != READY_LIBRARY_ELEMENT6 &&
475 result != READY_LIBRARY_ELEMENT7) {
476 Source targetSource = target.source;
477 if (targetSource != null &&
478 targetSource.fullName.startsWith(folderPath)) {
479 fail('No ResultData $result for $target');
480 }
481 }
482 continue;
483 }
484 Object expectedValue = expectedData.value;
485 Object actualValue = actualData.value;
486 print(' $result ${expectedValue?.runtimeType}');
487 _validateResult(target, result, actualValue, expectedValue);
488 }
489 }
490
491 void _validatePairs(AnalysisTarget target, ResultDescriptor result,
492 List actualList, List expectedList) {
493 if (expectedList == null) {
494 expect(actualList, isNull);
495 return;
496 }
497 expect(actualList, isNotNull);
498 expect(actualList, hasLength(expectedList.length));
499 for (int i = 0; i < expectedList.length; i++) {
500 Object expected = expectedList[i];
501 Object actual = actualList[i];
502 _validateResult(target, result, actual, expected);
503 }
504 }
505
506 void _validateResult(AnalysisTarget target, ResultDescriptor result,
507 Object actualValue, Object expectedValue) {
508 if (expectedValue is bool) {
509 expect(actualValue, expectedValue, reason: '$result of $target');
510 }
511 if (expectedValue is CompilationUnit) {
512 expect(actualValue, new isInstanceOf<CompilationUnit>());
513 new _AstValidator().isEqualNodes(expectedValue, actualValue);
514 }
515 if (expectedValue is Element) {
516 expect(actualValue, new isInstanceOf<Element>());
517 _validateElements(actualValue, expectedValue, new Set.identity());
518 }
519 if (expectedValue is List) {
520 if (actualValue is List) {
521 _validatePairs(target, result, actualValue, expectedValue);
522 } else {
523 _failTypeMismatch(actualValue, expectedValue);
524 }
525 }
526 if (expectedValue is AnalysisError) {
527 if (actualValue is AnalysisError) {
528 expect(actualValue.source, expectedValue.source);
529 expect(actualValue.offset, expectedValue.offset);
530 expect(actualValue.message, expectedValue.message);
531 } else {
532 _failTypeMismatch(actualValue, expectedValue);
533 }
534 }
535 }
536
537 void _validateTypes(DartType actualType, DartType expectedType, Set visited) {
538 if (!visited.add(expectedType)) {
539 return;
540 }
541 expect(actualType?.runtimeType, expectedType?.runtimeType);
542 _validateElements(actualType.element, expectedType.element, visited);
543 }
544
545 /**
546 * Create and return a source representing the given [file] within the given
547 * [context].
548 */
549 static Source _createSourceInContext(AnalysisContext context, fs.File file) {
550 Source source = file.createSource();
551 if (context == null) {
552 return source;
553 }
554 Uri uri = context.sourceFactory.restoreUri(source);
555 return file.createSource(uri);
556 }
557 }
558
559 /**
560 * Compares tokens and ASTs, and built elements of declared identifiers.
561 */
562 class _AstValidator extends AstComparator {
563 @override
564 bool isEqualNodes(AstNode expected, AstNode actual) {
565 // TODO(scheglov) skip comments for now
566 // [ElementBuilder.visitFunctionExpression] in resolver_test.dart
567 // Going from c4493869ca19ef9ba6bd35d3d42e1209eb3b7e63
568 // to 3977c9f2274df35df6332a65af9973fd6517bc12
569 // With files:
570 // '/Users/scheglov/tmp/limited-invalidation/sdk/pkg/analyzer/lib/src/gener ated/resolver.dart',
571 // '/Users/scheglov/tmp/limited-invalidation/sdk/pkg/analyzer/lib/src/dart/ element/builder.dart',
572 // '/Users/scheglov/tmp/limited-invalidation/sdk/pkg/analyzer/test/generate d/resolver_test.dart',
573 if (expected is CommentReference) {
574 return true;
575 }
576 // Compare nodes.
577 bool result = super.isEqualNodes(expected, actual);
578 if (!result) {
579 fail('|$actual| != expected |$expected|');
580 }
581 // Verify that identifiers have equal elements and types.
582 if (expected is SimpleIdentifier && actual is SimpleIdentifier) {
583 _verifyElements(actual.staticElement, expected.staticElement,
584 '$expected staticElement');
585 _verifyElements(actual.propagatedElement, expected.propagatedElement,
586 '$expected staticElement');
587 _verifyTypes(
588 actual.staticType, expected.staticType, '$expected staticType');
589 _verifyTypes(actual.propagatedType, expected.propagatedType,
590 '$expected propagatedType');
591 _verifyElements(actual.staticParameterElement,
592 expected.staticParameterElement, '$expected staticParameterElement');
593 _verifyElements(
594 actual.propagatedParameterElement,
595 expected.propagatedParameterElement,
596 '$expected propagatedParameterElement');
597 }
598 return true;
599 }
600
601 void _verifyElements(Element actual, Element expected, String desc) {
602 if (expected == null && actual == null) {
603 return;
604 }
605 if (expected is MultiplyDefinedElement &&
606 actual is MultiplyDefinedElement) {
607 return;
608 }
609 while (expected is Member) {
610 if (actual is Member) {
611 actual = (actual as Member).baseElement;
612 expected = (expected as Member).baseElement;
613 } else {
614 _failTypeMismatch(actual, expected, reason: desc);
615 }
616 }
617 expect(actual, equals(expected), reason: desc);
618 }
619
620 void _verifyTypes(DartType actual, DartType expected, String desc) {
621 _verifyElements(actual?.element, expected?.element, '$desc element');
622 if (expected is InterfaceType) {
623 if (actual is InterfaceType) {
624 List<DartType> actualArguments = actual.typeArguments;
625 List<DartType> expectedArguments = expected.typeArguments;
626 expect(
627 actualArguments,
628 pairwiseCompare(expectedArguments, (a, b) {
629 _verifyTypes(a, b, '$desc typeArguments');
630 return true;
631 }, 'elements'));
632 } else {
633 _failTypeMismatch(actual, expected);
634 }
635 }
636 }
637 }
OLDNEW
« no previous file with comments | « packages/analyzer/test/src/util/yaml_test.dart ('k') | packages/analyzer/test/test_all.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698