Index: packages/analyzer/test/generated/test_support.dart |
diff --git a/packages/analyzer/test/generated/test_support.dart b/packages/analyzer/test/generated/test_support.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..20cc0e2633937cf63bf8b7beb51ac0a9090b6df7 |
--- /dev/null |
+++ b/packages/analyzer/test/generated/test_support.dart |
@@ -0,0 +1,662 @@ |
+// 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. |
+ |
+library engine.test_support; |
+ |
+import 'dart:collection'; |
+ |
+import 'package:analyzer/src/generated/ast.dart' show AstNode, NodeLocator; |
+import 'package:analyzer/src/generated/element.dart'; |
+import 'package:analyzer/src/generated/engine.dart'; |
+import 'package:analyzer/src/generated/error.dart'; |
+import 'package:analyzer/src/generated/java_core.dart'; |
+import 'package:analyzer/src/generated/java_engine.dart'; |
+import 'package:analyzer/src/generated/source.dart'; |
+import 'package:unittest/unittest.dart'; |
+ |
+import 'resolver_test.dart'; |
+ |
+/** |
+ * The class `EngineTestCase` defines utility methods for making assertions. |
+ */ |
+class EngineTestCase { |
+ /** |
+ * Assert that the given collection has the same number of elements as the number of specified |
+ * names, and that for each specified name, a corresponding element can be found in the given |
+ * collection with that name. |
+ * |
+ * @param elements the elements |
+ * @param names the names |
+ */ |
+ void assertNamedElements(List<Element> elements, List<String> names) { |
+ for (String elemName in names) { |
+ bool found = false; |
+ for (Element elem in elements) { |
+ if (elem.name == elemName) { |
+ found = true; |
+ break; |
+ } |
+ } |
+ if (!found) { |
+ StringBuffer buffer = new StringBuffer(); |
+ buffer.write("Expected element named: "); |
+ buffer.write(elemName); |
+ buffer.write("\n but found: "); |
+ for (Element elem in elements) { |
+ buffer.write(elem.name); |
+ buffer.write(", "); |
+ } |
+ fail(buffer.toString()); |
+ } |
+ } |
+ expect(elements, hasLength(names.length)); |
+ } |
+ |
+ AnalysisContext createAnalysisContext() { |
+ return AnalysisContextFactory.contextWithCore(); |
+ } |
+ |
+ /** |
+ * Return the getter in the given type with the given name. Inherited getters are ignored. |
+ * |
+ * @param type the type in which the getter is declared |
+ * @param getterName the name of the getter to be returned |
+ * @return the property accessor element representing the getter with the given name |
+ */ |
+ PropertyAccessorElement getGetter(InterfaceType type, String getterName) { |
+ for (PropertyAccessorElement accessor in type.element.accessors) { |
+ if (accessor.isGetter && accessor.name == getterName) { |
+ return accessor; |
+ } |
+ } |
+ fail("Could not find getter named $getterName in ${type.displayName}"); |
+ return null; |
+ } |
+ |
+ /** |
+ * Return the method in the given type with the given name. Inherited methods are ignored. |
+ * |
+ * @param type the type in which the method is declared |
+ * @param methodName the name of the method to be returned |
+ * @return the method element representing the method with the given name |
+ */ |
+ MethodElement getMethod(InterfaceType type, String methodName) { |
+ for (MethodElement method in type.element.methods) { |
+ if (method.name == methodName) { |
+ return method; |
+ } |
+ } |
+ fail("Could not find method named $methodName in ${type.displayName}"); |
+ return null; |
+ } |
+ |
+ void setUp() {} |
+ |
+ void tearDown() {} |
+ |
+ /** |
+ * Assert that the given object is an instance of the expected class. |
+ * |
+ * @param expectedClass the class that the object is expected to be an instance of |
+ * @param object the object being tested |
+ * @return the object that was being tested |
+ * @throws Exception if the object is not an instance of the expected class |
+ */ |
+ static Object assertInstanceOf( |
+ Predicate<Object> predicate, Type expectedClass, Object object) { |
+ if (!predicate(object)) { |
+ fail( |
+ "Expected instance of $expectedClass, found ${object == null ? "null" : object.runtimeType}"); |
+ } |
+ return object; |
+ } |
+ |
+ /** |
+ * @return the [AstNode] with requested type at offset of the "prefix". |
+ */ |
+ static AstNode findNode( |
+ AstNode root, String code, String prefix, Predicate<AstNode> predicate) { |
+ int offset = code.indexOf(prefix); |
+ if (offset == -1) { |
+ throw new IllegalArgumentException("Not found '$prefix'."); |
+ } |
+ AstNode node = new NodeLocator(offset).searchWithin(root); |
+ return node.getAncestor(predicate); |
+ } |
+} |
+ |
+/** |
+ * Instances of the class `GatheringErrorListener` implement an error listener that collects |
+ * all of the errors passed to it for later examination. |
+ */ |
+class GatheringErrorListener implements AnalysisErrorListener { |
+ /** |
+ * An empty array of errors used when no errors are expected. |
+ */ |
+ static List<AnalysisError> _NO_ERRORS = new List<AnalysisError>(0); |
+ |
+ /** |
+ * A list containing the errors that were collected. |
+ */ |
+ List<AnalysisError> _errors = new List<AnalysisError>(); |
+ |
+ /** |
+ * A table mapping sources to the line information for the source. |
+ */ |
+ HashMap<Source, LineInfo> _lineInfoMap = new HashMap<Source, LineInfo>(); |
+ |
+ /** |
+ * Initialize a newly created error listener to collect errors. |
+ */ |
+ GatheringErrorListener(); |
+ |
+ /** |
+ * Return the errors that were collected. |
+ * |
+ * @return the errors that were collected |
+ */ |
+ List<AnalysisError> get errors => _errors; |
+ |
+ /** |
+ * Return `true` if at least one error has been gathered. |
+ * |
+ * @return `true` if at least one error has been gathered |
+ */ |
+ bool get hasErrors => _errors.length > 0; |
+ |
+ /** |
+ * Add all of the given errors to this listener. |
+ * |
+ * @param the errors to be added |
+ */ |
+ void addAll(List<AnalysisError> errors) { |
+ for (AnalysisError error in errors) { |
+ onError(error); |
+ } |
+ } |
+ |
+ /** |
+ * Add all of the errors recorded by the given listener to this listener. |
+ * |
+ * @param listener the listener that has recorded the errors to be added |
+ */ |
+ void addAll2(RecordingErrorListener listener) { |
+ addAll(listener.errors); |
+ } |
+ |
+ /** |
+ * Assert that the number of errors that have been gathered matches the number of errors that are |
+ * given and that they have the expected error codes and locations. The order in which the errors |
+ * were gathered is ignored. |
+ * |
+ * @param errorCodes the errors that should have been gathered |
+ * @throws AssertionFailedError if a different number of errors have been gathered than were |
+ * expected or if they do not have the same codes and locations |
+ */ |
+ void assertErrors(List<AnalysisError> expectedErrors) { |
+ if (_errors.length != expectedErrors.length) { |
+ _fail(expectedErrors); |
+ } |
+ List<AnalysisError> remainingErrors = new List<AnalysisError>(); |
+ for (AnalysisError error in expectedErrors) { |
+ remainingErrors.add(error); |
+ } |
+ for (AnalysisError error in _errors) { |
+ if (!_foundAndRemoved(remainingErrors, error)) { |
+ _fail(expectedErrors); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Assert that the number of errors that have been gathered matches the number of errors that are |
+ * given and that they have the expected error codes. The order in which the errors were gathered |
+ * is ignored. |
+ * |
+ * @param expectedErrorCodes the error codes of the errors that should have been gathered |
+ * @throws AssertionFailedError if a different number of errors have been gathered than were |
+ * expected |
+ */ |
+ void assertErrorsWithCodes( |
+ [List<ErrorCode> expectedErrorCodes = ErrorCode.EMPTY_LIST]) { |
+ StringBuffer buffer = new StringBuffer(); |
+ // |
+ // Verify that the expected error codes have a non-empty message. |
+ // |
+ for (ErrorCode errorCode in expectedErrorCodes) { |
+ expect(errorCode.message.isEmpty, isFalse, |
+ reason: "Empty error code message"); |
+ } |
+ // |
+ // Compute the expected number of each type of error. |
+ // |
+ HashMap<ErrorCode, int> expectedCounts = new HashMap<ErrorCode, int>(); |
+ for (ErrorCode code in expectedErrorCodes) { |
+ int count = expectedCounts[code]; |
+ if (count == null) { |
+ count = 1; |
+ } else { |
+ count = count + 1; |
+ } |
+ expectedCounts[code] = count; |
+ } |
+ // |
+ // Compute the actual number of each type of error. |
+ // |
+ HashMap<ErrorCode, List<AnalysisError>> errorsByCode = |
+ new HashMap<ErrorCode, List<AnalysisError>>(); |
+ for (AnalysisError error in _errors) { |
+ ErrorCode code = error.errorCode; |
+ List<AnalysisError> list = errorsByCode[code]; |
+ if (list == null) { |
+ list = new List<AnalysisError>(); |
+ errorsByCode[code] = list; |
+ } |
+ list.add(error); |
+ } |
+ // |
+ // Compare the expected and actual number of each type of error. |
+ // |
+ expectedCounts.forEach((ErrorCode code, int expectedCount) { |
+ int actualCount; |
+ List<AnalysisError> list = errorsByCode.remove(code); |
+ if (list == null) { |
+ actualCount = 0; |
+ } else { |
+ actualCount = list.length; |
+ } |
+ if (actualCount != expectedCount) { |
+ if (buffer.length == 0) { |
+ buffer.write("Expected "); |
+ } else { |
+ buffer.write("; "); |
+ } |
+ buffer.write(expectedCount); |
+ buffer.write(" errors of type "); |
+ buffer.write(code.uniqueName); |
+ buffer.write(", found "); |
+ buffer.write(actualCount); |
+ } |
+ }); |
+ // |
+ // Check that there are no more errors in the actual-errors map, |
+ // otherwise record message. |
+ // |
+ errorsByCode.forEach((ErrorCode code, List<AnalysisError> actualErrors) { |
+ int actualCount = actualErrors.length; |
+ if (buffer.length == 0) { |
+ buffer.write("Expected "); |
+ } else { |
+ buffer.write("; "); |
+ } |
+ buffer.write("0 errors of type "); |
+ buffer.write(code.uniqueName); |
+ buffer.write(", found "); |
+ buffer.write(actualCount); |
+ buffer.write(" ("); |
+ for (int i = 0; i < actualErrors.length; i++) { |
+ AnalysisError error = actualErrors[i]; |
+ if (i > 0) { |
+ buffer.write(", "); |
+ } |
+ buffer.write(error.offset); |
+ } |
+ buffer.write(")"); |
+ }); |
+ if (buffer.length > 0) { |
+ fail(buffer.toString()); |
+ } |
+ } |
+ |
+ /** |
+ * Assert that the number of errors that have been gathered matches the number of severities that |
+ * are given and that there are the same number of errors and warnings as specified by the |
+ * argument. The order in which the errors were gathered is ignored. |
+ * |
+ * @param expectedSeverities the severities of the errors that should have been gathered |
+ * @throws AssertionFailedError if a different number of errors have been gathered than were |
+ * expected |
+ */ |
+ void assertErrorsWithSeverities(List<ErrorSeverity> expectedSeverities) { |
+ int expectedErrorCount = 0; |
+ int expectedWarningCount = 0; |
+ for (ErrorSeverity severity in expectedSeverities) { |
+ if (severity == ErrorSeverity.ERROR) { |
+ expectedErrorCount++; |
+ } else { |
+ expectedWarningCount++; |
+ } |
+ } |
+ int actualErrorCount = 0; |
+ int actualWarningCount = 0; |
+ for (AnalysisError error in _errors) { |
+ if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) { |
+ actualErrorCount++; |
+ } else { |
+ actualWarningCount++; |
+ } |
+ } |
+ if (expectedErrorCount != actualErrorCount || |
+ expectedWarningCount != actualWarningCount) { |
+ fail( |
+ "Expected $expectedErrorCount errors and $expectedWarningCount warnings, found $actualErrorCount errors and $actualWarningCount warnings"); |
+ } |
+ } |
+ |
+ /** |
+ * Assert that no errors have been gathered. |
+ * |
+ * @throws AssertionFailedError if any errors have been gathered |
+ */ |
+ void assertNoErrors() { |
+ assertErrors(_NO_ERRORS); |
+ } |
+ |
+ /** |
+ * Return the line information associated with the given source, or `null` if no line |
+ * information has been associated with the source. |
+ * |
+ * @param source the source with which the line information is associated |
+ * @return the line information associated with the source |
+ */ |
+ LineInfo getLineInfo(Source source) => _lineInfoMap[source]; |
+ |
+ /** |
+ * Return `true` if an error with the given error code has been gathered. |
+ * |
+ * @param errorCode the error code being searched for |
+ * @return `true` if an error with the given error code has been gathered |
+ */ |
+ bool hasError(ErrorCode errorCode) { |
+ for (AnalysisError error in _errors) { |
+ if (identical(error.errorCode, errorCode)) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ @override |
+ void onError(AnalysisError error) { |
+ _errors.add(error); |
+ } |
+ |
+ /** |
+ * Set the line information associated with the given source to the given information. |
+ * |
+ * @param source the source with which the line information is associated |
+ * @param lineStarts the line start information to be associated with the source |
+ */ |
+ void setLineInfo(Source source, List<int> lineStarts) { |
+ _lineInfoMap[source] = new LineInfo(lineStarts); |
+ } |
+ |
+ /** |
+ * Return `true` if the two errors are equivalent. |
+ * |
+ * @param firstError the first error being compared |
+ * @param secondError the second error being compared |
+ * @return `true` if the two errors are equivalent |
+ */ |
+ bool _equalErrors(AnalysisError firstError, AnalysisError secondError) => |
+ identical(firstError.errorCode, secondError.errorCode) && |
+ firstError.offset == secondError.offset && |
+ firstError.length == secondError.length && |
+ _equalSources(firstError.source, secondError.source); |
+ |
+ /** |
+ * Return `true` if the two sources are equivalent. |
+ * |
+ * @param firstSource the first source being compared |
+ * @param secondSource the second source being compared |
+ * @return `true` if the two sources are equivalent |
+ */ |
+ bool _equalSources(Source firstSource, Source secondSource) { |
+ if (firstSource == null) { |
+ return secondSource == null; |
+ } else if (secondSource == null) { |
+ return false; |
+ } |
+ return firstSource == secondSource; |
+ } |
+ |
+ /** |
+ * Assert that the number of errors that have been gathered matches the number of errors that are |
+ * given and that they have the expected error codes. The order in which the errors were gathered |
+ * is ignored. |
+ * |
+ * @param errorCodes the errors that should have been gathered |
+ * @throws AssertionFailedError with |
+ */ |
+ void _fail(List<AnalysisError> expectedErrors) { |
+ StringBuffer buffer = new StringBuffer(); |
+ buffer.write("Expected "); |
+ buffer.write(expectedErrors.length); |
+ buffer.write(" errors:"); |
+ for (AnalysisError error in expectedErrors) { |
+ Source source = error.source; |
+ LineInfo lineInfo = _lineInfoMap[source]; |
+ buffer.writeln(); |
+ if (lineInfo == null) { |
+ int offset = error.offset; |
+ StringUtils.printf(buffer, " %s %s (%d..%d)", [ |
+ source == null ? "" : source.shortName, |
+ error.errorCode, |
+ offset, |
+ offset + error.length |
+ ]); |
+ } else { |
+ LineInfo_Location location = lineInfo.getLocation(error.offset); |
+ StringUtils.printf(buffer, " %s %s (%d, %d/%d)", [ |
+ source == null ? "" : source.shortName, |
+ error.errorCode, |
+ location.lineNumber, |
+ location.columnNumber, |
+ error.length |
+ ]); |
+ } |
+ } |
+ buffer.writeln(); |
+ buffer.write("found "); |
+ buffer.write(_errors.length); |
+ buffer.write(" errors:"); |
+ for (AnalysisError error in _errors) { |
+ Source source = error.source; |
+ LineInfo lineInfo = _lineInfoMap[source]; |
+ buffer.writeln(); |
+ if (lineInfo == null) { |
+ int offset = error.offset; |
+ StringUtils.printf(buffer, " %s %s (%d..%d): %s", [ |
+ source == null ? "" : source.shortName, |
+ error.errorCode, |
+ offset, |
+ offset + error.length, |
+ error.message |
+ ]); |
+ } else { |
+ LineInfo_Location location = lineInfo.getLocation(error.offset); |
+ StringUtils.printf(buffer, " %s %s (%d, %d/%d): %s", [ |
+ source == null ? "" : source.shortName, |
+ error.errorCode, |
+ location.lineNumber, |
+ location.columnNumber, |
+ error.length, |
+ error.message |
+ ]); |
+ } |
+ } |
+ fail(buffer.toString()); |
+ } |
+ |
+ /** |
+ * Search through the given list of errors for an error that is equal to the target error. If one |
+ * is found, remove it from the list and return `true`, otherwise return `false` |
+ * without modifying the list. |
+ * |
+ * @param errors the errors through which we are searching |
+ * @param targetError the error being searched for |
+ * @return `true` if the error is found and removed from the list |
+ */ |
+ bool _foundAndRemoved(List<AnalysisError> errors, AnalysisError targetError) { |
+ for (AnalysisError error in errors) { |
+ if (_equalErrors(error, targetError)) { |
+ errors.remove(error); |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+} |
+ |
+/** |
+ * Instances of the class [TestLogger] implement a logger that can be used by |
+ * tests. |
+ */ |
+class TestLogger implements Logger { |
+ /** |
+ * The number of error messages that were logged. |
+ */ |
+ int errorCount = 0; |
+ |
+ /** |
+ * The number of informational messages that were logged. |
+ */ |
+ int infoCount = 0; |
+ |
+ @override |
+ void logError(String message, [CaughtException exception]) { |
+ errorCount++; |
+ } |
+ |
+ @override |
+ void logError2(String message, Object exception) { |
+ errorCount++; |
+ } |
+ |
+ @override |
+ void logInformation(String message, [CaughtException exception]) { |
+ infoCount++; |
+ } |
+ |
+ @override |
+ void logInformation2(String message, Object exception) { |
+ infoCount++; |
+ } |
+} |
+ |
+class TestSource extends Source { |
+ String _name; |
+ String _contents; |
+ int _modificationStamp = 0; |
+ bool exists2 = true; |
+ |
+ /** |
+ * A flag indicating whether an exception should be generated when an attempt |
+ * is made to access the contents of this source. |
+ */ |
+ bool generateExceptionOnRead = false; |
+ |
+ @override |
+ int get modificationStamp => |
+ generateExceptionOnRead ? -1 : _modificationStamp; |
+ |
+ /** |
+ * The number of times that the contents of this source have been requested. |
+ */ |
+ int readCount = 0; |
+ |
+ TestSource([this._name = '/test.dart', this._contents]); |
+ |
+ TimestampedData<String> get contents { |
+ readCount++; |
+ if (generateExceptionOnRead) { |
+ String msg = "I/O Exception while getting the contents of " + _name; |
+ throw new Exception(msg); |
+ } |
+ return new TimestampedData<String>(0, _contents); |
+ } |
+ |
+ String get encoding { |
+ throw new UnsupportedOperationException(); |
+ } |
+ |
+ String get fullName { |
+ return _name; |
+ } |
+ |
+ int get hashCode => 0; |
+ bool get isInSystemLibrary { |
+ return false; |
+ } |
+ |
+ String get shortName { |
+ return _name; |
+ } |
+ |
+ Uri get uri { |
+ throw new UnsupportedOperationException(); |
+ } |
+ |
+ UriKind get uriKind { |
+ throw new UnsupportedOperationException(); |
+ } |
+ |
+ bool operator ==(Object other) { |
+ if (other is TestSource) { |
+ return other._name == _name; |
+ } |
+ return false; |
+ } |
+ |
+ bool exists() => exists2; |
+ void getContentsToReceiver(Source_ContentReceiver receiver) { |
+ throw new UnsupportedOperationException(); |
+ } |
+ |
+ Source resolve(String uri) { |
+ throw new UnsupportedOperationException(); |
+ } |
+ |
+ Uri resolveRelativeUri(Uri uri) { |
+ return new Uri(scheme: 'file', path: _name).resolveUri(uri); |
+ } |
+ |
+ void setContents(String value) { |
+ generateExceptionOnRead = false; |
+ _modificationStamp = new DateTime.now().millisecondsSinceEpoch; |
+ _contents = value; |
+ } |
+ |
+ @override |
+ String toString() => '$_name'; |
+} |
+ |
+class TestSourceWithUri extends TestSource { |
+ final Uri uri; |
+ |
+ TestSourceWithUri(String path, this.uri, [String content]) |
+ : super(path, content); |
+ |
+ UriKind get uriKind { |
+ if (uri == null) { |
+ return UriKind.FILE_URI; |
+ } else if (uri.scheme == 'dart') { |
+ return UriKind.DART_URI; |
+ } else if (uri.scheme == 'package') { |
+ return UriKind.PACKAGE_URI; |
+ } |
+ return UriKind.FILE_URI; |
+ } |
+ |
+ bool operator ==(Object other) { |
+ if (other is TestSource) { |
+ return other.uri == uri; |
+ } |
+ return false; |
+ } |
+ |
+ Uri resolveRelativeUri(Uri uri) { |
+ return this.uri.resolveUri(uri); |
+ } |
+} |