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

Side by Side Diff: pkg/analysis_server/test/stress/replay/replay.dart

Issue 1514693013: Updates to the stress test (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Created 5 years 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
OLDNEW
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 /** 5 /**
6 * A stress test for the analysis server. 6 * A stress test for the analysis server.
7 */ 7 */
8 library analysis_server.test.stress.replay.replay; 8 library analysis_server.test.stress.replay.replay;
9 9
10 import 'dart:async'; 10 import 'dart:async';
11 import 'dart:io'; 11 import 'dart:io';
12 12
13 import 'package:analysis_server/plugin/protocol/protocol.dart'; 13 import 'package:analysis_server/plugin/protocol/protocol.dart';
14 import 'package:analyzer/src/generated/error.dart' as error;
14 import 'package:analyzer/src/generated/java_engine.dart'; 15 import 'package:analyzer/src/generated/java_engine.dart';
16 import 'package:analyzer/src/generated/scanner.dart';
15 import 'package:analyzer/src/generated/source.dart'; 17 import 'package:analyzer/src/generated/source.dart';
16 import 'package:analyzer/src/util/glob.dart'; 18 import 'package:analyzer/src/util/glob.dart';
17 import 'package:args/args.dart'; 19 import 'package:args/args.dart';
18 import 'package:path/path.dart' as path; 20 import 'package:path/path.dart' as path;
19 21
20 import '../utilities/git.dart'; 22 import '../utilities/git.dart';
21 import '../utilities/server.dart'; 23 import '../utilities/server.dart';
22 import 'operation.dart'; 24 import 'operation.dart';
23 25
24 /** 26 /**
25 * Run the simulation based on the given command-line [arguments]. 27 * Run the simulation based on the given command-line [arguments].
26 */ 28 */
27 Future main(List<String> arguments) async { 29 Future main(List<String> arguments) async {
28 Driver driver = new Driver(); 30 Driver driver = new Driver();
29 await driver.run(arguments); 31 await driver.run(arguments);
30 } 32 }
31 33
32 /** 34 /**
33 * The driver class that runs the simulation. 35 * The driver class that runs the simulation.
34 */ 36 */
35 class Driver { 37 class Driver {
36 /** 38 /**
skybrian 2015/12/12 02:01:20 Any plans to switch to '///' in the analyzer?
Brian Wilkerson 2015/12/12 15:28:23 Nope. At least two of us really dislike that form
39 * The value of the [OVERLAY_STYLE_OPTION_NAME] indicating that modifications
40 * to a file should be represented by an add overlay, followed by zero or more
41 * change overlays, followed by a remove overlay.
42 */
43 static String CHANGE_OVERLAY_STYLE = 'change';
44
45 /**
37 * The name of the command-line flag that will print help text. 46 * The name of the command-line flag that will print help text.
38 */ 47 */
39 static String HELP_FLAG_NAME = 'help'; 48 static String HELP_FLAG_NAME = 'help';
40 49
41 /** 50 /**
51 * The value of the [OVERLAY_STYLE_OPTION_NAME] indicating that modifications
52 * to a file should be represented by an add overlay, followed by zero or more
53 * additional add overlays, followed by a remove overlay.
54 */
55 static String MULTIPLE_ADD_OVERLAY_STYLE = 'multipleAdd';
56
57 /**
58 * The name of the command-line option used to specify the style of
59 * interaction to use when making `analysis.updateContent` requests.
60 */
61 static String OVERLAY_STYLE_OPTION_NAME = 'overlay-style';
62
63 /**
42 * The name of the pubspec file. 64 * The name of the pubspec file.
43 */ 65 */
44 static const String PUBSPEC_FILE_NAME = 'pubspec.yaml'; 66 static const String PUBSPEC_FILE_NAME = 'pubspec.yaml';
45 67
46 /** 68 /**
47 * The name of the branch used to clean-up after making temporary changes. 69 * The name of the branch used to clean-up after making temporary changes.
48 */ 70 */
49 static const String TEMP_BRANCH_NAME = 'temp'; 71 static const String TEMP_BRANCH_NAME = 'temp';
50 72
51 /** 73 /**
74 * The style of interaction to use for analysis.updateContent requests.
75 */
76 OverlayStyle overlayStyle;
77
78 /**
52 * The absolute path of the repository. 79 * The absolute path of the repository.
53 */ 80 */
54 String repositoryPath; 81 String repositoryPath;
55 82
56 /** 83 /**
57 * The absolute paths to the analysis roots. 84 * The absolute paths to the analysis roots.
58 */ 85 */
59 List<String> analysisRoots; 86 List<String> analysisRoots;
60 87
61 /** 88 /**
(...skipping 18 matching lines...) Expand all
80 Statistics statistics; 107 Statistics statistics;
81 108
82 /** 109 /**
83 * Initialize a newly created driver. 110 * Initialize a newly created driver.
84 */ 111 */
85 Driver() { 112 Driver() {
86 statistics = new Statistics(this); 113 statistics = new Statistics(this);
87 } 114 }
88 115
89 /** 116 /**
90 * Run the test based on the given command-line arguments ([args]). 117 * Run the simulation based on the given command-line arguments ([args]).
91 */ 118 */
92 Future run(List<String> args) async { 119 Future run(List<String> args) async {
93 // 120 //
94 // Process the command-line arguments. 121 // Process the command-line arguments.
95 // 122 //
96 ArgParser parser = _createArgParser(); 123 if (!_processCommandLine(args)) {
97 ArgResults results;
98 try {
99 results = parser.parse(args);
100 } catch (exception) {
101 _showUsage(parser);
102 return null; 124 return null;
103 } 125 }
104
105 if (results[HELP_FLAG_NAME]) {
106 _showUsage(parser);
107 return null;
108 }
109
110 List<String> arguments = results.arguments;
111 if (arguments.length < 2) {
112 _showUsage(parser);
113 return null;
114 }
115 repositoryPath = path.normalize(arguments[0]);
116 repository = new GitRepository(repositoryPath);
117
118 analysisRoots = arguments
119 .sublist(1)
120 .map((String analysisRoot) => path.normalize(analysisRoot))
121 .toList();
122 for (String analysisRoot in analysisRoots) {
123 if (repositoryPath != analysisRoot &&
124 !path.isWithin(repositoryPath, analysisRoot)) {
125 _showUsage(parser,
126 'Analysis roots must be contained within the repository: $analysisRo ot');
127 return null;
128 }
129 }
130 // 126 //
131 // Replay the commit history. 127 // Simulate interactions with the server.
132 // 128 //
133 Stopwatch stopwatch = new Stopwatch(); 129 await _runSimulation();
134 statistics.stopwatch = stopwatch;
135 stopwatch.start();
136 await server.start();
137 server.sendServerSetSubscriptions([ServerService.STATUS]);
138 server.sendAnalysisSetGeneralSubscriptions(
139 [GeneralAnalysisService.ANALYZED_FILES]);
140 // TODO(brianwilkerson) Get the list of glob patterns from the server after
141 // an API for getting them has been implemented.
142 fileGlobs = <Glob>[
143 new Glob(path.context.separator, '**.dart'),
144 new Glob(path.context.separator, '**.html'),
145 new Glob(path.context.separator, '**.htm'),
146 new Glob(path.context.separator, '**/.analysisOptions')
147 ];
148 try {
149 _replayChanges();
150 } finally {
151 server.sendServerShutdown();
152 repository.checkout('master');
153 }
154 stopwatch.stop();
155 // 130 //
156 // Print out statistics gathered while performing the simulation. 131 // Print out statistics gathered while performing the simulation.
157 // 132 //
158 statistics.print(); 133 statistics.print();
134 exit(0);
159 return null; 135 return null;
160 } 136 }
161 137
162 /** 138 /**
163 * Create and return a parser that can be used to parse the command-line 139 * Create and return a parser that can be used to parse the command-line
164 * arguments. 140 * arguments.
165 */ 141 */
166 ArgParser _createArgParser() { 142 ArgParser _createArgParser() {
167 ArgParser parser = new ArgParser(); 143 ArgParser parser = new ArgParser();
168 parser.addFlag(HELP_FLAG_NAME, 144 parser.addFlag(HELP_FLAG_NAME,
169 abbr: 'h', 145 abbr: 'h',
170 help: 'Print usage information', 146 help: 'Print usage information',
171 defaultsTo: false, 147 defaultsTo: false,
172 negatable: false); 148 negatable: false);
149
150 parser.addOption(OVERLAY_STYLE_OPTION_NAME,
151 help:
152 'The style of interaction to use for analysis.updateContent requests ',
153 allowed: [CHANGE_OVERLAY_STYLE, MULTIPLE_ADD_OVERLAY_STYLE],
154 allowedHelp: {
155 CHANGE_OVERLAY_STYLE: '<add> <change>* <remove>',
156 MULTIPLE_ADD_OVERLAY_STYLE: '<add>+ <remove>'
157 },
158 defaultsTo: 'change');
173 return parser; 159 return parser;
174 } 160 }
175 161
162 /**
163 * Add source edits to the given [fileEdit] based on the given [blobDiff].
164 */
176 void _createSourceEdits(FileEdit fileEdit, BlobDiff blobDiff) { 165 void _createSourceEdits(FileEdit fileEdit, BlobDiff blobDiff) {
177 LineInfo info = fileEdit.lineInfo; 166 LineInfo info = fileEdit.lineInfo;
178 for (DiffHunk hunk in blobDiff.hunks) { 167 for (DiffHunk hunk in blobDiff.hunks) {
179 List<SourceEdit> sourceEdits = <SourceEdit>[];
180 int srcStart = info.getOffsetOfLine(hunk.srcLine); 168 int srcStart = info.getOffsetOfLine(hunk.srcLine);
181 int srcEnd = info.getOffsetOfLine(hunk.srcLine + hunk.removeLines.length); 169 int srcEnd = info.getOffsetOfLine(hunk.srcLine + hunk.removeLines.length);
182 // TODO(brianwilkerson) Create multiple edits instead of a single edit. 170 String addedText = _join(hunk.addLines);
183 sourceEdits.add(new SourceEdit( 171 //
184 srcStart, srcEnd - srcStart + 1, _join(hunk.addLines))); 172 // Create the source edits.
173 //
174 List<int> breakOffsets = _getBreakOffsets(addedText);
175 int breakCount = breakOffsets.length;
176 List<SourceEdit> sourceEdits = <SourceEdit>[];
177 if (breakCount == 0) {
178 sourceEdits
179 .add(new SourceEdit(srcStart, srcEnd - srcStart + 1, addedText));
180 } else {
181 int previousOffset = breakOffsets[0];
182 String string = addedText.substring(0, previousOffset);
183 sourceEdits
184 .add(new SourceEdit(srcStart, srcEnd - srcStart + 1, string));
185 String reconstruction = string;
186 for (int i = 1; i < breakCount; i++) {
187 int offset = breakOffsets[i];
188 string = addedText.substring(previousOffset, offset);
189 reconstruction += string;
190 sourceEdits.add(new SourceEdit(srcStart + previousOffset, 0, string));
191 previousOffset = offset;
192 }
193 string = addedText.substring(previousOffset);
194 reconstruction += string;
195 sourceEdits.add(new SourceEdit(srcStart + previousOffset, 0, string));
196 if (reconstruction != addedText) {
197 throw new AssertionError();
198 }
199 }
185 fileEdit.addSourceEdits(sourceEdits); 200 fileEdit.addSourceEdits(sourceEdits);
186 } 201 }
187 } 202 }
188 203
189 /** 204 /**
190 * Return athe absolute paths of all of the pubspec files in all of the 205 * Return the absolute paths of all of the pubspec files in all of the
191 * analysis roots. 206 * analysis roots.
192 */ 207 */
193 Iterable<String> _findPubspecsInAnalysisRoots() { 208 Iterable<String> _findPubspecsInAnalysisRoots() {
194 List<String> pubspecFiles = <String>[]; 209 List<String> pubspecFiles = <String>[];
195 for (String directoryPath in analysisRoots) { 210 for (String directoryPath in analysisRoots) {
196 Directory directory = new Directory(directoryPath); 211 Directory directory = new Directory(directoryPath);
197 List<FileSystemEntity> children = 212 List<FileSystemEntity> children =
198 directory.listSync(recursive: true, followLinks: false); 213 directory.listSync(recursive: true, followLinks: false);
199 for (FileSystemEntity child in children) { 214 for (FileSystemEntity child in children) {
200 String filePath = child.path; 215 String filePath = child.path;
201 if (path.basename(filePath) == PUBSPEC_FILE_NAME) { 216 if (path.basename(filePath) == PUBSPEC_FILE_NAME) {
202 pubspecFiles.add(filePath); 217 pubspecFiles.add(filePath);
203 } 218 }
204 } 219 }
205 } 220 }
206 return pubspecFiles; 221 return pubspecFiles;
207 } 222 }
208 223
224 /**
225 * Return a list of offsets into the given [text] that represent good places
226 * to break the text when building edits.
227 */
228 List<int> _getBreakOffsets(String text) {
skybrian 2015/12/12 02:01:20 It seems like we could simulate typing one charact
Brian Wilkerson 2015/12/12 15:28:23 We're trying to simulate how the client (editor) w
229 List<int> breakOffsets = <int>[];
230 Scanner scanner = new Scanner(null, new CharSequenceReader(text),
231 error.AnalysisErrorListener.NULL_LISTENER);
232 Token token = scanner.tokenize();
233 // TODO(brianwilkerson) Randomize. Sometimes add zero (0) as a break point.
234 while (token.type != TokenType.EOF) {
235 // TODO(brianwilkerson) Break inside comments?
236 // Token comment = token.precedingComments;
237 int offset = token.offset;
238 int length = token.length;
239 breakOffsets.add(offset);
240 if (token.type == TokenType.IDENTIFIER && length > 3) {
241 breakOffsets.add(offset + (length ~/ 2));
242 }
243 token = token.next;
244 }
245 return breakOffsets;
246 }
247
248 /**
249 * Join the given [lines] into a single string.
250 */
209 String _join(List<String> lines) { 251 String _join(List<String> lines) {
210 StringBuffer buffer = new StringBuffer(); 252 StringBuffer buffer = new StringBuffer();
211 for (int i = 0; i < lines.length; i++) { 253 for (int i = 0; i < lines.length; i++) {
212 buffer.writeln(lines[i]); 254 buffer.writeln(lines[i]);
213 } 255 }
214 return buffer.toString(); 256 return buffer.toString();
215 } 257 }
216 258
217 /** 259 /**
260 * Process the command-line [arguments]. Return `true` if the simulation
261 * should be run.
262 */
263 bool _processCommandLine(List<String> args) {
264 ArgParser parser = _createArgParser();
265 ArgResults results;
266 try {
267 results = parser.parse(args);
268 } catch (exception) {
269 _showUsage(parser);
270 return false;
271 }
272
273 if (results[HELP_FLAG_NAME]) {
274 _showUsage(parser);
275 return false;
276 }
277
278 String overlayStyleValue = results[OVERLAY_STYLE_OPTION_NAME];
279 if (overlayStyleValue == CHANGE_OVERLAY_STYLE) {
280 overlayStyle = OverlayStyle.change;
281 } else if (overlayStyleValue == MULTIPLE_ADD_OVERLAY_STYLE) {
282 overlayStyle = OverlayStyle.multipleAdd;
283 }
284
285 List<String> arguments = results.arguments;
286 if (arguments.length < 2) {
287 _showUsage(parser);
288 return false;
289 }
290 repositoryPath = path.normalize(arguments[0]);
291 repository = new GitRepository(repositoryPath);
292
293 analysisRoots = arguments
294 .sublist(1)
295 .map((String analysisRoot) => path.normalize(analysisRoot))
296 .toList();
297 for (String analysisRoot in analysisRoots) {
298 if (repositoryPath != analysisRoot &&
299 !path.isWithin(repositoryPath, analysisRoot)) {
300 _showUsage(parser,
301 'Analysis roots must be contained within the repository: $analysisRo ot');
302 return false;
303 }
304 }
305 return true;
306 }
307
308 /**
218 * Replay the changes in each commit. 309 * Replay the changes in each commit.
219 */ 310 */
220 void _replayChanges() { 311 Future _replayChanges() async {
221 // 312 //
222 // Get the revision history of the repo. 313 // Get the revision history of the repo.
223 // 314 //
224 LinearCommitHistory history = repository.getCommitHistory(); 315 LinearCommitHistory history = repository.getCommitHistory();
225 statistics.commitCount = history.commitIds.length; 316 statistics.commitCount = history.commitIds.length;
226 LinearCommitHistoryIterator iterator = history.iterator(); 317 LinearCommitHistoryIterator iterator = history.iterator();
227 // 318 //
228 // Iterate over the history, applying changes. 319 // Iterate over the history, applying changes.
229 // 320 //
230 bool firstCheckout = true; 321 bool firstCheckout = true;
231 // Map<String, List<AnalysisError>> expectedErrors = null; 322 ErrorMap expectedErrors = null;
232 Iterable<String> changedPubspecs; 323 Iterable<String> changedPubspecs;
233 while (iterator.moveNext()) { 324 while (iterator.moveNext()) {
234 // 325 //
235 // Checkout the commit on which the changes are based. 326 // Checkout the commit on which the changes are based.
236 // 327 //
237 repository.checkout(iterator.srcCommit); 328 String commit = iterator.srcCommit;
238 // if (expectedErrors != null) { 329 repository.checkout(commit);
239 // await server.analysisFinished; 330 if (expectedErrors != null) {
240 // server.expectErrorState(expectedErrors); 331 ErrorMap actualErrors =
241 // } 332 await server.computeErrorMap(server.analyzedDartFiles);
333 String difference = expectedErrors.expectErrorMap(actualErrors);
334 if (difference != null) {
335 stdout.write('Mismatched errors after commit ');
336 stdout.writeln(commit);
337 stdout.writeln();
338 stdout.writeln(difference);
339 return;
340 }
341 }
242 if (firstCheckout) { 342 if (firstCheckout) {
243 changedPubspecs = _findPubspecsInAnalysisRoots(); 343 changedPubspecs = _findPubspecsInAnalysisRoots();
244 server.sendAnalysisSetAnalysisRoots(analysisRoots, []); 344 server.sendAnalysisSetAnalysisRoots(analysisRoots, []);
245 firstCheckout = false; 345 firstCheckout = false;
246 } else { 346 } else {
247 server.removeAllOverlays(); 347 server.removeAllOverlays();
248 } 348 }
249 // await server.analysisFinished; 349 expectedErrors = await server.computeErrorMap(server.analyzedDartFiles);
250 // expectedErrors = server.errorMap;
251 for (String filePath in changedPubspecs) { 350 for (String filePath in changedPubspecs) {
252 _runPub(filePath); 351 _runPub(filePath);
253 } 352 }
254 // 353 //
255 // Apply the changes. 354 // Apply the changes.
256 // 355 //
257 CommitDelta commitDelta = iterator.next(); 356 CommitDelta commitDelta = iterator.next();
258 commitDelta.filterDiffs(analysisRoots, fileGlobs); 357 commitDelta.filterDiffs(analysisRoots, fileGlobs);
259 if (commitDelta.hasDiffs) { 358 if (commitDelta.hasDiffs) {
260 statistics.commitsWithChangeInRootCount++; 359 statistics.commitsWithChangeInRootCount++;
261 _replayDiff(commitDelta); 360 _replayDiff(commitDelta);
262 } 361 }
263 changedPubspecs = commitDelta.filesMatching(PUBSPEC_FILE_NAME); 362 changedPubspecs = commitDelta.filesMatching(PUBSPEC_FILE_NAME);
363 stdout.write('.');
264 } 364 }
265 server.removeAllOverlays(); 365 server.removeAllOverlays();
366 stdout.writeln();
266 } 367 }
267 368
369 /**
370 * Replay the changes between two commits, as represented by the given
371 * [commitDelta].
372 */
268 void _replayDiff(CommitDelta commitDelta) { 373 void _replayDiff(CommitDelta commitDelta) {
269 List<FileEdit> editList = <FileEdit>[]; 374 List<FileEdit> editList = <FileEdit>[];
270 for (DiffRecord record in commitDelta.diffRecords) { 375 for (DiffRecord record in commitDelta.diffRecords) {
271 FileEdit edit = new FileEdit(record); 376 FileEdit edit = new FileEdit(overlayStyle, record);
272 _createSourceEdits(edit, record.getBlobDiff()); 377 _createSourceEdits(edit, record.getBlobDiff());
273 editList.add(edit); 378 editList.add(edit);
274 } 379 }
380 //
275 // TODO(brianwilkerson) Randomize. 381 // TODO(brianwilkerson) Randomize.
276 // Randomly select operations from different files to simulate a user 382 // Randomly select operations from different files to simulate a user
277 // editing multiple files simultaneously. 383 // editing multiple files simultaneously.
384 //
278 for (FileEdit edit in editList) { 385 for (FileEdit edit in editList) {
279 List<String> currentFile = <String>[edit.filePath]; 386 List<String> currentFile = <String>[edit.filePath];
280 server.sendAnalysisSetPriorityFiles(currentFile); 387 server.sendAnalysisSetPriorityFiles(currentFile);
281 server.sendAnalysisSetSubscriptions({ 388 server.sendAnalysisSetSubscriptions({
282 AnalysisService.FOLDING: currentFile, 389 AnalysisService.FOLDING: currentFile,
283 AnalysisService.HIGHLIGHTS: currentFile, 390 AnalysisService.HIGHLIGHTS: currentFile,
284 AnalysisService.IMPLEMENTED: currentFile, 391 AnalysisService.IMPLEMENTED: currentFile,
285 AnalysisService.NAVIGATION: currentFile, 392 AnalysisService.NAVIGATION: currentFile,
286 AnalysisService.OCCURRENCES: currentFile, 393 AnalysisService.OCCURRENCES: currentFile,
287 AnalysisService.OUTLINE: currentFile, 394 AnalysisService.OUTLINE: currentFile,
(...skipping 11 matching lines...) Expand all
299 void _runPub(String filePath) { 406 void _runPub(String filePath) {
300 String directoryPath = path.dirname(filePath); 407 String directoryPath = path.dirname(filePath);
301 if (new Directory(directoryPath).existsSync()) { 408 if (new Directory(directoryPath).existsSync()) {
302 Process.runSync( 409 Process.runSync(
303 '/Users/brianwilkerson/Dev/dart/dart-sdk/bin/pub', ['get'], 410 '/Users/brianwilkerson/Dev/dart/dart-sdk/bin/pub', ['get'],
304 workingDirectory: directoryPath); 411 workingDirectory: directoryPath);
305 } 412 }
306 } 413 }
307 414
308 /** 415 /**
416 * Run the simulation by starting up a server and sending it requests.
417 */
418 Future _runSimulation() async {
419 Stopwatch stopwatch = new Stopwatch();
420 statistics.stopwatch = stopwatch;
421 stopwatch.start();
422 await server.start();
423 server.sendServerSetSubscriptions([ServerService.STATUS]);
424 server.sendAnalysisSetGeneralSubscriptions(
425 [GeneralAnalysisService.ANALYZED_FILES]);
426 // TODO(brianwilkerson) Get the list of glob patterns from the server after
427 // an API for getting them has been implemented.
428 fileGlobs = <Glob>[
429 new Glob(path.context.separator, '**.dart'),
430 new Glob(path.context.separator, '**.html'),
431 new Glob(path.context.separator, '**.htm'),
432 new Glob(path.context.separator, '**/.analysisOptions')
433 ];
434 try {
435 await _replayChanges();
436 } finally {
437 server.sendServerShutdown();
438 repository.checkout('master');
439 }
440 stopwatch.stop();
441 }
442
443 /**
309 * Display usage information, preceeded by the [errorMessage] if one is given. 444 * Display usage information, preceeded by the [errorMessage] if one is given.
310 */ 445 */
311 void _showUsage(ArgParser parser, [String errorMessage = null]) { 446 void _showUsage(ArgParser parser, [String errorMessage = null]) {
312 if (errorMessage != null) { 447 if (errorMessage != null) {
313 stderr.writeln(errorMessage); 448 stderr.writeln(errorMessage);
314 stderr.writeln(); 449 stderr.writeln();
315 } 450 }
316 stderr.writeln(''' 451 stderr.writeln('''
317 Usage: replay [options...] repositoryPath analysisRoot... 452 Usage: replay [options...] repositoryPath analysisRoot...
318 453
(...skipping 12 matching lines...) Expand all
331 OPTIONS:'''); 466 OPTIONS:''');
332 stderr.writeln(parser.usage); 467 stderr.writeln(parser.usage);
333 } 468 }
334 } 469 }
335 470
336 /** 471 /**
337 * A representation of the edits to be applied to a single file. 472 * A representation of the edits to be applied to a single file.
338 */ 473 */
339 class FileEdit { 474 class FileEdit {
340 /** 475 /**
476 * The style of interaction to use for analysis.updateContent requests.
477 */
478 OverlayStyle overlayStyle;
479
480 /**
341 * The absolute path of the file to be edited. 481 * The absolute path of the file to be edited.
342 */ 482 */
343 String filePath; 483 String filePath;
344 484
345 /** 485 /**
346 * The content of the file before any edits have been applied. 486 * The content of the file before any edits have been applied.
347 */ 487 */
348 String content; 488 String content;
349 489
350 /** 490 /**
351 * The line info for the file before any edits have been applied. 491 * The line info for the file before any edits have been applied.
352 */ 492 */
353 LineInfo lineInfo; 493 LineInfo lineInfo;
354 494
355 /** 495 /**
356 * The lists of source edits, one list for each hunk being edited. 496 * The lists of source edits, one list for each hunk being edited.
357 */ 497 */
358 List<List<SourceEdit>> editLists = <List<SourceEdit>>[]; 498 List<List<SourceEdit>> editLists = <List<SourceEdit>>[];
359 499
360 /** 500 /**
501 * The current content of the file. This field is only used if the overlay
502 * style is [OverlayStyle.multipleAdd].
503 */
504 String currentContent;
505
506 /**
361 * Initialize a collection of edits to be associated with the file at the 507 * Initialize a collection of edits to be associated with the file at the
362 * given [filePath]. 508 * given [filePath].
363 */ 509 */
364 FileEdit(DiffRecord record) { 510 FileEdit(this.overlayStyle, DiffRecord record) {
365 filePath = record.srcPath; 511 filePath = record.srcPath;
366 if (record.isAddition) { 512 if (record.isAddition) {
367 content = ''; 513 content = '';
368 lineInfo = new LineInfo(<int>[0]); 514 lineInfo = new LineInfo(<int>[0]);
369 } else if (record.isCopy || record.isRename || record.isTypeChange) { 515 } else if (record.isCopy || record.isRename || record.isTypeChange) {
370 throw new ArgumentError('Unhandled change of type ${record.status}'); 516 throw new ArgumentError('Unhandled change of type ${record.status}');
371 } else { 517 } else {
372 content = new File(filePath).readAsStringSync(); 518 content = new File(filePath).readAsStringSync();
373 lineInfo = new LineInfo(StringUtilities.computeLineStarts(content)); 519 lineInfo = new LineInfo(StringUtilities.computeLineStarts(content));
374 } 520 }
521 currentContent = content;
375 } 522 }
376 523
377 /** 524 /**
378 * Add a list of source edits that, taken together, transform a single hunk in 525 * Add a list of source edits that, taken together, transform a single hunk in
379 * the file. 526 * the file.
380 */ 527 */
381 void addSourceEdits(List<SourceEdit> sourceEdits) { 528 void addSourceEdits(List<SourceEdit> sourceEdits) {
382 editLists.add(sourceEdits); 529 editLists.add(sourceEdits);
383 } 530 }
384 531
385 /** 532 /**
386 * Return a list of operations to be sent to the server. 533 * Return a list of operations to be sent to the server.
387 */ 534 */
388 List<ServerOperation> getOperations() { 535 List<ServerOperation> getOperations() {
536 List<ServerOperation> operations = <ServerOperation>[];
537 void addUpdateContent(var overlay) {
538 operations.add(new Analysis_UpdateContent(filePath, overlay));
539 }
540
389 // TODO(brianwilkerson) Randomize. 541 // TODO(brianwilkerson) Randomize.
390 // Make the order of edits random. Doing so will require updating the 542 // Make the order of edits random. Doing so will require updating the
391 // offsets of edits after the selected edit point. 543 // offsets of edits after the selected edit point.
392 List<ServerOperation> operations = <ServerOperation>[]; 544 addUpdateContent(new AddContentOverlay(content));
393 operations.add(
394 new AnalysisUpdateContent(filePath, new AddContentOverlay(content)));
395 for (List<SourceEdit> editList in editLists.reversed) { 545 for (List<SourceEdit> editList in editLists.reversed) {
396 for (SourceEdit edit in editList.reversed) { 546 for (SourceEdit edit in editList.reversed) {
397 operations.add(new AnalysisUpdateContent( 547 var overlay = null;
398 filePath, new ChangeContentOverlay([edit]))); 548 if (overlayStyle == OverlayStyle.change) {
549 overlay = new ChangeContentOverlay([edit]);
550 } else if (overlayStyle == OverlayStyle.multipleAdd) {
551 currentContent = edit.apply(currentContent);
552 overlay = new AddContentOverlay(currentContent);
553 } else {
554 throw new StateError(
555 'Failed to handle overlay style = $overlayStyle');
556 }
557 if (overlay != null) {
558 addUpdateContent(overlay);
559 }
399 } 560 }
400 } 561 }
401 operations 562 addUpdateContent(new RemoveContentOverlay());
402 .add(new AnalysisUpdateContent(filePath, new RemoveContentOverlay()));
403 return operations; 563 return operations;
404 } 564 }
405 } 565 }
406 566
407 /** 567 /**
568 * The possible styles of interaction to use for analysis.updateContent requests .
569 */
570 enum OverlayStyle { change, multipleAdd }
571
572 /**
408 * A set of statistics related to the execution of the simulation. 573 * A set of statistics related to the execution of the simulation.
409 */ 574 */
410 class Statistics { 575 class Statistics {
411 /** 576 /**
412 * The driver driving the simulation. 577 * The driver driving the simulation.
413 */ 578 */
414 final Driver driver; 579 final Driver driver;
415 580
416 /** 581 /**
417 * The stopwatch being used to time the simulation. 582 * The stopwatch being used to time the simulation.
418 */ 583 */
419 Stopwatch stopwatch; 584 Stopwatch stopwatch;
420 585
421 /** 586 /**
422 * The total number of commits in the repository. 587 * The total number of commits in the repository.
423 */ 588 */
424 int commitCount; 589 int commitCount;
425 590
426 /** 591 /**
427 * The number of commits in the repository that touched one of the files in 592 * The number of commits in the repository that touched one of the files in
428 * one of the analysis roots. 593 * one of the analysis roots.
429 */ 594 */
430 int commitsWithChangeInRootCount = 0; 595 int commitsWithChangeInRootCount = 0;
431 596
432 /** 597 /**
433 * Initialize a newly created set of statistics. 598 * Initialize a newly created set of statistics.
434 */ 599 */
435 Statistics(this.driver); 600 Statistics(this.driver);
436 601
602 /**
603 * Print the statistics to [stdout].
604 */
437 void print() { 605 void print() {
438 stdout.write('Replay commits in '); 606 stdout.write('Replay commits in ');
439 stdout.writeln(driver.repositoryPath); 607 stdout.writeln(driver.repositoryPath);
440 stdout.write(' replay took '); 608 stdout.write(' replay took ');
441 stdout.writeln(_printTime(stopwatch.elapsedMilliseconds)); 609 stdout.writeln(_printTime(stopwatch.elapsedMilliseconds));
442 stdout.write(' analysis roots = '); 610 stdout.write(' analysis roots = ');
443 stdout.writeln(driver.analysisRoots); 611 stdout.writeln(driver.analysisRoots);
444 stdout.write(' number of commits = '); 612 stdout.write(' number of commits = ');
445 stdout.writeln(commitCount); 613 stdout.writeln(commitCount);
446 stdout.write(' number of commits with a change in an analysis root = '); 614 stdout.write(' number of commits with a change in an analysis root = ');
447 stdout.writeln(commitsWithChangeInRootCount); 615 stdout.writeln(commitsWithChangeInRootCount);
448 } 616 }
449 617
618 /**
619 * Return a textual representation of the given duration, represented in
620 * [milliseconds].
621 */
450 String _printTime(int milliseconds) { 622 String _printTime(int milliseconds) {
451 int seconds = milliseconds ~/ 1000; 623 int seconds = milliseconds ~/ 1000;
452 milliseconds -= seconds * 1000; 624 milliseconds -= seconds * 1000;
453 int minutes = seconds ~/ 60; 625 int minutes = seconds ~/ 60;
454 seconds -= minutes * 60; 626 seconds -= minutes * 60;
455 int hours = minutes ~/ 60; 627 int hours = minutes ~/ 60;
456 minutes -= hours * 60; 628 minutes -= hours * 60;
457 629
458 if (hours > 0) { 630 if (hours > 0) {
459 return '$hours:$minutes:$seconds.$milliseconds'; 631 return '$hours:$minutes:$seconds.$milliseconds';
460 } else if (minutes > 0) { 632 } else if (minutes > 0) {
461 return '$minutes:$seconds.$milliseconds m'; 633 return '$minutes:$seconds.$milliseconds m';
462 } else if (seconds > 0) { 634 } else if (seconds > 0) {
463 return '$seconds.$milliseconds s'; 635 return '$seconds.$milliseconds s';
464 } 636 }
465 return '$milliseconds ms'; 637 return '$milliseconds ms';
466 } 638 }
467 } 639 }
OLDNEW
« no previous file with comments | « pkg/analysis_server/test/stress/replay/operation.dart ('k') | pkg/analysis_server/test/stress/utilities/server.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698