blob: f67ff84c26e41496dc52a88a4bfc594a98f12067 [file] [log] [blame]
/*
* 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);
}
}