From 7e36498dd4c94e472ee8a0aa3abe8f7268f469af Mon Sep 17 00:00:00 2001 From: "paulberry@google.com" Date: Mon, 4 Aug 2014 17:15:09 +0000 Subject: [PATCH] Add more matchers for use in analysis server integration testing. R=jwren@google.com Review URL: https://codereview.chromium.org//439313003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@38868 260f80e4-7a28-3924-810f-c04153c831b5 --- .../integration/analysis_domain_int_test.dart | 1 + .../integration/analysis_error_int_test.dart | 1 + .../test/integration/integration_tests.dart | 302 ++- .../test/integration/protocol_matchers.dart | 1706 +++++++++++++++++ .../integration/server_domain_int_test.dart | 1 + 5 files changed, 1852 insertions(+), 159 deletions(-) create mode 100644 pkg/analysis_server/test/integration/protocol_matchers.dart diff --git a/pkg/analysis_server/test/integration/analysis_domain_int_test.dart b/pkg/analysis_server/test/integration/analysis_domain_int_test.dart index f744c1d9793..7bde9aed38a 100644 --- a/pkg/analysis_server/test/integration/analysis_domain_int_test.dart +++ b/pkg/analysis_server/test/integration/analysis_domain_int_test.dart @@ -12,6 +12,7 @@ import 'package:path/path.dart'; import 'package:unittest/unittest.dart'; import 'integration_tests.dart'; +import 'protocol_matchers.dart'; @ReflectiveTestCase() class AnalysisDomainIntegrationTest extends diff --git a/pkg/analysis_server/test/integration/analysis_error_int_test.dart b/pkg/analysis_server/test/integration/analysis_error_int_test.dart index d43090ba3b0..662957ac824 100644 --- a/pkg/analysis_server/test/integration/analysis_error_int_test.dart +++ b/pkg/analysis_server/test/integration/analysis_error_int_test.dart @@ -8,6 +8,7 @@ import 'package:analysis_testing/reflective_tests.dart'; import 'package:unittest/unittest.dart'; import 'integration_tests.dart'; +import 'protocol_matchers.dart'; @ReflectiveTestCase() class AnalysisErrorIntegrationTest extends AbstractAnalysisServerIntegrationTest diff --git a/pkg/analysis_server/test/integration/integration_tests.dart b/pkg/analysis_server/test/integration/integration_tests.dart index 47b18561eda..5488d3fe44a 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'); const Matcher isInt = const isInstanceOf('int'); const Matcher isBool = const isInstanceOf('bool'); -// AnalysisError -final Matcher isAnalysisError = new MatchesJsonObject('AnalysisError', { - 'severity': isErrorSeverity, - 'type': isErrorType, - 'location': isLocation, - 'message': isString, -}, optionalFields: { - 'correction': isString -}); - -// AnalysisStatus -const Matcher isAnalysisStatus = const MatchesJsonObject('AnalysisStatus', const - { - 'analyzing': isBool -}, optionalFields: const { - 'analysisTarget': isString -}); - -// ErrorSeverity -final Matcher isErrorSeverity = isIn(['INFO', 'WARNING', 'ERROR']); - -// ErrorType -final Matcher isErrorType = isIn(['COMPILE_TIME_ERROR', 'HINT', - 'STATIC_TYPE_WARNING', 'STATIC_WARNING', 'SYNTACTIC_ERROR', 'TODO']); - -// 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 -}); - -// Location -const Matcher isLocation = const MatchesJsonObject('Location', const { - 'file': isString, - 'offset': isInt, - 'length': isInt, - 'startLine': isInt, - 'startColumn': isInt -}); - +const Matcher isObject = isMap; /** * Type of closures used by MatchesJsonObject to record field mismatches. */ -typedef Description MismatchDescriber(Description mismatchDescription, bool - verbose); +typedef Description MismatchDescriber(Description mismatchDescription); + +/** + * Base class for matchers that operate by recursing through the contents of + * an object. + */ +abstract class _RecursiveMatcher extends Matcher { + const _RecursiveMatcher(); + + @override + bool matches(item, Map matchState) { + List mismatches = []; + populateMismatches(item, mismatches); + if (mismatches.isEmpty) { + return true; + } else { + addStateInfo(matchState, { + 'mismatches': mismatches + }); + return false; + } + } + + @override + Description describeMismatch(item, Description mismatchDescription, Map + matchState, bool verbose) { + List 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); + } + } + + /** + * Populate [mismatches] with descriptions of all the ways in which [item] + * does not match. + */ + void populateMismatches(item, List mismatches); + + /** + * Create a [MismatchDescriber] describing a mismatch with a simple string. + */ + MismatchDescriber simpleDescription(String description) => (Description + mismatchDescription) { + mismatchDescription.add(description); + }; + + /** + * 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 + 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 mismatches) { if (item is! Map) { - return false; + mismatches.add(simpleDescription('is not a map')); + return; } - List mismatches = []; 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 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 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)); } } @@ -469,6 +414,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 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. diff --git a/pkg/analysis_server/test/integration/protocol_matchers.dart b/pkg/analysis_server/test/integration/protocol_matchers.dart new file mode 100644 index 00000000000..87f05f417a6 --- /dev/null +++ b/pkg/analysis_server/test/integration/protocol_matchers.dart @@ -0,0 +1,1706 @@ +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/** + * Matchers for data types defined in the analysis server API + */ +library test.integration.protocol.matchers; + +import 'package:unittest/unittest.dart'; + +import 'integration_tests.dart'; + +/** + * server.getVersion params + */ +final Matcher isServerGetVersionParams = isNull; + +/** + * server.getVersion result + * + * { + * "version": String + * } + */ +final Matcher isServerGetVersionResult = new MatchesJsonObject( + "server.getVersion result", { + "version": isString + }); + +/** + * server.shutdown params + */ +final Matcher isServerShutdownParams = isNull; + +/** + * server.shutdown result + */ +final Matcher isServerShutdownResult = isNull; + +/** + * server.setSubscriptions params + * + * { + * "subscriptions": List + * } + */ +final Matcher isServerSetSubscriptionsParams = new MatchesJsonObject( + "server.setSubscriptions params", { + "subscriptions": isListOf(isServerService) + }); + +/** + * server.setSubscriptions result + */ +final Matcher isServerSetSubscriptionsResult = isNull; + +/** + * server.connected params + */ +final Matcher isServerConnectedParams = isNull; + +/** + * server.error params + * + * { + * "fatal": bool + * "message": String + * "stackTrace": String + * } + */ +final Matcher isServerErrorParams = new MatchesJsonObject( + "server.error params", { + "fatal": isBool, + "message": isString, + "stackTrace": isString + }); + +/** + * server.status params + * + * { + * "analysis": optional AnalysisStatus + * } + */ +final Matcher isServerStatusParams = new MatchesJsonObject( + "server.status params", null, optionalFields: { + "analysis": isAnalysisStatus + }); + +/** + * analysis.getErrors params + * + * { + * "file": FilePath + * } + */ +final Matcher isAnalysisGetErrorsParams = new MatchesJsonObject( + "analysis.getErrors params", { + "file": isFilePath + }); + +/** + * analysis.getErrors result + * + * { + * "errors": List + * } + */ +final Matcher isAnalysisGetErrorsResult = new MatchesJsonObject( + "analysis.getErrors result", { + "errors": isListOf(isAnalysisError) + }); + +/** + * analysis.getHover params + * + * { + * "file": FilePath + * "offset": int + * } + */ +final Matcher isAnalysisGetHoverParams = new MatchesJsonObject( + "analysis.getHover params", { + "file": isFilePath, + "offset": isInt + }); + +/** + * analysis.getHover result + * + * { + * "hovers": List + * } + */ +final Matcher isAnalysisGetHoverResult = new MatchesJsonObject( + "analysis.getHover result", { + "hovers": isListOf(isHoverInformation) + }); + +/** + * analysis.reanalyze params + */ +final Matcher isAnalysisReanalyzeParams = isNull; + +/** + * analysis.reanalyze result + */ +final Matcher isAnalysisReanalyzeResult = isNull; + +/** + * analysis.setAnalysisRoots params + * + * { + * "included": List + * "excluded": List + * } + */ +final Matcher isAnalysisSetAnalysisRootsParams = new MatchesJsonObject( + "analysis.setAnalysisRoots params", { + "included": isListOf(isFilePath), + "excluded": isListOf(isFilePath) + }); + +/** + * analysis.setAnalysisRoots result + */ +final Matcher isAnalysisSetAnalysisRootsResult = isNull; + +/** + * analysis.setPriorityFiles params + * + * { + * "files": List + * } + */ +final Matcher isAnalysisSetPriorityFilesParams = new MatchesJsonObject( + "analysis.setPriorityFiles params", { + "files": isListOf(isFilePath) + }); + +/** + * analysis.setPriorityFiles result + */ +final Matcher isAnalysisSetPriorityFilesResult = isNull; + +/** + * analysis.setSubscriptions params + * + * { + * "subscriptions": Map> + * } + */ +final Matcher isAnalysisSetSubscriptionsParams = new MatchesJsonObject( + "analysis.setSubscriptions params", { + "subscriptions": isMapOf(isAnalysisService, isListOf(isFilePath)) + }); + +/** + * analysis.setSubscriptions result + */ +final Matcher isAnalysisSetSubscriptionsResult = isNull; + +/** + * analysis.updateContent params + * + * { + * "files": Map + * } + */ +final Matcher isAnalysisUpdateContentParams = new MatchesJsonObject( + "analysis.updateContent params", { + "files": isMapOf(isFilePath, isContentChange) + }); + +/** + * analysis.updateContent result + */ +final Matcher isAnalysisUpdateContentResult = isNull; + +/** + * analysis.updateOptions params + * + * { + * "options": AnalysisOptions + * } + */ +final Matcher isAnalysisUpdateOptionsParams = new MatchesJsonObject( + "analysis.updateOptions params", { + "options": isAnalysisOptions + }); + +/** + * analysis.updateOptions result + */ +final Matcher isAnalysisUpdateOptionsResult = isNull; + +/** + * analysis.updateSdks params + * + * { + * "added": List + * "removed": List + * "default": optional FilePath + * } + */ +final Matcher isAnalysisUpdateSdksParams = new MatchesJsonObject( + "analysis.updateSdks params", { + "added": isListOf(isFilePath), + "removed": isListOf(isFilePath) + }, optionalFields: { + "default": isFilePath + }); + +/** + * analysis.updateSdks result + */ +final Matcher isAnalysisUpdateSdksResult = isNull; + +/** + * analysis.errors params + * + * { + * "file": FilePath + * "errors": List + * } + */ +final Matcher isAnalysisErrorsParams = new MatchesJsonObject( + "analysis.errors params", { + "file": isFilePath, + "errors": isListOf(isAnalysisError) + }); + +/** + * analysis.folding params + * + * { + * "file": FilePath + * "regions": List + * } + */ +final Matcher isAnalysisFoldingParams = new MatchesJsonObject( + "analysis.folding params", { + "file": isFilePath, + "regions": isListOf(isFoldingRegion) + }); + +/** + * analysis.highlights params + * + * { + * "file": FilePath + * "regions": List + * } + */ +final Matcher isAnalysisHighlightsParams = new MatchesJsonObject( + "analysis.highlights params", { + "file": isFilePath, + "regions": isListOf(isHighlightRegion) + }); + +/** + * analysis.navigation params + * + * { + * "file": FilePath + * "regions": List + * } + */ +final Matcher isAnalysisNavigationParams = new MatchesJsonObject( + "analysis.navigation params", { + "file": isFilePath, + "regions": isListOf(isNavigationRegion) + }); + +/** + * analysis.occurrences params + * + * { + * "file": FilePath + * "occurrences": List + * } + */ +final Matcher isAnalysisOccurrencesParams = new MatchesJsonObject( + "analysis.occurrences params", { + "file": isFilePath, + "occurrences": isListOf(isOccurrences) + }); + +/** + * analysis.outline params + * + * { + * "file": FilePath + * "outline": Outline + * } + */ +final Matcher isAnalysisOutlineParams = new MatchesJsonObject( + "analysis.outline params", { + "file": isFilePath, + "outline": isOutline + }); + +/** + * analysis.overrides params + * + * { + * "file": FilePath + * "overrides": List + * } + */ +final Matcher isAnalysisOverridesParams = new MatchesJsonObject( + "analysis.overrides params", { + "file": isFilePath, + "overrides": isListOf(isOverride) + }); + +/** + * completion.getSuggestions params + * + * { + * "file": FilePath + * "offset": int + * } + */ +final Matcher isCompletionGetSuggestionsParams = new MatchesJsonObject( + "completion.getSuggestions params", { + "file": isFilePath, + "offset": isInt + }); + +/** + * completion.getSuggestions result + * + * { + * "id": CompletionId + * } + */ +final Matcher isCompletionGetSuggestionsResult = new MatchesJsonObject( + "completion.getSuggestions result", { + "id": isCompletionId + }); + +/** + * completion.results params + * + * { + * "id": CompletionId + * "replacementOffset": int + * "replacementLength": int + * "results": List + * "last": bool + * } + */ +final Matcher isCompletionResultsParams = new MatchesJsonObject( + "completion.results params", { + "id": isCompletionId, + "replacementOffset": isInt, + "replacementLength": isInt, + "results": isListOf(isCompletionSuggestion), + "last": isBool + }); + +/** + * search.findElementReferences params + * + * { + * "file": FilePath + * "offset": int + * "includePotential": bool + * } + */ +final Matcher isSearchFindElementReferencesParams = new MatchesJsonObject( + "search.findElementReferences params", { + "file": isFilePath, + "offset": isInt, + "includePotential": isBool + }); + +/** + * search.findElementReferences result + * + * { + * "id": SearchId + * "element": Element + * } + */ +final Matcher isSearchFindElementReferencesResult = new MatchesJsonObject( + "search.findElementReferences result", { + "id": isSearchId, + "element": isElement + }); + +/** + * search.findMemberDeclarations params + * + * { + * "name": String + * } + */ +final Matcher isSearchFindMemberDeclarationsParams = new MatchesJsonObject( + "search.findMemberDeclarations params", { + "name": isString + }); + +/** + * search.findMemberDeclarations result + * + * { + * "id": SearchId + * } + */ +final Matcher isSearchFindMemberDeclarationsResult = new MatchesJsonObject( + "search.findMemberDeclarations result", { + "id": isSearchId + }); + +/** + * search.findMemberReferences params + * + * { + * "name": String + * } + */ +final Matcher isSearchFindMemberReferencesParams = new MatchesJsonObject( + "search.findMemberReferences params", { + "name": isString + }); + +/** + * search.findMemberReferences result + * + * { + * "id": SearchId + * } + */ +final Matcher isSearchFindMemberReferencesResult = new MatchesJsonObject( + "search.findMemberReferences result", { + "id": isSearchId + }); + +/** + * search.findTopLevelDeclarations params + * + * { + * "pattern": String + * } + */ +final Matcher isSearchFindTopLevelDeclarationsParams = new MatchesJsonObject( + "search.findTopLevelDeclarations params", { + "pattern": isString + }); + +/** + * search.findTopLevelDeclarations result + * + * { + * "id": SearchId + * } + */ +final Matcher isSearchFindTopLevelDeclarationsResult = new MatchesJsonObject( + "search.findTopLevelDeclarations result", { + "id": isSearchId + }); + +/** + * search.getTypeHierarchy params + * + * { + * "file": FilePath + * "offset": int + * } + */ +final Matcher isSearchGetTypeHierarchyParams = new MatchesJsonObject( + "search.getTypeHierarchy params", { + "file": isFilePath, + "offset": isInt + }); + +/** + * search.getTypeHierarchy result + * + * { + * "hierarchy": TypeHierarchyItem + * } + */ +final Matcher isSearchGetTypeHierarchyResult = new MatchesJsonObject( + "search.getTypeHierarchy result", { + "hierarchy": isTypeHierarchyItem + }); + +/** + * search.results params + * + * { + * "id": SearchId + * "results": List + * "last": bool + * } + */ +final Matcher isSearchResultsParams = new MatchesJsonObject( + "search.results params", { + "id": isSearchId, + "results": isListOf(isSearchResult), + "last": isBool + }); + +/** + * edit.applyRefactoring params + * + * { + * "id": RefactoringId + * } + */ +final Matcher isEditApplyRefactoringParams = new MatchesJsonObject( + "edit.applyRefactoring params", { + "id": isRefactoringId + }); + +/** + * edit.applyRefactoring result + * + * { + * "status": List + * "change": SourceChange + * } + */ +final Matcher isEditApplyRefactoringResult = new MatchesJsonObject( + "edit.applyRefactoring result", { + "status": isListOf(isRefactoringProblem), + "change": isSourceChange + }); + +/** + * edit.createRefactoring params + * + * { + * "kindId": RefactoringKind + * "file": FilePath + * "offset": int + * "length": int + * } + */ +final Matcher isEditCreateRefactoringParams = new MatchesJsonObject( + "edit.createRefactoring params", { + "kindId": isRefactoringKind, + "file": isFilePath, + "offset": isInt, + "length": isInt + }); + +/** + * edit.createRefactoring result + * + * { + * "id": RefactoringId + * "status": List + * } + */ +final Matcher isEditCreateRefactoringResult = new MatchesJsonObject( + "edit.createRefactoring result", { + "id": isRefactoringId, + "status": isListOf(isRefactoringProblem) + }); + +/** + * edit.deleteRefactoring params + * + * { + * "id": RefactoringId + * } + */ +final Matcher isEditDeleteRefactoringParams = new MatchesJsonObject( + "edit.deleteRefactoring params", { + "id": isRefactoringId + }); + +/** + * edit.deleteRefactoring result + */ +final Matcher isEditDeleteRefactoringResult = isNull; + +/** + * edit.getAssists params + * + * { + * "file": FilePath + * "offset": int + * "length": int + * } + */ +final Matcher isEditGetAssistsParams = new MatchesJsonObject( + "edit.getAssists params", { + "file": isFilePath, + "offset": isInt, + "length": isInt + }); + +/** + * edit.getAssists result + * + * { + * "assists": List + * } + */ +final Matcher isEditGetAssistsResult = new MatchesJsonObject( + "edit.getAssists result", { + "assists": isListOf(isSourceChange) + }); + +/** + * edit.getFixes params + * + * { + * "file": FilePath + * "offset": int + * } + */ +final Matcher isEditGetFixesParams = new MatchesJsonObject( + "edit.getFixes params", { + "file": isFilePath, + "offset": isInt + }); + +/** + * edit.getFixes result + * + * { + * "fixes": List + * } + */ +final Matcher isEditGetFixesResult = new MatchesJsonObject( + "edit.getFixes result", { + "fixes": isListOf(isErrorFixes) + }); + +/** + * edit.getRefactorings params + * + * { + * "file": FilePath + * "offset": int + * "length": int + * } + */ +final Matcher isEditGetRefactoringsParams = new MatchesJsonObject( + "edit.getRefactorings params", { + "file": isFilePath, + "offset": isInt, + "length": isInt + }); + +/** + * edit.getRefactorings result + * + * { + * "kinds": List + * } + */ +final Matcher isEditGetRefactoringsResult = new MatchesJsonObject( + "edit.getRefactorings result", { + "kinds": isListOf(isRefactoringKind) + }); + +/** + * edit.setRefactoringOptions params + * + * { + * "id": RefactoringId + * } + */ +final Matcher isEditSetRefactoringOptionsParams = new MatchesJsonObject( + "edit.setRefactoringOptions params", { + "id": isRefactoringId + }); + +/** + * edit.setRefactoringOptions result + * + * { + * "status": List + * } + */ +final Matcher isEditSetRefactoringOptionsResult = new MatchesJsonObject( + "edit.setRefactoringOptions result", { + "status": isListOf(isRefactoringProblem) + }); + +/** + * debug.createContext params + * + * { + * "contextRoot": FilePath + * } + */ +final Matcher isDebugCreateContextParams = new MatchesJsonObject( + "debug.createContext params", { + "contextRoot": isFilePath + }); + +/** + * debug.createContext result + * + * { + * "id": DebugContextId + * } + */ +final Matcher isDebugCreateContextResult = new MatchesJsonObject( + "debug.createContext result", { + "id": isDebugContextId + }); + +/** + * debug.deleteContext params + * + * { + * "id": DebugContextId + * } + */ +final Matcher isDebugDeleteContextParams = new MatchesJsonObject( + "debug.deleteContext params", { + "id": isDebugContextId + }); + +/** + * debug.deleteContext result + */ +final Matcher isDebugDeleteContextResult = isNull; + +/** + * debug.mapUri params + * + * { + * "id": DebugContextId + * "file": optional FilePath + * "uri": optional String + * } + */ +final Matcher isDebugMapUriParams = new MatchesJsonObject( + "debug.mapUri params", { + "id": isDebugContextId + }, optionalFields: { + "file": isFilePath, + "uri": isString + }); + +/** + * debug.mapUri result + * + * { + * "file": optional FilePath + * "uri": optional String + * } + */ +final Matcher isDebugMapUriResult = new MatchesJsonObject( + "debug.mapUri result", null, optionalFields: { + "file": isFilePath, + "uri": isString + }); + +/** + * debug.setSubscriptions params + * + * { + * "subscriptions": List + * } + */ +final Matcher isDebugSetSubscriptionsParams = new MatchesJsonObject( + "debug.setSubscriptions params", { + "subscriptions": isListOf(isDebugService) + }); + +/** + * debug.setSubscriptions result + */ +final Matcher isDebugSetSubscriptionsResult = isNull; + +/** + * debug.launchData params + * + * { + * "executables": List + * "dartToHtml": Map> + * "htmlToDart": Map> + * } + */ +final Matcher isDebugLaunchDataParams = new MatchesJsonObject( + "debug.launchData params", { + "executables": isListOf(isExecutableFile), + "dartToHtml": isMapOf(isFilePath, isListOf(isFilePath)), + "htmlToDart": isMapOf(isFilePath, isListOf(isFilePath)) + }); + +/** + * AnalysisError + * + * { + * "severity": ErrorSeverity + * "type": ErrorType + * "location": Location + * "message": String + * "correction": optional String + * } + */ +final Matcher isAnalysisError = new MatchesJsonObject( + "AnalysisError", { + "severity": isErrorSeverity, + "type": isErrorType, + "location": isLocation, + "message": isString + }, optionalFields: { + "correction": isString + }); + +/** + * AnalysisOptions + * + * { + * "analyzeAngular": optional bool + * "analyzePolymer": optional bool + * "enableAsync": optional bool + * "enableDeferredLoading": optional bool + * "enableEnums": optional bool + * "generateDart2jsHints": optional bool + * "generateHints": optional bool + * } + */ +final Matcher isAnalysisOptions = new MatchesJsonObject( + "AnalysisOptions", null, optionalFields: { + "analyzeAngular": isBool, + "analyzePolymer": isBool, + "enableAsync": isBool, + "enableDeferredLoading": isBool, + "enableEnums": isBool, + "generateDart2jsHints": isBool, + "generateHints": isBool + }); + +/** + * AnalysisService + * + * enum { + * ERRORS + * FOLDING + * HIGHLIGHTS + * NAVIGATION + * OCCURRENCES + * OUTLINE + * OVERRIDES + * } + */ +final Matcher isAnalysisService = isIn([ + "ERRORS", + "FOLDING", + "HIGHLIGHTS", + "NAVIGATION", + "OCCURRENCES", + "OUTLINE", + "OVERRIDES" +]); + +/** + * AnalysisStatus + * + * { + * "analyzing": bool + * "analysisTarget": optional String + * } + */ +final Matcher isAnalysisStatus = new MatchesJsonObject( + "AnalysisStatus", { + "analyzing": isBool + }, optionalFields: { + "analysisTarget": isString + }); + +/** + * CompletionId + * + * String + */ +final Matcher isCompletionId = isString; + +/** + * CompletionRelevance + * + * enum { + * LOW + * DEFAULT + * HIGH + * } + */ +final Matcher isCompletionRelevance = isIn([ + "LOW", + "DEFAULT", + "HIGH" +]); + +/** + * CompletionSuggestion + * + * { + * "kind": CompletionSuggestionKind + * "relevance": CompletionRelevance + * "completion": String + * "selectionOffset": int + * "selectionLength": int + * "isDeprecated": bool + * "isPotential": bool + * "docSummary": optional String + * "docComplete": optional String + * "declaringType": optional String + * "returnType": optional String + * "parameterNames": optional List + * "parameterTypes": optional List + * "requiredParameterCount": optional int + * "positionalParameterCount": optional int + * "parameterName": optional String + * "parameterType": optional String + * } + */ +final Matcher isCompletionSuggestion = new MatchesJsonObject( + "CompletionSuggestion", { + "kind": isCompletionSuggestionKind, + "relevance": isCompletionRelevance, + "completion": isString, + "selectionOffset": isInt, + "selectionLength": isInt, + "isDeprecated": isBool, + "isPotential": isBool + }, optionalFields: { + "docSummary": isString, + "docComplete": isString, + "declaringType": isString, + "returnType": isString, + "parameterNames": isListOf(isString), + "parameterTypes": isListOf(isString), + "requiredParameterCount": isInt, + "positionalParameterCount": isInt, + "parameterName": isString, + "parameterType": isString + }); + +/** + * CompletionSuggestionKind + * + * enum { + * ARGUMENT_LIST + * CLASS + * CLASS_ALIAS + * CONSTRUCTOR + * FIELD + * FUNCTION + * FUNCTION_TYPE_ALIAS + * GETTER + * IMPORT + * LIBRARY_PREFIX + * METHOD + * METHOD_NAME + * NAMED_ARGUMENT + * OPTIONAL_ARGUMENT + * PARAMETER + * SETTER + * TOP_LEVEL_VARIABLE + * TYPE_PARAMETER + * VARIABLE + * } + */ +final Matcher isCompletionSuggestionKind = isIn([ + "ARGUMENT_LIST", + "CLASS", + "CLASS_ALIAS", + "CONSTRUCTOR", + "FIELD", + "FUNCTION", + "FUNCTION_TYPE_ALIAS", + "GETTER", + "IMPORT", + "LIBRARY_PREFIX", + "METHOD", + "METHOD_NAME", + "NAMED_ARGUMENT", + "OPTIONAL_ARGUMENT", + "PARAMETER", + "SETTER", + "TOP_LEVEL_VARIABLE", + "TYPE_PARAMETER", + "VARIABLE" +]); + +/** + * ContentChange + * + * { + * "content": String + * "offset": optional int + * "oldLength": optional int + * "newLength": optional int + * } + */ +final Matcher isContentChange = new MatchesJsonObject( + "ContentChange", { + "content": isString + }, optionalFields: { + "offset": isInt, + "oldLength": isInt, + "newLength": isInt + }); + +/** + * DebugContextId + * + * String + */ +final Matcher isDebugContextId = isString; + +/** + * DebugService + * + * enum { + * LAUNCH_DATA + * } + */ +final Matcher isDebugService = isIn([ + "LAUNCH_DATA" +]); + +/** + * Element + * + * { + * "kind": ElementKind + * "name": String + * "location": Location + * "flags": int + * "parameters": optional String + * "returnType": optional String + * } + */ +final Matcher isElement = new MatchesJsonObject( + "Element", { + "kind": isElementKind, + "name": isString, + "location": isLocation, + "flags": isInt + }, optionalFields: { + "parameters": isString, + "returnType": isString + }); + +/** + * ElementKind + * + * enum { + * CLASS + * CLASS_TYPE_ALIAS + * COMPILATION_UNIT + * CONSTRUCTOR + * GETTER + * FIELD + * FUNCTION + * FUNCTION_TYPE_ALIAS + * LIBRARY + * LOCAL_VARIABLE + * METHOD + * SETTER + * TOP_LEVEL_VARIABLE + * TYPE_PARAMETER + * UNKNOWN + * UNIT_TEST_GROUP + * UNIT_TEST_TEST + * } + */ +final Matcher isElementKind = isIn([ + "CLASS", + "CLASS_TYPE_ALIAS", + "COMPILATION_UNIT", + "CONSTRUCTOR", + "GETTER", + "FIELD", + "FUNCTION", + "FUNCTION_TYPE_ALIAS", + "LIBRARY", + "LOCAL_VARIABLE", + "METHOD", + "SETTER", + "TOP_LEVEL_VARIABLE", + "TYPE_PARAMETER", + "UNKNOWN", + "UNIT_TEST_GROUP", + "UNIT_TEST_TEST" +]); + +/** + * Error + * + * { + * "code": String + * "message": String + * "data": optional object + * } + */ +final Matcher isError = new MatchesJsonObject( + "Error", { + // TODO(paulberry): the API spec says "code" should be a string, but the + // server is currently outputting integers. + "code": isInt, + "message": isString + }, optionalFields: { + "data": isObject + }); + +/** + * ErrorFixes + * + * { + * "error": AnalysisError + * "fixes": List + * } + */ +final Matcher isErrorFixes = new MatchesJsonObject( + "ErrorFixes", { + "error": isAnalysisError, + "fixes": isListOf(isSourceChange) + }); + +/** + * ErrorSeverity + * + * enum { + * INFO + * WARNING + * ERROR + * } + */ +final Matcher isErrorSeverity = isIn([ + "INFO", + "WARNING", + "ERROR" +]); + +/** + * ErrorType + * + * enum { + * COMPILE_TIME_ERROR + * HINT + * STATIC_TYPE_WARNING + * STATIC_WARNING + * SYNTACTIC_ERROR + * TODO + * } + */ +final Matcher isErrorType = isIn([ + "COMPILE_TIME_ERROR", + "HINT", + "STATIC_TYPE_WARNING", + "STATIC_WARNING", + "SYNTACTIC_ERROR", + "TODO" +]); + +/** + * ExecutableFile + * + * { + * "file": FilePath + * "offset": ExecutableKind + * } + */ +final Matcher isExecutableFile = new MatchesJsonObject( + "ExecutableFile", { + "file": isFilePath, + "offset": isExecutableKind + }); + +/** + * ExecutableKind + * + * enum { + * CLIENT + * EITHER + * SERVER + * } + */ +final Matcher isExecutableKind = isIn([ + "CLIENT", + "EITHER", + "SERVER" +]); + +/** + * FilePath + * + * String + */ +final Matcher isFilePath = isString; + +/** + * FoldingKind + * + * enum { + * COMMENT + * CLASS_MEMBER + * DIRECTIVES + * DOCUMENTATION_COMMENT + * TOP_LEVEL_DECLARATION + * } + */ +final Matcher isFoldingKind = isIn([ + "COMMENT", + "CLASS_MEMBER", + "DIRECTIVES", + "DOCUMENTATION_COMMENT", + "TOP_LEVEL_DECLARATION" +]); + +/** + * FoldingRegion + * + * { + * "kind": FoldingKind + * "offset": int + * "length": int + * } + */ +final Matcher isFoldingRegion = new MatchesJsonObject( + "FoldingRegion", { + "kind": isFoldingKind, + "offset": isInt, + "length": isInt + }); + +/** + * HighlightRegion + * + * { + * "type": HighlightRegionType + * "offset": int + * "length": int + * } + */ +final Matcher isHighlightRegion = new MatchesJsonObject( + "HighlightRegion", { + "type": isHighlightRegionType, + "offset": isInt, + "length": isInt + }); + +/** + * HighlightRegionType + * + * enum { + * ANNOTATION + * BUILT_IN + * CLASS + * COMMENT_BLOCK + * COMMENT_DOCUMENTATION + * COMMENT_END_OF_LINE + * CONSTRUCTOR + * DIRECTIVE + * DYNAMIC_TYPE + * FIELD + * FIELD_STATIC + * FUNCTION_DECLARATION + * FUNCTION + * FUNCTION_TYPE_ALIAS + * GETTER_DECLARATION + * KEYWORD + * IDENTIFIER_DEFAULT + * IMPORT_PREFIX + * LITERAL_BOOLEAN + * LITERAL_DOUBLE + * LITERAL_INTEGER + * LITERAL_LIST + * LITERAL_MAP + * LITERAL_STRING + * LOCAL_VARIABLE_DECLARATION + * LOCAL_VARIABLE + * METHOD_DECLARATION + * METHOD_DECLARATION_STATIC + * METHOD + * METHOD_STATIC + * PARAMETER + * SETTER_DECLARATION + * TOP_LEVEL_VARIABLE + * TYPE_NAME_DYNAMIC + * TYPE_PARAMETER + * } + */ +final Matcher isHighlightRegionType = isIn([ + "ANNOTATION", + "BUILT_IN", + "CLASS", + "COMMENT_BLOCK", + "COMMENT_DOCUMENTATION", + "COMMENT_END_OF_LINE", + "CONSTRUCTOR", + "DIRECTIVE", + "DYNAMIC_TYPE", + "FIELD", + "FIELD_STATIC", + "FUNCTION_DECLARATION", + "FUNCTION", + "FUNCTION_TYPE_ALIAS", + "GETTER_DECLARATION", + "KEYWORD", + "IDENTIFIER_DEFAULT", + "IMPORT_PREFIX", + "LITERAL_BOOLEAN", + "LITERAL_DOUBLE", + "LITERAL_INTEGER", + "LITERAL_LIST", + "LITERAL_MAP", + "LITERAL_STRING", + "LOCAL_VARIABLE_DECLARATION", + "LOCAL_VARIABLE", + "METHOD_DECLARATION", + "METHOD_DECLARATION_STATIC", + "METHOD", + "METHOD_STATIC", + "PARAMETER", + "SETTER_DECLARATION", + "TOP_LEVEL_VARIABLE", + "TYPE_NAME_DYNAMIC", + "TYPE_PARAMETER" +]); + +/** + * HoverInformation + * + * { + * "offset": int + * "length": int + * "containingLibraryPath": optional String + * "containingLibraryName": optional String + * "dartdoc": optional String + * "elementDescription": optional String + * "elementKind": optional String + * "parameter": optional String + * "propagatedType": optional String + * "staticType": optional String + * } + */ +final Matcher isHoverInformation = new MatchesJsonObject( + "HoverInformation", { + "offset": isInt, + "length": isInt + }, optionalFields: { + "containingLibraryPath": isString, + "containingLibraryName": isString, + "dartdoc": isString, + "elementDescription": isString, + "elementKind": isString, + "parameter": isString, + "propagatedType": isString, + "staticType": isString + }); + +/** + * Location + * + * { + * "file": FilePath + * "offset": int + * "length": int + * "startLine": int + * "startColumn": int + * } + */ +final Matcher isLocation = new MatchesJsonObject( + "Location", { + "file": isFilePath, + "offset": isInt, + "length": isInt, + "startLine": isInt, + "startColumn": isInt + }); + +/** + * NavigationRegion + * + * { + * "offset": int + * "length": int + * "targets": List + * } + */ +final Matcher isNavigationRegion = new MatchesJsonObject( + "NavigationRegion", { + "offset": isInt, + "length": isInt, + "targets": isListOf(isElement) + }); + +/** + * Occurrences + * + * { + * "element": Element + * "offsets": List + * "length": int + * } + */ +final Matcher isOccurrences = new MatchesJsonObject( + "Occurrences", { + "element": isElement, + "offsets": isListOf(isInt), + "length": isInt + }); + +/** + * Outline + * + * { + * "element": Element + * "offset": int + * "length": int + * "children": optional List + * } + */ +final Matcher isOutline = new MatchesJsonObject( + "Outline", { + "element": isElement, + "offset": isInt, + "length": isInt + }, optionalFields: { + "children": isListOf(isOutline) + }); + +/** + * Override + * + * { + * "offset": int + * "length": int + * "superclassMember": optional OverriddenMember + * "interfaceMembers": optional List + * } + */ +final Matcher isOverride = new MatchesJsonObject( + "Override", { + "offset": isInt, + "length": isInt + }, optionalFields: { + "superclassMember": isOverriddenMember, + "interfaceMembers": isListOf(isOverriddenMember) + }); + +/** + * OverriddenMember + * + * { + * "element": Element + * "className": String + * } + */ +final Matcher isOverriddenMember = new MatchesJsonObject( + "OverriddenMember", { + "element": isElement, + "className": isString + }); + +/** + * Parameter + * + * { + * "type": String + * "name": String + * } + */ +final Matcher isParameter = new MatchesJsonObject( + "Parameter", { + "type": isString, + "name": isString + }); + +/** + * RefactoringId + * + * String + */ +final Matcher isRefactoringId = isString; + +/** + * RefactoringKind + * + * enum { + * CONVERT_GETTER_TO_METHOD + * CONVERT_METHOD_TO_GETTER + * EXTRACT_LOCAL_VARIABLE + * EXTRACT_METHOD + * INLINE_LOCAL_VARIABLE + * INLINE_METHOD + * RENAME + * } + */ +final Matcher isRefactoringKind = isIn([ + "CONVERT_GETTER_TO_METHOD", + "CONVERT_METHOD_TO_GETTER", + "EXTRACT_LOCAL_VARIABLE", + "EXTRACT_METHOD", + "INLINE_LOCAL_VARIABLE", + "INLINE_METHOD", + "RENAME" +]); + +/** + * RefactoringProblem + * + * { + * "severity": RefactoringProblemSeverity + * "message": String + * "location": Location + * } + */ +final Matcher isRefactoringProblem = new MatchesJsonObject( + "RefactoringProblem", { + "severity": isRefactoringProblemSeverity, + "message": isString, + "location": isLocation + }); + +/** + * RefactoringProblemSeverity + * + * enum { + * INFO + * WARNING + * ERROR + * FATAL + * } + */ +final Matcher isRefactoringProblemSeverity = isIn([ + "INFO", + "WARNING", + "ERROR", + "FATAL" +]); + +/** + * SearchId + * + * String + */ +final Matcher isSearchId = isString; + +/** + * SearchResult + * + * { + * "location": Location + * "kind": SearchResultKind + * "isPotential": bool + * "path": List + * } + */ +final Matcher isSearchResult = new MatchesJsonObject( + "SearchResult", { + "location": isLocation, + "kind": isSearchResultKind, + "isPotential": isBool, + "path": isListOf(isElement) + }); + +/** + * SearchResultKind + * + * enum { + * DECLARATION + * INVOCATION + * READ + * READ_WRITE + * REFERENCE + * WRITE + * } + */ +final Matcher isSearchResultKind = isIn([ + "DECLARATION", + "INVOCATION", + "READ", + "READ_WRITE", + "REFERENCE", + "WRITE" +]); + +/** + * ServerService + * + * enum { + * STATUS + * } + */ +final Matcher isServerService = isIn([ + "STATUS" +]); + +/** + * SourceChange + * + * { + * "message": String + * "edits": List + * } + */ +final Matcher isSourceChange = new MatchesJsonObject( + "SourceChange", { + "message": isString, + "edits": isListOf(isSourceFileEdit) + }); + +/** + * SourceEdit + * + * { + * "offset": int + * "length": int + * "replacement": String + * } + */ +final Matcher isSourceEdit = new MatchesJsonObject( + "SourceEdit", { + "offset": isInt, + "length": isInt, + "replacement": isString + }); + +/** + * SourceFileEdit + * + * { + * "file": FilePath + * "edits": List + * } + */ +final Matcher isSourceFileEdit = new MatchesJsonObject( + "SourceFileEdit", { + "file": isFilePath, + "edits": isListOf(isSourceEdit) + }); + +/** + * TypeHierarchyItem + * + * { + * "classElement": Element + * "displayName": optional String + * "memberElement": optional Element + * "superclass": optional TypeHierarchyItem + * "interfaces": List + * "mixins": List + * "subclasses": List + * } + */ +final Matcher isTypeHierarchyItem = new MatchesJsonObject( + "TypeHierarchyItem", { + "classElement": isElement, + "interfaces": isListOf(isTypeHierarchyItem), + "mixins": isListOf(isTypeHierarchyItem), + "subclasses": isListOf(isTypeHierarchyItem) + }, optionalFields: { + "displayName": isString, + "memberElement": isElement, + "superclass": isTypeHierarchyItem + }); + diff --git a/pkg/analysis_server/test/integration/server_domain_int_test.dart b/pkg/analysis_server/test/integration/server_domain_int_test.dart index c5779422d58..90cd81998c3 100644 --- a/pkg/analysis_server/test/integration/server_domain_int_test.dart +++ b/pkg/analysis_server/test/integration/server_domain_int_test.dart @@ -11,6 +11,7 @@ import 'package:analysis_testing/reflective_tests.dart'; import 'package:unittest/unittest.dart'; import 'integration_tests.dart'; +import 'protocol_matchers.dart'; @ReflectiveTestCase() class ServerDomainIntegrationTest extends AbstractAnalysisServerIntegrationTest