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

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

Issue 2834773003: Remove the limited invalidation feature. (Closed)
Patch Set: Created 3 years, 8 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 | « pkg/analyzer/test/src/task/test_all.dart ('k') | no next file » | 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:test/test.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 contentCache = new ContentCache();
249 ContextBuilderOptions builderOptions = new ContextBuilderOptions();
250 builderOptions.defaultOptions = new AnalysisOptionsImpl();
251 ContextBuilder builder = new ContextBuilder(
252 resourceProvider, sdkManager, contentCache,
253 options: builderOptions);
254 expectedContext = builder.buildContext(folderPath);
255 actualContext = builder.buildContext(folderPath);
256 expectedContext.analysisOptions =
257 new AnalysisOptionsImpl.from(expectedContext.analysisOptions)
258 ..incremental = true;
259 actualContext.analysisOptions =
260 new AnalysisOptionsImpl.from(actualContext.analysisOptions)
261 ..incremental = true
262 ..finerGrainedInvalidation = true;
263 print('Created contexts');
264 }
265
266 run() async {
267 GitRepository repository = new GitRepository(repoPath);
268
269 // Recover.
270 repository.removeIndexLock();
271 await repository.resetHard();
272
273 await repository.checkout('master');
274 List<GitRevision> revisions =
275 await repository.getRevisions(after: '2016-01-01');
276 revisions = revisions.reversed.toList();
277 // TODO(scheglov) Use to compare two revisions.
278 // List<GitRevision> revisions = [
279 // new GitRevision(0, '99517a162cbabf3d3afbdb566df3fe2b18cd4877', 'aaa'),
280 // new GitRevision(0, '2ef00b0c3d0182b5e4ea5ca55fd00b9d038ae40d', 'bbb'),
281 // ];
282 FolderInfo oldFolder = null;
283 for (GitRevision revision in revisions) {
284 print(revision);
285 await repository.checkout(revision.hash);
286
287 // Run "pub get".
288 if (!new File('$folderPath/pubspec.yaml').existsSync()) {
289 continue;
290 }
291 {
292 ProcessResult processResult = await Process.run(
293 '/Users/scheglov/Applications/dart-sdk/bin/pub', <String>['get'],
294 workingDirectory: folderPath);
295 if (processResult.exitCode != 0) {
296 _logPrint('Pub get failed.');
297 _logPrint(processResult.stdout);
298 _logPrint(processResult.stderr);
299 continue;
300 }
301 _logPrint('\tpub get OK');
302 }
303 FolderInfo newFolder = new FolderInfo(folderPath);
304
305 if (expectedContext == null) {
306 createContexts();
307 _applyChanges(
308 newFolder.files.map((file) => file.path).toList(), [], []);
309 _analyzeContexts();
310 }
311
312 if (oldFolder != null) {
313 FolderDiff diff = newFolder.diff(oldFolder);
314 print(' $diff');
315 if (diff.isNotEmpty) {
316 _applyChanges(diff.added, diff.changed, diff.removed);
317 _analyzeContexts();
318 }
319 }
320 oldFolder = newFolder;
321 print('\n');
322 print('\n');
323 }
324 }
325
326 /**
327 * Perform analysis tasks up to 512 times and assert that it was enough.
328 */
329 void _analyzeAll_assertFinished(AnalysisContext context,
330 [int maxIterations = 1000000]) {
331 for (int i = 0; i < maxIterations; i++) {
332 List<ChangeNotice> notice = context.performAnalysisTask().changeNotices;
333 if (notice == null) {
334 return;
335 }
336 }
337 throw new StateError(
338 "performAnalysisTask failed to terminate after analyzing all sources");
339 }
340
341 void _analyzeContexts() {
342 {
343 Stopwatch sw = new Stopwatch()..start();
344 _analyzeAll_assertFinished(expectedContext);
345 print(' analyze(expected): ${sw.elapsedMilliseconds}');
346 }
347 {
348 Stopwatch sw = new Stopwatch()..start();
349 _analyzeAll_assertFinished(actualContext);
350 print(' analyze(actual): ${sw.elapsedMilliseconds}');
351 }
352 _validateContexts();
353 }
354
355 void _applyChanges(
356 List<String> added, List<String> changed, List<String> removed) {
357 ChangeSet changeSet = new ChangeSet();
358 added.map(_pathToSource).forEach(changeSet.addedSource);
359 removed.map(_pathToSource).forEach(changeSet.removedSource);
360 changed.map(_pathToSource).forEach(changeSet.changedSource);
361 changed.forEach((path) => new File(path).readAsStringSync());
362 {
363 Stopwatch sw = new Stopwatch()..start();
364 expectedContext.applyChanges(changeSet);
365 print(' apply(expected): ${sw.elapsedMilliseconds}');
366 }
367 {
368 Stopwatch sw = new Stopwatch()..start();
369 actualContext.applyChanges(changeSet);
370 print(' apply(actual): ${sw.elapsedMilliseconds}');
371 }
372 }
373
374 Source _pathToSource(String path) {
375 fs.File file = resourceProvider.getFile(path);
376 return _createSourceInContext(expectedContext, file);
377 }
378
379 void _validateContexts() {
380 currentRevisionValidatedElements.clear();
381 MapIterator<AnalysisTarget, CacheEntry> iterator =
382 expectedContext.privateAnalysisCachePartition.iterator();
383 while (iterator.moveNext()) {
384 AnalysisTarget target = iterator.key;
385 CacheEntry entry = iterator.value;
386 if (target is NonExistingSource) {
387 continue;
388 }
389 _validateEntry(target, entry);
390 }
391 }
392
393 void _validateElements(
394 Element actualValue, Element expectedValue, Set visited) {
395 if (actualValue == null && expectedValue == null) {
396 return;
397 }
398 if (!currentRevisionValidatedElements.add(expectedValue)) {
399 return;
400 }
401 if (!visited.add(expectedValue)) {
402 return;
403 }
404 List<Element> sortElements(List<Element> elements) {
405 elements = elements.toList();
406 elements.sort((a, b) {
407 if (a.nameOffset != b.nameOffset) {
408 return a.nameOffset - b.nameOffset;
409 }
410 return a.name.compareTo(b.name);
411 });
412 return elements;
413 }
414
415 void validateSortedElements(
416 List<Element> actualElements, List<Element> expectedElements) {
417 expect(actualElements, hasLength(expectedElements.length));
418 actualElements = sortElements(actualElements);
419 expectedElements = sortElements(expectedElements);
420 for (int i = 0; i < expectedElements.length; i++) {
421 _validateElements(actualElements[i], expectedElements[i], visited);
422 }
423 }
424
425 expect(actualValue?.runtimeType, expectedValue?.runtimeType);
426 expect(actualValue.nameOffset, expectedValue.nameOffset);
427 expect(actualValue.name, expectedValue.name);
428 if (expectedValue is ClassElement) {
429 var actualElement = actualValue as ClassElement;
430 validateSortedElements(actualElement.accessors, expectedValue.accessors);
431 validateSortedElements(
432 actualElement.constructors, expectedValue.constructors);
433 validateSortedElements(actualElement.fields, expectedValue.fields);
434 validateSortedElements(actualElement.methods, expectedValue.methods);
435 }
436 if (expectedValue is CompilationUnitElement) {
437 var actualElement = actualValue as CompilationUnitElement;
438 validateSortedElements(actualElement.accessors, expectedValue.accessors);
439 validateSortedElements(actualElement.functions, expectedValue.functions);
440 validateSortedElements(actualElement.types, expectedValue.types);
441 validateSortedElements(
442 actualElement.functionTypeAliases, expectedValue.functionTypeAliases);
443 validateSortedElements(
444 actualElement.topLevelVariables, expectedValue.topLevelVariables);
445 }
446 if (expectedValue is ExecutableElement) {
447 var actualElement = actualValue as ExecutableElement;
448 validateSortedElements(
449 actualElement.parameters, expectedValue.parameters);
450 _validateTypes(
451 actualElement.returnType, expectedValue.returnType, visited);
452 }
453 }
454
455 void _validateEntry(AnalysisTarget target, CacheEntry expectedEntry) {
456 CacheEntry actualEntry =
457 actualContext.privateAnalysisCachePartition.get(target);
458 if (actualEntry == null) {
459 return;
460 }
461 print(' (${target.runtimeType}) $target');
462 for (ResultDescriptor result in expectedEntry.nonInvalidResults) {
463 var expectedData = expectedEntry.getResultDataOrNull(result);
464 var actualData = actualEntry.getResultDataOrNull(result);
465 if (expectedData?.state == CacheState.INVALID) {
466 expectedData = null;
467 }
468 if (actualData?.state == CacheState.INVALID) {
469 actualData = null;
470 }
471 if (actualData == null) {
472 if (result != CONTENT &&
473 result != LIBRARY_ELEMENT4 &&
474 result != LIBRARY_ELEMENT5 &&
475 result != READY_LIBRARY_ELEMENT6 &&
476 result != READY_LIBRARY_ELEMENT7) {
477 Source targetSource = target.source;
478 if (targetSource != null &&
479 targetSource.fullName.startsWith(folderPath)) {
480 fail('No ResultData $result for $target');
481 }
482 }
483 continue;
484 }
485 Object expectedValue = expectedData.value;
486 Object actualValue = actualData.value;
487 print(' $result ${expectedValue?.runtimeType}');
488 _validateResult(target, result, actualValue, expectedValue);
489 }
490 }
491
492 void _validatePairs(AnalysisTarget target, ResultDescriptor result,
493 List actualList, List expectedList) {
494 if (expectedList == null) {
495 expect(actualList, isNull);
496 return;
497 }
498 expect(actualList, isNotNull);
499 expect(actualList, hasLength(expectedList.length));
500 for (int i = 0; i < expectedList.length; i++) {
501 Object expected = expectedList[i];
502 Object actual = actualList[i];
503 _validateResult(target, result, actual, expected);
504 }
505 }
506
507 void _validateResult(AnalysisTarget target, ResultDescriptor result,
508 Object actualValue, Object expectedValue) {
509 if (expectedValue is bool) {
510 expect(actualValue, expectedValue, reason: '$result of $target');
511 }
512 if (expectedValue is CompilationUnit) {
513 expect(actualValue, new isInstanceOf<CompilationUnit>());
514 new _AstValidator().isEqualNodes(expectedValue, actualValue);
515 }
516 if (expectedValue is Element) {
517 expect(actualValue, new isInstanceOf<Element>());
518 _validateElements(actualValue, expectedValue, new Set.identity());
519 }
520 if (expectedValue is List) {
521 if (actualValue is List) {
522 _validatePairs(target, result, actualValue, expectedValue);
523 } else {
524 _failTypeMismatch(actualValue, expectedValue);
525 }
526 }
527 if (expectedValue is AnalysisError) {
528 if (actualValue is AnalysisError) {
529 expect(actualValue.source, expectedValue.source);
530 expect(actualValue.offset, expectedValue.offset);
531 expect(actualValue.message, expectedValue.message);
532 } else {
533 _failTypeMismatch(actualValue, expectedValue);
534 }
535 }
536 }
537
538 void _validateTypes(DartType actualType, DartType expectedType, Set visited) {
539 if (!visited.add(expectedType)) {
540 return;
541 }
542 expect(actualType?.runtimeType, expectedType?.runtimeType);
543 _validateElements(actualType.element, expectedType.element, visited);
544 }
545
546 /**
547 * Create and return a source representing the given [file] within the given
548 * [context].
549 */
550 static Source _createSourceInContext(AnalysisContext context, fs.File file) {
551 Source source = file.createSource();
552 if (context == null) {
553 return source;
554 }
555 Uri uri = context.sourceFactory.restoreUri(source);
556 return file.createSource(uri);
557 }
558 }
559
560 /**
561 * Compares tokens and ASTs, and built elements of declared identifiers.
562 */
563 class _AstValidator extends AstComparator {
564 @override
565 bool isEqualNodes(AstNode expected, AstNode actual) {
566 // TODO(scheglov) skip comments for now
567 // [ElementBuilder.visitFunctionExpression] in resolver_test.dart
568 // Going from c4493869ca19ef9ba6bd35d3d42e1209eb3b7e63
569 // to 3977c9f2274df35df6332a65af9973fd6517bc12
570 // With files:
571 // '/Users/scheglov/tmp/limited-invalidation/sdk/pkg/analyzer/lib/src/gener ated/resolver.dart',
572 // '/Users/scheglov/tmp/limited-invalidation/sdk/pkg/analyzer/lib/src/dart/ element/builder.dart',
573 // '/Users/scheglov/tmp/limited-invalidation/sdk/pkg/analyzer/test/generate d/resolver_test.dart',
574 if (expected is CommentReference) {
575 return true;
576 }
577 // Compare nodes.
578 bool result = super.isEqualNodes(expected, actual);
579 if (!result) {
580 fail('|$actual| != expected |$expected|');
581 }
582 // Verify that identifiers have equal elements and types.
583 if (expected is SimpleIdentifier && actual is SimpleIdentifier) {
584 _verifyElements(actual.staticElement, expected.staticElement,
585 '$expected staticElement');
586 _verifyElements(actual.propagatedElement, expected.propagatedElement,
587 '$expected staticElement');
588 _verifyTypes(
589 actual.staticType, expected.staticType, '$expected staticType');
590 _verifyTypes(actual.propagatedType, expected.propagatedType,
591 '$expected propagatedType');
592 _verifyElements(actual.staticParameterElement,
593 expected.staticParameterElement, '$expected staticParameterElement');
594 _verifyElements(
595 actual.propagatedParameterElement,
596 expected.propagatedParameterElement,
597 '$expected propagatedParameterElement');
598 }
599 return true;
600 }
601
602 void _verifyElements(Element actual, Element expected, String desc) {
603 if (expected == null && actual == null) {
604 return;
605 }
606 if (expected is MultiplyDefinedElement &&
607 actual is MultiplyDefinedElement) {
608 return;
609 }
610 while (expected is Member) {
611 if (actual is Member) {
612 actual = (actual as Member).baseElement;
613 expected = (expected as Member).baseElement;
614 } else {
615 _failTypeMismatch(actual, expected, reason: desc);
616 }
617 }
618 expect(actual, equals(expected), reason: desc);
619 }
620
621 void _verifyTypes(DartType actual, DartType expected, String desc) {
622 _verifyElements(actual?.element, expected?.element, '$desc element');
623 if (expected is InterfaceType) {
624 if (actual is InterfaceType) {
625 List<DartType> actualArguments = actual.typeArguments;
626 List<DartType> expectedArguments = expected.typeArguments;
627 expect(
628 actualArguments,
629 pairwiseCompare(expectedArguments, (a, b) {
630 _verifyTypes(a, b, '$desc typeArguments');
631 return true;
632 }, 'elements'));
633 } else {
634 _failTypeMismatch(actual, expected);
635 }
636 }
637 }
638 }
OLDNEW
« no previous file with comments | « pkg/analyzer/test/src/task/test_all.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698