/* * 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.resolver; import com.google.dart.engine.AnalysisEngine; import com.google.dart.engine.EngineTestCase; import com.google.dart.engine.ast.CompilationUnit; import com.google.dart.engine.ast.CompilationUnitMember; import com.google.dart.engine.ast.Expression; import com.google.dart.engine.ast.TopLevelVariableDeclaration; import com.google.dart.engine.ast.VariableDeclaration; import com.google.dart.engine.context.AnalysisContext; import com.google.dart.engine.context.AnalysisContextFactory; import com.google.dart.engine.context.AnalysisException; import com.google.dart.engine.context.AnalysisOptions; import com.google.dart.engine.context.ChangeSet; import com.google.dart.engine.element.ClassElement; 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.GatheringErrorListener; import com.google.dart.engine.internal.context.AnalysisContextImpl; import com.google.dart.engine.internal.context.AnalysisOptionsImpl; import com.google.dart.engine.internal.element.ClassElementImpl; import com.google.dart.engine.internal.element.CompilationUnitElementImpl; import com.google.dart.engine.internal.element.LibraryElementImpl; import com.google.dart.engine.internal.resolver.ResolutionVerifier; import com.google.dart.engine.internal.resolver.TypeProvider; import com.google.dart.engine.source.FileBasedSource; import com.google.dart.engine.source.Source; import static com.google.dart.engine.ast.AstFactory.identifier; import static com.google.dart.engine.ast.AstFactory.libraryIdentifier; import static com.google.dart.engine.utilities.io.FileUtilities2.createFile; import junit.framework.AssertionFailedError; public class ResolverTestCase extends EngineTestCase { /** * The analysis context used to parse the compilation units being resolved. */ protected AnalysisContextImpl analysisContext; @Override public void setUp() { reset(); } /** * Add a source file to the content provider. The file path should be absolute. * * @param filePath the path of the file being added * @param contents the contents to be returned by the content provider for the specified file * @return the source object representing the added file */ protected Source addNamedSource(String filePath, String contents) { Source source = cacheSource(filePath, contents); ChangeSet changeSet = new ChangeSet(); changeSet.addedSource(source); analysisContext.applyChanges(changeSet); return source; } /** * Add a source file to the content provider. * * @param contents the contents to be returned by the content provider for the specified file * @return the source object representing the added file */ protected Source addSource(String contents) { return addNamedSource("/test.dart", contents); } /** * Assert that the number of errors reported against the given source 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 source the source against which the errors should have been reported * @param expectedErrorCodes the error codes of the errors that should have been reported * @throws AnalysisException if the reported errors could not be computed * @throws AssertionFailedError if a different number of errors have been reported than were * expected */ protected void assertErrors(Source source, ErrorCode... expectedErrorCodes) throws AnalysisException { GatheringErrorListener errorListener = new GatheringErrorListener(); for (AnalysisError error : analysisContext.computeErrors(source)) { errorListener.onError(error); } errorListener.assertErrorsWithCodes(expectedErrorCodes); } /** * Assert that no errors have been reported against the given source. * * @param source the source against which no errors should have been reported * @throws AnalysisException if the reported errors could not be computed * @throws AssertionFailedError if any errors have been reported */ protected void assertNoErrors(Source source) throws AnalysisException { assertErrors(source); } /** * Cache the source file content in the source factory but don't add the source to the analysis * context. The file path should be absolute. * * @param filePath the path of the file being cached * @param contents the contents to be returned by the content provider for the specified file * @return the source object representing the cached file */ protected Source cacheSource(String filePath, String contents) { Source source = new FileBasedSource(createFile(filePath)); analysisContext.setContents(source, contents); return source; } /** * Create a library element that represents a library named {@code "test"} containing a single * empty compilation unit. * * @return the library element that was created */ protected LibraryElementImpl createDefaultTestLibrary() { return createTestLibrary(new AnalysisContextImpl(), "test"); } /** * Create a library element that represents a library with the given name containing a single * empty compilation unit. * * @param libraryName the name of the library to be created * @return the library element that was created */ protected LibraryElementImpl createTestLibrary(AnalysisContext context, String libraryName, String... typeNames) { int count = typeNames.length; CompilationUnitElementImpl[] sourcedCompilationUnits = new CompilationUnitElementImpl[count]; for (int i = 0; i < count; i++) { String typeName = typeNames[i]; ClassElementImpl type = new ClassElementImpl(identifier(typeName)); String fileName = typeName + ".dart"; CompilationUnitElementImpl compilationUnit = new CompilationUnitElementImpl(fileName); compilationUnit.setSource(createNamedSource(fileName)); compilationUnit.setTypes(new ClassElement[] {type}); sourcedCompilationUnits[i] = compilationUnit; } String fileName = libraryName + ".dart"; CompilationUnitElementImpl compilationUnit = new CompilationUnitElementImpl(fileName); compilationUnit.setSource(createNamedSource(fileName)); LibraryElementImpl library = new LibraryElementImpl(context, libraryIdentifier(libraryName)); library.setDefiningCompilationUnit(compilationUnit); library.setParts(sourcedCompilationUnits); return library; } /** * Enable optionally strict union types for the current test. * * @param strictUnionTypes {@code true} if union types should be strict. */ protected void enableUnionTypes(boolean strictUnionTypes) { AnalysisEngine.getInstance().setEnableUnionTypes(true); AnalysisEngine.getInstance().setStrictUnionTypes(strictUnionTypes); } protected Expression findTopLevelConstantExpression(CompilationUnit compilationUnit, String name) { return findTopLevelDeclaration(compilationUnit, name).getInitializer(); } protected VariableDeclaration findTopLevelDeclaration(CompilationUnit compilationUnit, String name) { for (CompilationUnitMember member : compilationUnit.getDeclarations()) { if (member instanceof TopLevelVariableDeclaration) { for (VariableDeclaration variable : ((TopLevelVariableDeclaration) member).getVariables().getVariables()) { if (variable.getName().getName().equals(name)) { return variable; } } } } return null; // Not found } protected AnalysisContext getAnalysisContext() { return analysisContext; } /** * Return a type provider that can be used to test the results of resolution. * * @return a type provider * @throws AnalysisException if dart:core cannot be resolved */ protected TypeProvider getTypeProvider() throws AnalysisException { return analysisContext.getTypeProvider(); } /** * In the rare cases we want to group several tests into single "test_" method, so need a way to * reset test instance to reuse it. */ protected void reset() { analysisContext = AnalysisContextFactory.contextWithCore(); // These defaults are duplicated for the editor in // editor/tools/plugins/com.google.dart.tools.core/.options . AnalysisEngine.getInstance().setEnableUnionTypes(false); AnalysisEngine.getInstance().setStrictUnionTypes(false); } /** * Reset the analysis context to have the 'enableAsync' option set to true. */ protected void resetWithAsync() { AnalysisOptionsImpl options = new AnalysisOptionsImpl(); options.setEnableAsync(true); analysisContext = AnalysisContextFactory.contextWithCoreAndOptions(options); } /** * Reset the analysis context to have the 'enableEnum' option set to true. */ protected void resetWithEnum() { AnalysisOptionsImpl options = new AnalysisOptionsImpl(); options.setEnableEnum(true); analysisContext = AnalysisContextFactory.contextWithCoreAndOptions(options); } /** * Reset the analysis context to have the given options applied. * * @param options the analysis options to be applied to the context */ protected void resetWithOptions(AnalysisOptions options) { analysisContext = AnalysisContextFactory.contextWithCoreAndOptions(options); } /** * Given a library and all of its parts, resolve the contents of the library and the contents of * the parts. This assumes that the sources for the library and its parts have already been added * to the content provider using the method {@link #addNamedSource(String, String)}. * * @param librarySource the source for the compilation unit that defines the library * @return the element representing the resolved library * @throws AnalysisException if the analysis could not be performed */ protected LibraryElement resolve(Source librarySource) throws AnalysisException { return analysisContext.computeLibraryElement(librarySource); } protected void resolve(String[] strSources, ErrorCode[] codes) throws Exception { // Analysis and assertions Source source = resolveSources(strSources); assertErrors(source, codes); verify(source); } /** * Return the resolved compilation unit corresponding to the given source in the given library. * * @param source the source of the compilation unit to be returned * @param library the library in which the compilation unit is to be resolved * @return the resolved compilation unit * @throws Exception if the compilation unit could not be resolved */ protected CompilationUnit resolveCompilationUnit(Source source, LibraryElement library) throws Exception { return analysisContext.resolveCompilationUnit(source, library); } protected CompilationUnit resolveSource(String sourceText) throws AnalysisException { return resolveSource("/test.dart", sourceText); } protected CompilationUnit resolveSource(String fileName, String sourceText) throws AnalysisException { Source source = addNamedSource(fileName, sourceText); LibraryElement library = getAnalysisContext().computeLibraryElement(source); return getAnalysisContext().resolveCompilationUnit(source, library); } protected Source resolveSources(String[] sourceTexts) throws AnalysisException { for (int i = 0; i < sourceTexts.length; i++) { CompilationUnit unit = resolveSource("/lib" + (i + 1) + ".dart", sourceTexts[i]); // reference the source if this is the last source if (i + 1 == sourceTexts.length) { return unit.getElement().getSource(); } } return null; } protected void resolveWithAndWithoutExperimental(String[] strSources, ErrorCode[] codesWithoutExperimental, ErrorCode[] codesWithExperimental) throws Exception { // Setup analysis context as non-experimental AnalysisOptionsImpl options = new AnalysisOptionsImpl(); options.setEnableDeferredLoading(false); resetWithOptions(options); // Analysis and assertions Source source = resolveSources(strSources); assertErrors(source, codesWithoutExperimental); verify(source); // Setup analysis context as experimental reset(); // Analysis and assertions source = resolveSources(strSources); assertErrors(source, codesWithExperimental); verify(source); } @Override protected void tearDown() throws Exception { analysisContext = null; super.tearDown(); } /** * Verify that all of the identifiers in the compilation units associated with the given sources * have been resolved. * * @param resolvedElementMap a table mapping the AST nodes that have been resolved to the element * to which they were resolved * @param sources the sources identifying the compilation units to be verified * @throws Exception if the contents of the compilation unit cannot be accessed */ protected void verify(Source... sources) throws Exception { ResolutionVerifier verifier = new ResolutionVerifier(); for (Source source : sources) { analysisContext.parseCompilationUnit(source).accept(verifier); } verifier.assertResolved(); } /** * Create a source object representing a file with the given name and give it an empty content. * * @param fileName the name of the file for which a source is to be created * @return the source that was created */ private FileBasedSource createNamedSource(String fileName) { FileBasedSource source = new FileBasedSource(createFile(fileName)); analysisContext.setContents(source, ""); return source; } }