| /* |
| * Copyright (c) 2013, the Dart project authors. |
| * |
| * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except |
| * in compliance with the License. You may obtain a copy of the License at |
| * |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.dart.engine.integration; |
| |
| import com.google.dart.engine.ast.CompilationUnit; |
| import com.google.dart.engine.context.AnalysisContext; |
| import com.google.dart.engine.context.AnalysisException; |
| import com.google.dart.engine.element.CompilationUnitElement; |
| import com.google.dart.engine.element.LibraryElement; |
| import com.google.dart.engine.error.AnalysisError; |
| import com.google.dart.engine.error.ErrorCode; |
| import com.google.dart.engine.error.TodoCode; |
| import com.google.dart.engine.internal.context.PerformanceStatistics; |
| import com.google.dart.engine.internal.resolver.ResolutionVerifier; |
| import com.google.dart.engine.internal.resolver.StaticTypeVerifier; |
| import com.google.dart.engine.source.Source; |
| import com.google.dart.engine.utilities.io.PrintStringWriter; |
| |
| import junit.framework.Assert; |
| import junit.framework.TestCase; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| |
| /** |
| * The abstract class {@code LibraryAnalysisTest} defines utility methods useful for integration |
| * tests that analyze one or more libraries as a single test. |
| */ |
| public abstract class LibraryAnalysisTest extends TestCase { |
| /** |
| * A set containing all of the libraries that have been visited as part of verifying the results |
| * of analysis. |
| */ |
| private HashSet<LibraryElement> visitedLibraries = new HashSet<LibraryElement>(); |
| |
| /** |
| * A flag indicating whether libraries that are part of the SDK should be validated. |
| */ |
| private boolean validateSdk = false; |
| |
| /** |
| * A list to which all of the errors in the libraries will be added. |
| */ |
| private ArrayList<AnalysisError> errorList = new ArrayList<AnalysisError>(); |
| |
| /** |
| * The object used to verify that everything that ought to have a static type associated with it |
| * has a static type. |
| */ |
| private StaticTypeVerifier staticTypeVerifier = new StaticTypeVerifier(); |
| |
| /** |
| * The object used to verify that everything that ought to have been resolved was resolved. |
| */ |
| private ResolutionVerifier resolutionVerifier = new ResolutionVerifier(); |
| |
| /** |
| * The object used to verify that the element structures of the libraries were correctly formed. |
| */ |
| private ElementStructureVerifier elementVerifier = new ElementStructureVerifier(); |
| |
| /** |
| * The object used to verify that the containing libraries of the sources were correctly computed. |
| */ |
| private ContainingLibrariesVerifier librariesVerifier = new ContainingLibrariesVerifier(); |
| |
| /** |
| * Assert that the results of analyzing the libraries (as computed by {#link |
| * {@link #verify(LibraryElement)}) were as expected. |
| * |
| * @param context the analysis context used to get additional information |
| */ |
| protected void assertValid(AnalysisContext context) { |
| assertErrors(); |
| elementVerifier.assertValid(); |
| staticTypeVerifier.assertResolved(); |
| resolutionVerifier.assertResolved(); |
| librariesVerifier.assertValid(context); |
| } |
| |
| protected void printStatistics() { |
| System.out.print(" scan: "); |
| printTime(PerformanceStatistics.scan.getResult()); |
| System.out.println(); |
| |
| System.out.print(" parse: "); |
| printTime(PerformanceStatistics.parse.getResult()); |
| System.out.println(); |
| |
| System.out.print(" resolve: "); |
| printTime(PerformanceStatistics.resolve.getResult()); |
| System.out.println(); |
| |
| System.out.print(" errors: "); |
| printTime(PerformanceStatistics.errors.getResult()); |
| System.out.println(); |
| |
| System.out.print(" hints: "); |
| printTime(PerformanceStatistics.hints.getResult()); |
| System.out.println(); |
| } |
| |
| /** |
| * Mark the given library as one that should be ignored when validating the libraries that were |
| * analyzed. |
| * |
| * @param library the library to be ignored |
| */ |
| protected void validateSdk() { |
| validateSdk = true; |
| } |
| |
| /** |
| * Add all of the errors in the given library and all referenced libraries to the list of errors. |
| * |
| * @param library the library whose errors are to be added |
| * @throws AnalysisException if the errors could not be determined |
| */ |
| protected void verify(LibraryElement library) throws AnalysisException { |
| if (library == null || visitedLibraries.contains(library) |
| || (!validateSdk && library.getSource().isInSystemLibrary())) { |
| return; |
| } |
| visitedLibraries.add(library); |
| library.accept(elementVerifier); |
| CompilationUnitElement definingUnit = library.getDefiningCompilationUnit(); |
| verify(definingUnit); |
| librariesVerifier.addLibrary(definingUnit); |
| for (CompilationUnitElement part : library.getParts()) { |
| verify(part); |
| librariesVerifier.addPart(part, definingUnit); |
| } |
| for (LibraryElement importedLibrary : library.getImportedLibraries()) { |
| verify(importedLibrary); |
| } |
| for (LibraryElement exportedLibrary : library.getExportedLibraries()) { |
| verify(exportedLibrary); |
| } |
| } |
| |
| /** |
| * Assert that the errors that were reported match the expected behavior of the test. |
| */ |
| private void assertErrors() { |
| int size = errorList.size(); |
| if (size > 0) { |
| @SuppressWarnings("resource") |
| PrintStringWriter writer = new PrintStringWriter(); |
| writer.print("Expected 0 errors, found "); |
| writer.print(errorList.size()); |
| writer.print(":"); |
| int todoCount = countErrorsOfType(TodoCode.TODO); |
| size -= todoCount; |
| if (size > 128) { |
| HashMap<String, Integer> counts = new HashMap<String, Integer>(); |
| int maxCount = 0; |
| for (AnalysisError error : errorList) { |
| ErrorCode code = error.getErrorCode(); |
| String codeName = code.getClass().getSimpleName() + "." + code; |
| Integer oldCount = counts.get(codeName); |
| int newCount = (oldCount == null) ? 1 : oldCount.intValue() + 1; |
| counts.put(codeName, Integer.valueOf(newCount)); |
| maxCount = Math.max(maxCount, newCount); |
| } |
| int countWidth = Integer.toString(maxCount).length(); |
| String format = "%0" + countWidth + "d" + " %s"; |
| for (Map.Entry<String, Integer> entry : counts.entrySet()) { |
| writer.println(); |
| writer.printf(format, entry.getValue(), entry.getKey()); |
| } |
| } else { |
| // Collections.sort(errorList, AnalysisError.FILE_COMPARATOR); |
| Collections.sort(errorList, AnalysisError.ERROR_CODE_COMPARATOR); |
| for (AnalysisError error : errorList) { |
| Source source = error.getSource(); |
| ErrorCode code = error.getErrorCode(); |
| if (code != TodoCode.TODO) { |
| int offset = error.getOffset(); |
| writer.println(); |
| writer.printf( |
| "%s %s (%d..%d) \"%s\"%s", |
| source == null ? "null" : source.getShortName(), |
| code.getClass().getSimpleName() + "." + code, |
| offset, |
| offset + error.getLength(), |
| error.getMessage(), |
| source == null ? "" : " (" + source.getFullName() + ")"); |
| } |
| } |
| if (todoCount > 0) { |
| writer.println(); |
| writer.printf("%d %s", todoCount, TodoCode.TODO); |
| } |
| } |
| Assert.fail(writer.toString()); |
| } |
| } |
| |
| /** |
| * Return the number of errors with the given error code that were recorded. |
| * |
| * @param errorCode the error code being searched for |
| * @return the number of errors with the given error code |
| */ |
| private int countErrorsOfType(ErrorCode errorCode) { |
| int count = 0; |
| for (AnalysisError error : errorList) { |
| if (error.getErrorCode() == errorCode) { |
| count++; |
| } |
| } |
| return count; |
| } |
| |
| /** |
| * Print the given value as a number of milliseconds. |
| * |
| * @param time the number of milliseconds to be printed |
| */ |
| private void printTime(long time) { |
| if (time == 0) { |
| System.out.print("0 ms"); |
| } else { |
| System.out.print(time); |
| System.out.print(" ms"); |
| if (time > 60000) { |
| long seconds = time / 1000; |
| long minutes = seconds / 60; |
| seconds -= minutes * 60; |
| System.out.print(" ("); |
| System.out.print(minutes); |
| System.out.print(":"); |
| if (seconds < 10) { |
| System.out.print("0"); |
| } |
| System.out.print(seconds); |
| System.out.print(")"); |
| } |
| } |
| } |
| |
| /** |
| * Add the errors reported for the given compilation unit to the given list of errors. |
| * |
| * @param element the compilation unit whose errors are to be added |
| * @throws AnalysisException if the errors could not be determined |
| */ |
| private void verify(CompilationUnitElement element) throws AnalysisException { |
| LibraryElement library = element.getLibrary(); |
| AnalysisContext context = library.getContext(); |
| CompilationUnit unit = context.resolveCompilationUnit(element.getSource(), library); |
| AnalysisError[] errors = context.computeErrors(element.getSource()); |
| if (errors == null) { |
| Assert.fail("The compilation unit \"" + element.getSource().getFullName() |
| + "\" was not resolved"); |
| } |
| for (AnalysisError error : errors) { |
| errorList.add(error); |
| } |
| unit.accept(staticTypeVerifier); |
| unit.accept(resolutionVerifier); |
| } |
| } |