/*
* Copyright (c) 2012, 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.AnalysisEngine;
import com.google.dart.engine.context.AnalysisContext;
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.sdk.DartSdk;
import com.google.dart.engine.sdk.DirectoryBasedDartSdk;
import com.google.dart.engine.source.DartUriResolver;
import com.google.dart.engine.source.FileBasedSource;
import com.google.dart.engine.source.FileUriResolver;
import com.google.dart.engine.source.PackageUriResolver;
import com.google.dart.engine.source.Source;
import com.google.dart.engine.source.SourceFactory;
import com.google.dart.engine.utilities.io.FileUtilities;
import com.google.dart.engine.utilities.io.PrintStringWriter;
import junit.framework.Assert;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
public class LanguageAnalysisTest extends DirectoryBasedSuiteBuilder {
public class AnalysisTestWithSource extends AnalysisTest {
private int index;
private String contents;
public AnalysisTestWithSource(File sourceFile, int index, String contents) {
super(sourceFile);
this.index = index;
this.contents = contents;
}
@Override
public void testFile() throws Exception {
testSingleFile(getSourceFile(), contents);
}
@Override
protected String getTestName() {
if (index >= 0) {
return getSourceFile().getName() + ":" + index;
}
return getSourceFile().getName();
}
}
public class ReportingTest extends TestCase {
public ReportingTest(String methodName) {
super(methodName);
}
public void reportResults() throws Exception {
System.out.print("Analyzed ");
System.out.print(fileCount);
System.out.print(" files in ");
printTime(totalTime);
System.out.println();
printStatistics();
System.out.print(skippedTests);
System.out.println(" tests were skipped");
System.out.print(errorCount);
System.out.println(" tests failed with unexpected errors");
System.out.print(noErrorCount);
System.out.println(" tests failed with no errors being generated");
}
}
/**
* The URI resolver used to resolve package: URI's.
*/
private static PackageUriResolver packageResolver;
/**
* An array containing the relative paths of test files that are expected to fail.
*/
private static final String[] FAILING_TESTS = {//
"/application_test.dart", // missing a part-of directive
"/bit_operations_test.dart", // requires type inferencing
"/checked_null_test.dart", // invocation of undefined function
"/closure_in_initializer2_test.dart", // invocation of undefined function
"/compile_time_constant_k_test.dart", // duplicated keys in literal map
"/compile_time_constant_o_test.dart", // duplicated keys in literal map
"/const_objects_are_immutable_test.dart", // undefined setter
"/constructor_test.dart", // references undefined method
"/factory_implementation_test.dart", // misuse of factory method
"/first_class_types_literals_test.dart", // type literals don't implement +
"/function_malformed_result_type_test.dart", // wrong number of type arguments
"/generic_instanceof_test.dart", // missing part-of directive
"/implicit_scope_test.dart", //test implies that some statements create their own scope
"/import_combinators_test.dart", // missing a part-of directive
"/lazy_static6_test.dart", // missing a part-of directive
"/many_generic_instanceof_test.dart", // missing a part-of directive
"/many_overridden_no_such_method_test.dart", // missing a part-of directive
"/map_literal2_test.dart", // keys in a map must be constant
"/multi_pass2_test.dart", // missing a part-of directive
"/multi_pass_test.dart", // missing a part-of directive
"/null_no_such_method_test.dart", // valid and expected warning which isn't declared by test
"/overridden_no_such_method_test.dart", // missing a part-of directive
"/private2_test.dart", // missing a part-of directive
"/private3_test.dart", // missing a part-of directive
"/private_test.dart", // missing a part-of directive
"/super_assign_test.dart", // type propagation required
"/super_call4_test.dart", // invocations through noSuchMethod
"/super_getter_setter_test.dart", //invocations through noSuchMethod
"/super_operator_index5_test.dart", //invocations through noSuchMethod
"/super_operator_index6_test.dart", //invocations through noSuchMethod
"/super_operator_index7_test.dart", //invocations through noSuchMethod
"/ternary_test.dart", // no unary plus operator
"/top_level_entry_test.dart", // missing a part-of directive
"/top_level_multiple_files_test.dart", // missing a part-of directive
"/top_level_non_prefixed_library_test.dart", // missing a part-of directive
"/type_error_test.dart", // invalid assumption about the type of an assignment statement
"/type_vm_test.dart", // StaticTypeWarningCode.NON_BOOL_CONDITION is a warning, not an error
"/unary2_test.dart", // no unary plus operator
"/unary_test.dart", // no unary plus operator
};
/**
* Build a JUnit test suite that will analyze all of the tests in the language test suite.
*
* @return the test suite that was built
*/
public static Test suite() {
String svnRootName = System.getProperty("svnRoot");
if (svnRootName != null) {
File svnRoot = new File(svnRootName);
File packageDirectory = new File(svnRoot, "pkg");
File testDirectory = new File(svnRoot, "tests/language");
if (packageDirectory.exists() && testDirectory.exists()) {
packageResolver = new PackageUriResolver(packageDirectory) {
@Override
protected File getCanonicalFile(File packagesDirectory, String pkgName, String relPath) {
File pkgDir = new File(packagesDirectory, pkgName);
try {
pkgDir = pkgDir.getCanonicalFile();
} catch (IOException e) {
AnalysisEngine.getInstance().getLogger().logError("Canonical failed: " + pkgDir, e);
}
File file = new File(pkgDir, relPath.replace('/', File.separatorChar));
if (!file.exists()) {
file = new File(new File(pkgDir, "lib"), relPath.replace('/', File.separatorChar));
}
return file;
}
};
LanguageAnalysisTest tester = new LanguageAnalysisTest();
TestSuite suite = tester.buildSuite(testDirectory, "Analyze language files");
suite.addTest(tester.new ReportingTest("reportResults"));
return suite;
}
}
return new TestSuite("Analyze language files (no tests: directory not found)");
}
/**
* Return {@code true} if the given file should be skipped because it is on the list of files to
* be skipped.
*
* @param file the file being tested
* @return {@code true} if the file should be skipped
*/
private static boolean expectedToFail(File file) {
String fullPath = file.getAbsolutePath();
for (String relativePath : FAILING_TESTS) {
if (fullPath.endsWith(relativePath)) {
return true;
}
}
return false;
}
private long fileCount = 0L;
private long totalTime = 0L;
private int skippedTests = 0;
@Override
protected void addTestForFile(TestSuite suite, File file) {
if (file.getName().endsWith("_test.dart")) {
//
// Determine how many tests to create from this one file.
//
try {
String contents = FileUtilities.getContents(file);
if (contents.indexOf("///") < 0) {
suite.addTest(new AnalysisTestWithSource(file, -1, contents));
return;
}
String[] lines = toLines(contents);
int count = getTestCount(lines);
for (int i = 0; i < count; i++) {
String testSource = getTestSource(i, lines);
suite.addTest(new AnalysisTestWithSource(file, i, testSource));
}
} catch (IOException exception) {
suite.addTest(new TestSuite("Analyze " + file.getAbsolutePath() + " (could not read file)"));
}
}
}
@Override
protected void testSingleFile(File sourceFile) throws IOException {
// This method should never be called.
throw new InternalError("Wrong test method invoked for file " + sourceFile.getAbsolutePath());
}
protected void testSingleFile(File sourceFile, String contents) throws Exception {
//
// Uncomment the lines below to stop reporting failures for files containing directives or
// interface declarations.
//
if (contents.indexOf("#library") >= 0 || contents.indexOf("#import") >= 0
|| contents.indexOf("#source") >= 0 || contents.indexOf("interface") >= 0
|| contents.indexOf("===") >= 0 || contents.indexOf("!==") >= 0) {
skippedTests++;
return;
}
//
// Determine whether the test is expected to pass or fail.
//
boolean errorExpected = sourceFile.getName().endsWith("_negative_test.dart")
|| contents.indexOf("compile-time error") > 0
|| contents.indexOf("static type warning") > 0 || contents.indexOf("static warning") > 0;
// Uncomment the lines below to stop reporting failures for files that are expected to contain
// errors.
// if (errorExpected) {
// skippedTests++;
// return;
// }
//
// Create the analysis context in which the file will be analyzed.
//
DartSdk sdk = DirectoryBasedDartSdk.getDefaultSdk();
Assert.assertNotNull(packageResolver);
SourceFactory sourceFactory = new SourceFactory(
new DartUriResolver(sdk),
new FileUriResolver(),
packageResolver);
AnalysisContext context = AnalysisEngine.getInstance().createAnalysisContext();
context.setSourceFactory(sourceFactory);
//
// Analyze the file.
//
Source source = new FileBasedSource(sourceFile);
context.setContents(source, contents);
long startTime = System.currentTimeMillis();
LibraryElement library = context.computeLibraryElement(source);
long endTime = System.currentTimeMillis();
if (library == null) {
Assert.fail("Could not analyze " + sourceFile.getAbsolutePath());
}
//
// Gather statistics.
//
fileCount++;
totalTime += (endTime - startTime);
//
// Validate the results.
//
ElementStructureVerifier elementVerifier = new ElementStructureVerifier();
library.accept(elementVerifier);
elementVerifier.assertValid();
ArrayList<AnalysisError> errorList = new ArrayList<AnalysisError>();
addErrors(errorList, library.getDefiningCompilationUnit());
for (CompilationUnitElement part : library.getParts()) {
addErrors(errorList, part);
}
assertErrors(errorExpected, expectedToFail(sourceFile), errorList);
}
private int getTestCount(String[] lines) {
int maxIndex = 0;
for (int i = 0; i < lines.length; i++) {
int testIndex = parseTestIndex(lines[i]);
maxIndex = Math.max(maxIndex, testIndex);
}
return maxIndex + 1;
}
private String getTestSource(int testIndex, String[] lines) {
@SuppressWarnings("resource")
PrintStringWriter writer = new PrintStringWriter();
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
int index = parseTestIndex(line);
if (index < 0 || index == testIndex) {
writer.println(line);
}
}
return writer.toString();
}
private int parseTestIndex(String line) {
int index = line.indexOf("///");
if (index < 0) {
return -1;
}
int length = line.length();
while (index < length && Character.isWhitespace(line.charAt(index))) {
index++;
}
int testIndex = 0;
while (index < length && Character.isDigit(line.charAt(index))) {
testIndex = (testIndex * 10) + Character.digit(line.charAt(index++), 10);
}
return testIndex;
}
private String[] toLines(String source) {
ArrayList<String> lines = new ArrayList<String>();
BufferedReader reader = new BufferedReader(new StringReader(source));
String line;
try {
line = reader.readLine();
while (line != null) {
lines.add(line);
line = reader.readLine();
}
} catch (IOException exception) {
// This cannot happen because we are reading from a string, not from an external source.
}
return lines.toArray(new String[lines.size()]);
}
}