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

Unified Diff: pkg/analysis_server/test/integration/integration_tests.dart

Issue 439313003: Add more matchers for use in analysis server integration testing. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 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 side-by-side diff with in-line comments
Download patch
Index: pkg/analysis_server/test/integration/integration_tests.dart
diff --git a/pkg/analysis_server/test/integration/integration_tests.dart b/pkg/analysis_server/test/integration/integration_tests.dart
index 47b18561edab9ecdd7bf4820c4e25c723cb711f3..5488d3fe44a2e2735a01854c7cb284a17fb66927 100644
--- a/pkg/analysis_server/test/integration/integration_tests.dart
+++ b/pkg/analysis_server/test/integration/integration_tests.dart
@@ -13,6 +13,8 @@ import 'package:analysis_server/src/constants.dart';
import 'package:path/path.dart';
import 'package:unittest/unittest.dart';
+import 'protocol_matchers.dart';
+
/**
* Base class for analysis server integration tests.
*/
@@ -150,7 +152,9 @@ abstract class AbstractAnalysisServerIntegrationTest {
});
return server.start().then((params) {
serverConnectedParams = params;
- server.exitCode.then((_) { skipShutdown = true; });
+ server.exitCode.then((_) {
+ skipShutdown = true;
+ });
return serverConnected.future;
});
}
@@ -181,137 +185,124 @@ abstract class AbstractAnalysisServerIntegrationTest {
}
}
-// Matchers for data types defined in the analysis server API
-// ==========================================================
-// TODO(paulberry): add more matchers.
-
-// Matchers common to all domains
-// ------------------------------
-
-const Matcher isResponse = const MatchesJsonObject('response', const {
+final Matcher isResponse = new MatchesJsonObject('response', {
'id': isString
-}, optionalFields: const {
+}, optionalFields: {
'result': anything,
'error': isError
});
-const Matcher isError = const MatchesJsonObject('Error', const {
- // TODO(paulberry): once we decide what the set of permitted error codes are,
- // add validation for 'code'.
- 'code': anything,
- 'message': isString
-}, optionalFields: const {
- // TODO(paulberry): API spec says that 'data' is required, but sometimes we
- // don't see it (example: error "Expected parameter subscriptions to be a
- // string list map" in response to a malformed "analysis.setSubscriptions"
- // command).
- 'data': anything
-});
-
const Matcher isNotification = const MatchesJsonObject('notification', const {
'event': isString
}, optionalFields: const {
'params': isMap
});
-// Matchers for specific responses and notifications
-// -------------------------------------------------
-
-// server.getVersion
-const Matcher isServerGetVersionResult = const MatchesJsonObject(
- 'server.getVersion result', const {
- 'version': isString
-});
-
-// server.status
-const Matcher isServerStatusParams = const MatchesJsonObject(
- 'server.status params', null, optionalFields: const {
- 'analysis': isAnalysisStatus
-});
-
-// analysis.getErrors
-final Matcher isAnalysisGetErrorsResult = new MatchesJsonObject(
- 'analysis.getErrors result', {
- 'errors': isListOf(isAnalysisError)
-});
-
-// analysis.getHover
-final Matcher isAnalysisGetHoverResult = new MatchesJsonObject(
- 'analysis.getHover result', {
- 'hovers': isListOf(isHoverInformation)
-});
-
-// Matchers for data types used in responses and notifications
-// -----------------------------------------------------------
-
const Matcher isString = const isInstanceOf<String>('String');
const Matcher isInt = const isInstanceOf<int>('int');
const Matcher isBool = const isInstanceOf<bool>('bool');
-// AnalysisError
-final Matcher isAnalysisError = new MatchesJsonObject('AnalysisError', {
- 'severity': isErrorSeverity,
- 'type': isErrorType,
- 'location': isLocation,
- 'message': isString,
-}, optionalFields: {
- 'correction': isString
-});
+const Matcher isObject = isMap;
-// AnalysisStatus
-const Matcher isAnalysisStatus = const MatchesJsonObject('AnalysisStatus', const
- {
- 'analyzing': isBool
-}, optionalFields: const {
- 'analysisTarget': isString
-});
+/**
+ * Type of closures used by MatchesJsonObject to record field mismatches.
+ */
+typedef Description MismatchDescriber(Description mismatchDescription);
-// ErrorSeverity
-final Matcher isErrorSeverity = isIn(['INFO', 'WARNING', 'ERROR']);
+/**
+ * Base class for matchers that operate by recursing through the contents of
+ * an object.
+ */
+abstract class _RecursiveMatcher extends Matcher {
+ const _RecursiveMatcher();
-// ErrorType
-final Matcher isErrorType = isIn(['COMPILE_TIME_ERROR', 'HINT',
- 'STATIC_TYPE_WARNING', 'STATIC_WARNING', 'SYNTACTIC_ERROR', 'TODO']);
+ @override
+ bool matches(item, Map matchState) {
+ List<MismatchDescriber> mismatches = <MismatchDescriber>[];
+ populateMismatches(item, mismatches);
+ if (mismatches.isEmpty) {
+ return true;
+ } else {
+ addStateInfo(matchState, {
+ 'mismatches': mismatches
+ });
+ return false;
+ }
+ }
-// HoverInformation
-const Matcher isHoverInformation = const MatchesJsonObject('HoverInformation',
- const {
- 'offset': isInt,
- 'length': isInt
-}, optionalFields: const {
- 'containingLibraryPath': isString,
- 'containingLibraryName': isString,
- 'dartdoc': isString,
- 'elementDescription': isString,
- 'elementKind': isString,
- 'parameter': isString,
- 'propagatedType': isString,
- 'staticType': isString
-});
+ @override
+ Description describeMismatch(item, Description mismatchDescription, Map
+ matchState, bool verbose) {
+ List<MismatchDescriber> mismatches = matchState['mismatches'];
+ if (mismatches != null) {
+ for (int i = 0; i < mismatches.length; i++) {
+ MismatchDescriber mismatch = mismatches[i];
+ if (i > 0) {
+ if (mismatches.length == 2) {
+ mismatchDescription = mismatchDescription.add(' and ');
+ } else if (i == mismatches.length - 1) {
+ mismatchDescription = mismatchDescription.add(', and ');
+ } else {
+ mismatchDescription = mismatchDescription.add(', ');
+ }
+ }
+ mismatchDescription = mismatch(mismatchDescription);
+ }
+ return mismatchDescription;
+ } else {
+ return super.describeMismatch(item, mismatchDescription, matchState,
+ verbose);
+ }
+ }
-// Location
-const Matcher isLocation = const MatchesJsonObject('Location', const {
- 'file': isString,
- 'offset': isInt,
- 'length': isInt,
- 'startLine': isInt,
- 'startColumn': isInt
-});
+ /**
+ * Populate [mismatches] with descriptions of all the ways in which [item]
+ * does not match.
+ */
+ void populateMismatches(item, List<MismatchDescriber> mismatches);
+ /**
+ * Create a [MismatchDescriber] describing a mismatch with a simple string.
+ */
+ MismatchDescriber simpleDescription(String description) => (Description
+ mismatchDescription) {
+ mismatchDescription.add(description);
+ };
-/**
- * Type of closures used by MatchesJsonObject to record field mismatches.
- */
-typedef Description MismatchDescriber(Description mismatchDescription, bool
- verbose);
+ /**
+ * Check the type of a substructure whose value is [item], using [matcher].
+ * If it doesn't match, record a closure in [mismatches] which can describe
+ * the mismatch. [describeSubstructure] is used to describe which
+ * substructure did not match.
+ */
+ checkSubstructure(item, Matcher matcher, List<MismatchDescriber>
+ mismatches, Description describeSubstructure(Description)) {
+ Map subState = {};
+ if (!matcher.matches(item, subState)) {
+ mismatches.add((Description mismatchDescription) {
+ mismatchDescription = mismatchDescription.add('contains malformed ');
+ mismatchDescription = describeSubstructure(mismatchDescription);
+ mismatchDescription = mismatchDescription.add(' (should be '
+ ).addDescriptionOf(matcher);
+ String subDescription = matcher.describeMismatch(item,
+ new StringDescription(), subState, false).toString();
+ if (subDescription.isNotEmpty) {
+ mismatchDescription = mismatchDescription.add('; ').add(subDescription
+ );
+ }
+ return mismatchDescription.add(')');
+ });
+ }
+ }
+}
/**
* Matcher that matches a JSON object, with a given set of required and
* optional fields, and their associated types (expressed as [Matcher]s).
*/
-class MatchesJsonObject extends Matcher {
+class MatchesJsonObject extends _RecursiveMatcher {
/**
* Short description of the expected type.
*/
@@ -333,15 +324,15 @@ class MatchesJsonObject extends Matcher {
MatchesJsonObject(this.description, this.requiredFields, {this.optionalFields});
@override
- bool matches(item, Map matchState) {
+ void populateMismatches(item, List<MismatchDescriber> mismatches) {
if (item is! Map) {
- return false;
+ mismatches.add(simpleDescription('is not a map'));
+ return;
}
- List<MismatchDescriber> mismatches = <MismatchDescriber>[];
if (requiredFields != null) {
requiredFields.forEach((String key, Matcher valueMatcher) {
if (!item.containsKey(key)) {
- mismatches.add((Description mismatchDescription, bool verbose) =>
+ mismatches.add((Description mismatchDescription) =>
mismatchDescription.add('is missing field ').addDescriptionOf(key).add(' ('
).addDescriptionOf(valueMatcher).add(')'));
} else {
@@ -355,49 +346,16 @@ class MatchesJsonObject extends Matcher {
} else if (optionalFields != null && optionalFields.containsKey(key)) {
_checkField(key, value, optionalFields[key], mismatches);
} else {
- mismatches.add((Description mismatchDescription, bool verbose) =>
+ mismatches.add((Description mismatchDescription) =>
mismatchDescription.add('has unexpected field ').addDescriptionOf(key));
}
});
- if (mismatches.isEmpty) {
- return true;
- } else {
- addStateInfo(matchState, {
- 'mismatches': mismatches
- });
- return false;
- }
}
@override
Description describe(Description description) => description.add(
this.description);
- @override
- Description describeMismatch(item, Description mismatchDescription, Map
- matchState, bool verbose) {
- List<MismatchDescriber> mismatches = matchState['mismatches'];
- if (mismatches != null) {
- for (int i = 0; i < mismatches.length; i++) {
- MismatchDescriber mismatch = mismatches[i];
- if (i > 0) {
- if (mismatches.length == 2) {
- mismatchDescription = mismatchDescription.add(' and ');
- } else if (i == mismatches.length - 1) {
- mismatchDescription = mismatchDescription.add(', and ');
- } else {
- mismatchDescription = mismatchDescription.add(', ');
- }
- }
- mismatchDescription = mismatch(mismatchDescription, verbose);
- }
- return mismatchDescription;
- } else {
- return super.describeMismatch(item, mismatchDescription, matchState,
- verbose);
- }
- }
-
/**
* Check the type of a field called [key], having value [value], using
* [valueMatcher]. If it doesn't match, record a closure in [mismatches]
@@ -405,21 +363,8 @@ class MatchesJsonObject extends Matcher {
*/
void _checkField(String key, value, Matcher
valueMatcher, List<MismatchDescriber> mismatches) {
- Map subState = {};
- if (!valueMatcher.matches(value, subState)) {
- mismatches.add((Description mismatchDescription, bool verbose) {
- mismatchDescription = mismatchDescription.add(
- 'contains malformed field ').addDescriptionOf(key).add(' (should be '
- ).addDescriptionOf(valueMatcher);
- String subDescription = valueMatcher.describeMismatch(value,
- new StringDescription(), subState, false).toString();
- if (subDescription.isNotEmpty) {
- mismatchDescription = mismatchDescription.add('; ').add(subDescription
- );
- }
- return mismatchDescription.add(')');
- });
- }
+ checkSubstructure(value, valueMatcher, mismatches, (Description description)
+ => description.add('field ').addDescriptionOf(key));
}
}
@@ -470,6 +415,45 @@ class _ListOf extends Matcher {
Matcher isListOf(Matcher elementMatcher) => new _ListOf(elementMatcher);
/**
+ * Matcher that matches a map of objects, where each key/value pair in the
+ * map satisies the given key and value matchers.
+ */
+class _MapOf extends _RecursiveMatcher {
+ /**
+ * Matcher which every key in the map must satisfy.
+ */
+ final Matcher keyMatcher;
+
+ /**
+ * Matcher which every value in the map must satisfy.
+ */
+ final Matcher valueMatcher;
+
+ _MapOf(this.keyMatcher, this.valueMatcher);
+
+ @override
+ void populateMismatches(item, List<MismatchDescriber> mismatches) {
+ if (item is! Map) {
+ mismatches.add(simpleDescription('is not a map'));
+ return;
+ }
+ item.forEach((key, value) {
+ checkSubstructure(key, keyMatcher, mismatches, (Description description)
+ => description.add('key ').addDescriptionOf(key));
+ checkSubstructure(value, valueMatcher, mismatches, (Description
+ description) => description.add('field ').addDescriptionOf(key));
+ });
+ }
+
+ @override
+ Description describe(Description description) => description.add('Map from '
+ ).addDescriptionOf(keyMatcher).add(' to ').addDescriptionOf(valueMatcher);
+}
+
+Matcher isMapOf(Matcher keyMatcher, Matcher valueMatcher) => new _MapOf(
+ keyMatcher, valueMatcher);
+
+/**
* Instances of the class [Server] manage a connection to a server process, and
* facilitate communication to and from the server.
*/

Powered by Google App Engine
This is Rietveld 408576698