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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: pkg/analysis_server/test/stress/replay/replay.dart
diff --git a/pkg/analysis_server/test/stress/replay/replay.dart b/pkg/analysis_server/test/stress/replay/replay.dart
index 37de9114da57fad6d84c2a367d62ad81879a35b2..159944f92395a33cd5c3a3c6f318363c377bab71 100644
--- a/pkg/analysis_server/test/stress/replay/replay.dart
+++ b/pkg/analysis_server/test/stress/replay/replay.dart
@@ -11,7 +11,9 @@ import 'dart:async';
import 'dart:io';
import 'package:analysis_server/plugin/protocol/protocol.dart';
+import 'package:analyzer/src/generated/error.dart' as error;
import 'package:analyzer/src/generated/java_engine.dart';
+import 'package:analyzer/src/generated/scanner.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/util/glob.dart';
import 'package:args/args.dart';
@@ -34,11 +36,31 @@ Future main(List<String> arguments) async {
*/
class Driver {
/**
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
+ * The value of the [OVERLAY_STYLE_OPTION_NAME] indicating that modifications
+ * to a file should be represented by an add overlay, followed by zero or more
+ * change overlays, followed by a remove overlay.
+ */
+ static String CHANGE_OVERLAY_STYLE = 'change';
+
+ /**
* The name of the command-line flag that will print help text.
*/
static String HELP_FLAG_NAME = 'help';
/**
+ * The value of the [OVERLAY_STYLE_OPTION_NAME] indicating that modifications
+ * to a file should be represented by an add overlay, followed by zero or more
+ * additional add overlays, followed by a remove overlay.
+ */
+ static String MULTIPLE_ADD_OVERLAY_STYLE = 'multipleAdd';
+
+ /**
+ * The name of the command-line option used to specify the style of
+ * interaction to use when making `analysis.updateContent` requests.
+ */
+ static String OVERLAY_STYLE_OPTION_NAME = 'overlay-style';
+
+ /**
* The name of the pubspec file.
*/
static const String PUBSPEC_FILE_NAME = 'pubspec.yaml';
@@ -49,6 +71,11 @@ class Driver {
static const String TEMP_BRANCH_NAME = 'temp';
/**
+ * The style of interaction to use for analysis.updateContent requests.
+ */
+ OverlayStyle overlayStyle;
+
+ /**
* The absolute path of the repository.
*/
String repositoryPath;
@@ -87,75 +114,24 @@ class Driver {
}
/**
- * Run the test based on the given command-line arguments ([args]).
+ * Run the simulation based on the given command-line arguments ([args]).
*/
Future run(List<String> args) async {
//
// Process the command-line arguments.
//
- ArgParser parser = _createArgParser();
- ArgResults results;
- try {
- results = parser.parse(args);
- } catch (exception) {
- _showUsage(parser);
+ if (!_processCommandLine(args)) {
return null;
}
-
- if (results[HELP_FLAG_NAME]) {
- _showUsage(parser);
- return null;
- }
-
- List<String> arguments = results.arguments;
- if (arguments.length < 2) {
- _showUsage(parser);
- return null;
- }
- repositoryPath = path.normalize(arguments[0]);
- repository = new GitRepository(repositoryPath);
-
- analysisRoots = arguments
- .sublist(1)
- .map((String analysisRoot) => path.normalize(analysisRoot))
- .toList();
- for (String analysisRoot in analysisRoots) {
- if (repositoryPath != analysisRoot &&
- !path.isWithin(repositoryPath, analysisRoot)) {
- _showUsage(parser,
- 'Analysis roots must be contained within the repository: $analysisRoot');
- return null;
- }
- }
//
- // Replay the commit history.
+ // Simulate interactions with the server.
//
- Stopwatch stopwatch = new Stopwatch();
- statistics.stopwatch = stopwatch;
- stopwatch.start();
- await server.start();
- server.sendServerSetSubscriptions([ServerService.STATUS]);
- server.sendAnalysisSetGeneralSubscriptions(
- [GeneralAnalysisService.ANALYZED_FILES]);
- // TODO(brianwilkerson) Get the list of glob patterns from the server after
- // an API for getting them has been implemented.
- fileGlobs = <Glob>[
- new Glob(path.context.separator, '**.dart'),
- new Glob(path.context.separator, '**.html'),
- new Glob(path.context.separator, '**.htm'),
- new Glob(path.context.separator, '**/.analysisOptions')
- ];
- try {
- _replayChanges();
- } finally {
- server.sendServerShutdown();
- repository.checkout('master');
- }
- stopwatch.stop();
+ await _runSimulation();
//
// Print out statistics gathered while performing the simulation.
//
statistics.print();
+ exit(0);
return null;
}
@@ -170,24 +146,63 @@ class Driver {
help: 'Print usage information',
defaultsTo: false,
negatable: false);
+
+ parser.addOption(OVERLAY_STYLE_OPTION_NAME,
+ help:
+ 'The style of interaction to use for analysis.updateContent requests',
+ allowed: [CHANGE_OVERLAY_STYLE, MULTIPLE_ADD_OVERLAY_STYLE],
+ allowedHelp: {
+ CHANGE_OVERLAY_STYLE: '<add> <change>* <remove>',
+ MULTIPLE_ADD_OVERLAY_STYLE: '<add>+ <remove>'
+ },
+ defaultsTo: 'change');
return parser;
}
+ /**
+ * Add source edits to the given [fileEdit] based on the given [blobDiff].
+ */
void _createSourceEdits(FileEdit fileEdit, BlobDiff blobDiff) {
LineInfo info = fileEdit.lineInfo;
for (DiffHunk hunk in blobDiff.hunks) {
- List<SourceEdit> sourceEdits = <SourceEdit>[];
int srcStart = info.getOffsetOfLine(hunk.srcLine);
int srcEnd = info.getOffsetOfLine(hunk.srcLine + hunk.removeLines.length);
- // TODO(brianwilkerson) Create multiple edits instead of a single edit.
- sourceEdits.add(new SourceEdit(
- srcStart, srcEnd - srcStart + 1, _join(hunk.addLines)));
+ String addedText = _join(hunk.addLines);
+ //
+ // Create the source edits.
+ //
+ List<int> breakOffsets = _getBreakOffsets(addedText);
+ int breakCount = breakOffsets.length;
+ List<SourceEdit> sourceEdits = <SourceEdit>[];
+ if (breakCount == 0) {
+ sourceEdits
+ .add(new SourceEdit(srcStart, srcEnd - srcStart + 1, addedText));
+ } else {
+ int previousOffset = breakOffsets[0];
+ String string = addedText.substring(0, previousOffset);
+ sourceEdits
+ .add(new SourceEdit(srcStart, srcEnd - srcStart + 1, string));
+ String reconstruction = string;
+ for (int i = 1; i < breakCount; i++) {
+ int offset = breakOffsets[i];
+ string = addedText.substring(previousOffset, offset);
+ reconstruction += string;
+ sourceEdits.add(new SourceEdit(srcStart + previousOffset, 0, string));
+ previousOffset = offset;
+ }
+ string = addedText.substring(previousOffset);
+ reconstruction += string;
+ sourceEdits.add(new SourceEdit(srcStart + previousOffset, 0, string));
+ if (reconstruction != addedText) {
+ throw new AssertionError();
+ }
+ }
fileEdit.addSourceEdits(sourceEdits);
}
}
/**
- * Return athe absolute paths of all of the pubspec files in all of the
+ * Return the absolute paths of all of the pubspec files in all of the
* analysis roots.
*/
Iterable<String> _findPubspecsInAnalysisRoots() {
@@ -206,6 +221,33 @@ class Driver {
return pubspecFiles;
}
+ /**
+ * Return a list of offsets into the given [text] that represent good places
+ * to break the text when building edits.
+ */
+ 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
+ List<int> breakOffsets = <int>[];
+ Scanner scanner = new Scanner(null, new CharSequenceReader(text),
+ error.AnalysisErrorListener.NULL_LISTENER);
+ Token token = scanner.tokenize();
+ // TODO(brianwilkerson) Randomize. Sometimes add zero (0) as a break point.
+ while (token.type != TokenType.EOF) {
+ // TODO(brianwilkerson) Break inside comments?
+// Token comment = token.precedingComments;
+ int offset = token.offset;
+ int length = token.length;
+ breakOffsets.add(offset);
+ if (token.type == TokenType.IDENTIFIER && length > 3) {
+ breakOffsets.add(offset + (length ~/ 2));
+ }
+ token = token.next;
+ }
+ return breakOffsets;
+ }
+
+ /**
+ * Join the given [lines] into a single string.
+ */
String _join(List<String> lines) {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < lines.length; i++) {
@@ -215,9 +257,58 @@ class Driver {
}
/**
+ * Process the command-line [arguments]. Return `true` if the simulation
+ * should be run.
+ */
+ bool _processCommandLine(List<String> args) {
+ ArgParser parser = _createArgParser();
+ ArgResults results;
+ try {
+ results = parser.parse(args);
+ } catch (exception) {
+ _showUsage(parser);
+ return false;
+ }
+
+ if (results[HELP_FLAG_NAME]) {
+ _showUsage(parser);
+ return false;
+ }
+
+ String overlayStyleValue = results[OVERLAY_STYLE_OPTION_NAME];
+ if (overlayStyleValue == CHANGE_OVERLAY_STYLE) {
+ overlayStyle = OverlayStyle.change;
+ } else if (overlayStyleValue == MULTIPLE_ADD_OVERLAY_STYLE) {
+ overlayStyle = OverlayStyle.multipleAdd;
+ }
+
+ List<String> arguments = results.arguments;
+ if (arguments.length < 2) {
+ _showUsage(parser);
+ return false;
+ }
+ repositoryPath = path.normalize(arguments[0]);
+ repository = new GitRepository(repositoryPath);
+
+ analysisRoots = arguments
+ .sublist(1)
+ .map((String analysisRoot) => path.normalize(analysisRoot))
+ .toList();
+ for (String analysisRoot in analysisRoots) {
+ if (repositoryPath != analysisRoot &&
+ !path.isWithin(repositoryPath, analysisRoot)) {
+ _showUsage(parser,
+ 'Analysis roots must be contained within the repository: $analysisRoot');
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Replay the changes in each commit.
*/
- void _replayChanges() {
+ Future _replayChanges() async {
//
// Get the revision history of the repo.
//
@@ -228,17 +319,26 @@ class Driver {
// Iterate over the history, applying changes.
//
bool firstCheckout = true;
-// Map<String, List<AnalysisError>> expectedErrors = null;
+ ErrorMap expectedErrors = null;
Iterable<String> changedPubspecs;
while (iterator.moveNext()) {
//
// Checkout the commit on which the changes are based.
//
- repository.checkout(iterator.srcCommit);
-// if (expectedErrors != null) {
-// await server.analysisFinished;
-// server.expectErrorState(expectedErrors);
-// }
+ String commit = iterator.srcCommit;
+ repository.checkout(commit);
+ if (expectedErrors != null) {
+ ErrorMap actualErrors =
+ await server.computeErrorMap(server.analyzedDartFiles);
+ String difference = expectedErrors.expectErrorMap(actualErrors);
+ if (difference != null) {
+ stdout.write('Mismatched errors after commit ');
+ stdout.writeln(commit);
+ stdout.writeln();
+ stdout.writeln(difference);
+ return;
+ }
+ }
if (firstCheckout) {
changedPubspecs = _findPubspecsInAnalysisRoots();
server.sendAnalysisSetAnalysisRoots(analysisRoots, []);
@@ -246,8 +346,7 @@ class Driver {
} else {
server.removeAllOverlays();
}
-// await server.analysisFinished;
-// expectedErrors = server.errorMap;
+ expectedErrors = await server.computeErrorMap(server.analyzedDartFiles);
for (String filePath in changedPubspecs) {
_runPub(filePath);
}
@@ -261,20 +360,28 @@ class Driver {
_replayDiff(commitDelta);
}
changedPubspecs = commitDelta.filesMatching(PUBSPEC_FILE_NAME);
+ stdout.write('.');
}
server.removeAllOverlays();
+ stdout.writeln();
}
+ /**
+ * Replay the changes between two commits, as represented by the given
+ * [commitDelta].
+ */
void _replayDiff(CommitDelta commitDelta) {
List<FileEdit> editList = <FileEdit>[];
for (DiffRecord record in commitDelta.diffRecords) {
- FileEdit edit = new FileEdit(record);
+ FileEdit edit = new FileEdit(overlayStyle, record);
_createSourceEdits(edit, record.getBlobDiff());
editList.add(edit);
}
+ //
// TODO(brianwilkerson) Randomize.
// Randomly select operations from different files to simulate a user
// editing multiple files simultaneously.
+ //
for (FileEdit edit in editList) {
List<String> currentFile = <String>[edit.filePath];
server.sendAnalysisSetPriorityFiles(currentFile);
@@ -306,6 +413,34 @@ class Driver {
}
/**
+ * Run the simulation by starting up a server and sending it requests.
+ */
+ Future _runSimulation() async {
+ Stopwatch stopwatch = new Stopwatch();
+ statistics.stopwatch = stopwatch;
+ stopwatch.start();
+ await server.start();
+ server.sendServerSetSubscriptions([ServerService.STATUS]);
+ server.sendAnalysisSetGeneralSubscriptions(
+ [GeneralAnalysisService.ANALYZED_FILES]);
+ // TODO(brianwilkerson) Get the list of glob patterns from the server after
+ // an API for getting them has been implemented.
+ fileGlobs = <Glob>[
+ new Glob(path.context.separator, '**.dart'),
+ new Glob(path.context.separator, '**.html'),
+ new Glob(path.context.separator, '**.htm'),
+ new Glob(path.context.separator, '**/.analysisOptions')
+ ];
+ try {
+ await _replayChanges();
+ } finally {
+ server.sendServerShutdown();
+ repository.checkout('master');
+ }
+ stopwatch.stop();
+ }
+
+ /**
* Display usage information, preceeded by the [errorMessage] if one is given.
*/
void _showUsage(ArgParser parser, [String errorMessage = null]) {
@@ -338,6 +473,11 @@ OPTIONS:''');
*/
class FileEdit {
/**
+ * The style of interaction to use for analysis.updateContent requests.
+ */
+ OverlayStyle overlayStyle;
+
+ /**
* The absolute path of the file to be edited.
*/
String filePath;
@@ -358,10 +498,16 @@ class FileEdit {
List<List<SourceEdit>> editLists = <List<SourceEdit>>[];
/**
+ * The current content of the file. This field is only used if the overlay
+ * style is [OverlayStyle.multipleAdd].
+ */
+ String currentContent;
+
+ /**
* Initialize a collection of edits to be associated with the file at the
* given [filePath].
*/
- FileEdit(DiffRecord record) {
+ FileEdit(this.overlayStyle, DiffRecord record) {
filePath = record.srcPath;
if (record.isAddition) {
content = '';
@@ -372,6 +518,7 @@ class FileEdit {
content = new File(filePath).readAsStringSync();
lineInfo = new LineInfo(StringUtilities.computeLineStarts(content));
}
+ currentContent = content;
}
/**
@@ -386,25 +533,43 @@ class FileEdit {
* Return a list of operations to be sent to the server.
*/
List<ServerOperation> getOperations() {
+ List<ServerOperation> operations = <ServerOperation>[];
+ void addUpdateContent(var overlay) {
+ operations.add(new Analysis_UpdateContent(filePath, overlay));
+ }
+
// TODO(brianwilkerson) Randomize.
// Make the order of edits random. Doing so will require updating the
// offsets of edits after the selected edit point.
- List<ServerOperation> operations = <ServerOperation>[];
- operations.add(
- new AnalysisUpdateContent(filePath, new AddContentOverlay(content)));
+ addUpdateContent(new AddContentOverlay(content));
for (List<SourceEdit> editList in editLists.reversed) {
for (SourceEdit edit in editList.reversed) {
- operations.add(new AnalysisUpdateContent(
- filePath, new ChangeContentOverlay([edit])));
+ var overlay = null;
+ if (overlayStyle == OverlayStyle.change) {
+ overlay = new ChangeContentOverlay([edit]);
+ } else if (overlayStyle == OverlayStyle.multipleAdd) {
+ currentContent = edit.apply(currentContent);
+ overlay = new AddContentOverlay(currentContent);
+ } else {
+ throw new StateError(
+ 'Failed to handle overlay style = $overlayStyle');
+ }
+ if (overlay != null) {
+ addUpdateContent(overlay);
+ }
}
}
- operations
- .add(new AnalysisUpdateContent(filePath, new RemoveContentOverlay()));
+ addUpdateContent(new RemoveContentOverlay());
return operations;
}
}
/**
+ * The possible styles of interaction to use for analysis.updateContent requests.
+ */
+enum OverlayStyle { change, multipleAdd }
+
+/**
* A set of statistics related to the execution of the simulation.
*/
class Statistics {
@@ -434,6 +599,9 @@ class Statistics {
*/
Statistics(this.driver);
+ /**
+ * Print the statistics to [stdout].
+ */
void print() {
stdout.write('Replay commits in ');
stdout.writeln(driver.repositoryPath);
@@ -447,6 +615,10 @@ class Statistics {
stdout.writeln(commitsWithChangeInRootCount);
}
+ /**
+ * Return a textual representation of the given duration, represented in
+ * [milliseconds].
+ */
String _printTime(int milliseconds) {
int seconds = milliseconds ~/ 1000;
milliseconds -= seconds * 1000;
« 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