Chromium Code Reviews| Index: editor/tools/plugins/com.google.dart.engine_test/src/com/google/dart/engine/internal/index/IndexContributorTest.java |
| diff --git a/editor/tools/plugins/com.google.dart.engine_test/src/com/google/dart/engine/internal/index/IndexContributorTest.java b/editor/tools/plugins/com.google.dart.engine_test/src/com/google/dart/engine/internal/index/IndexContributorTest.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e34890083bf3ba2c5066a7022ff9d101a0f666cb |
| --- /dev/null |
| +++ b/editor/tools/plugins/com.google.dart.engine_test/src/com/google/dart/engine/internal/index/IndexContributorTest.java |
| @@ -0,0 +1,399 @@ |
| +/* |
| + * 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.internal.index; |
| + |
| +import com.google.common.collect.ImmutableList; |
| +import com.google.common.collect.Lists; |
| +import com.google.dart.engine.EngineTestCase; |
| +import com.google.dart.engine.ast.ASTNode; |
| +import com.google.dart.engine.ast.ClassDeclaration; |
| +import com.google.dart.engine.ast.CompilationUnit; |
| +import com.google.dart.engine.ast.ExtendsClause; |
| +import com.google.dart.engine.ast.FunctionDeclaration; |
| +import com.google.dart.engine.ast.FunctionTypeAlias; |
| +import com.google.dart.engine.ast.ImplementsClause; |
| +import com.google.dart.engine.ast.NodeList; |
| +import com.google.dart.engine.ast.PrefixedIdentifier; |
| +import com.google.dart.engine.ast.SimpleIdentifier; |
| +import com.google.dart.engine.ast.TopLevelVariableDeclaration; |
| +import com.google.dart.engine.ast.TypeName; |
| +import com.google.dart.engine.ast.VariableDeclaration; |
| +import com.google.dart.engine.ast.VariableDeclarationList; |
| +import com.google.dart.engine.element.ClassElement; |
| +import com.google.dart.engine.element.CompilationUnitElement; |
| +import com.google.dart.engine.element.Element; |
| +import com.google.dart.engine.element.ElementLocation; |
| +import com.google.dart.engine.element.ElementProxy; |
| +import com.google.dart.engine.element.FieldElement; |
| +import com.google.dart.engine.element.FunctionElement; |
| +import com.google.dart.engine.element.LibraryElement; |
| +import com.google.dart.engine.element.TypeAliasElement; |
| +import com.google.dart.engine.element.VariableElement; |
| +import com.google.dart.engine.index.IndexStore; |
| +import com.google.dart.engine.index.Location; |
| +import com.google.dart.engine.index.Relationship; |
| +import com.google.dart.engine.source.Source; |
| +import com.google.dart.engine.type.Type; |
| + |
| +import org.mockito.ArgumentCaptor; |
| + |
| +import static org.fest.assertions.Assertions.assertThat; |
| +import static org.mockito.Mockito.atLeast; |
| +import static org.mockito.Mockito.mock; |
| +import static org.mockito.Mockito.reset; |
| +import static org.mockito.Mockito.verify; |
| +import static org.mockito.Mockito.verifyNoMoreInteractions; |
| +import static org.mockito.Mockito.when; |
| + |
| +import java.util.List; |
| + |
| +public class IndexContributorTest extends EngineTestCase { |
| + private static class ExpectedLocation { |
| + Element element; |
| + int offset; |
| + String name; |
| + |
| + ExpectedLocation(Element element, int offset, String name) { |
| + this.element = element; |
| + this.offset = offset; |
| + this.name = name; |
| + } |
| + } |
| + |
| + /** |
| + * Information about single relation recorded into {@link IndexStore}. |
| + */ |
| + private static class RecordedRelation { |
| + final ElementProxy element; |
| + final Relationship relation; |
| + final Location location; |
| + |
| + public RecordedRelation(ElementProxy element, Relationship relation, Location location) { |
| + this.element = element; |
| + this.relation = relation; |
| + this.location = location; |
| + } |
| + } |
| + |
| + /** |
| + * Asserts that given {@link ElementProxy} represents given {@link Element}. |
| + */ |
| + private static void assertElementProxy(Element expected, ElementProxy actual) { |
| + assertEquals(new ElementProxy(expected), actual); |
| + } |
| + |
| + /** |
| + * Asserts that actual {@link Location} has given properties. |
| + */ |
| + private static void assertLocation(Location actual, Element expectedElement, int expectedOffset, |
| + String expectedNameForLength) { |
| + assertElementProxy(expectedElement, actual.getElement()); |
| + assertEquals(expectedOffset, actual.getOffset()); |
| + assertEquals(expectedNameForLength.length(), actual.getLength()); |
| + assertSame(null, actual.getImportPrefix()); |
| + } |
| + |
| + /** |
| + * Asserts that actual {@link Location} has given properties. |
| + */ |
| + private static void assertLocation(Location actual, ExpectedLocation expected) { |
| + assertLocation(actual, expected.element, expected.offset, expected.name); |
| + } |
| + |
| + private static void assertRecordedRelation(RecordedRelation recordedRelation, |
| + Element expectedElement, Relationship expectedRelationship, ExpectedLocation expectedLocation) { |
| + assertElementProxy(expectedElement, recordedRelation.element); |
| + assertSame(expectedRelationship, recordedRelation.relation); |
| + assertLocation(recordedRelation.location, expectedLocation); |
| + } |
| + |
| + private static <T extends Element> T mockElement(Class<T> clazz, Element enclosingElement, |
| + ElementLocation location, int offset, String name) { |
| + T element = mockElement(clazz, location, offset, name); |
| + when(element.getEnclosingElement()).thenReturn(enclosingElement); |
| + return element; |
| + } |
| + |
| + private static <T extends Element> T mockElement(Class<T> clazz, ElementLocation location, |
| + int offset, String name) { |
| + T element = mock(clazz); |
| + when(element.getLocation()).thenReturn(location); |
| + when(element.getNameOffset()).thenReturn(offset); |
| + when(element.getName()).thenReturn(name); |
| + return element; |
| + } |
| + |
| + private static <T extends ASTNode> NodeList<T> mockNodeList(ASTNode owner, T... nodes) { |
|
Brian Wilkerson
2013/01/07 21:27:18
This isn't a mock, it's a real node list, so the n
scheglov
2013/01/07 22:45:41
Done.
|
| + NodeList<T> nodeList = new NodeList<T>(owner); |
| + for (int i = 0; i < nodes.length; i++) { |
| + nodeList.add(nodes[i]); |
| + } |
| + return nodeList; |
| + } |
| + |
| + private static SimpleIdentifier mockSimpleIdentifier(Element element, int offset, String name) { |
| + SimpleIdentifier identifier = mock(SimpleIdentifier.class); |
| + when(identifier.getElement()).thenReturn(element); |
| + when(identifier.getOffset()).thenReturn(offset); |
| + when(identifier.getLength()).thenReturn(name.length()); |
| + return identifier; |
| + } |
| + |
| + private static TypeName mockTypeName(Type type, int offset, String name) { |
| + TypeName typeName = mock(TypeName.class); |
| + when(typeName.getType()).thenReturn(type); |
| + when(typeName.getOffset()).thenReturn(offset); |
| + when(typeName.getLength()).thenReturn(name.length()); |
| + return typeName; |
| + } |
| + |
| + private static VariableDeclarationList mockVariableDeclaration(VariableDeclaration var) { |
| + VariableDeclarationList variableList = mock(VariableDeclarationList.class); |
| + NodeList<VariableDeclaration> variableNodeList = mockNodeList(variableList, var); |
| + when(variableList.getVariables()).thenReturn(variableNodeList); |
| + return variableList; |
| + } |
| + |
| + private IndexStore store = mock(IndexStore.class); |
| + private IndexContributor index = new IndexContributor(store); |
| + private Source unitSource = mock(Source.class); |
| + private CompilationUnit unitNode = mock(CompilationUnit.class); |
| + private LibraryElement libraryElement = mock(LibraryElement.class); |
| + private ElementLocation libraryLocation = mock(ElementLocation.class); |
| + |
| + private CompilationUnitElement unitElement = mock(CompilationUnitElement.class); |
| + |
| + public void test_accessByQualified_field() throws Exception { |
| + FunctionElement enclosingFunction = mock(FunctionElement.class); |
| + FieldElement fieldElement = mockElement(FieldElement.class, null, 10, "myField"); |
| + SimpleIdentifier field = mockSimpleIdentifier(fieldElement, 50, "myField"); |
| + // wrap into PrefixedIdentifier |
| + PrefixedIdentifier prefixedIdentifier = mock(PrefixedIdentifier.class); |
| + when(prefixedIdentifier.getIdentifier()).thenReturn(field); |
| + when(field.getParent()).thenReturn(prefixedIdentifier); |
| + // index |
| + index.enterScope(enclosingFunction); |
| + index.visitSimpleIdentifier(field); |
| + // verify |
| + List<RecordedRelation> relations = captureRecordedRelations(); |
| + assertThat(relations).hasSize(1); |
| + assertRecordedRelation( |
| + relations.get(0), |
| + fieldElement, |
| + IndexConstants.IS_ACCESSED_BY_QUALIFIED, |
| + new ExpectedLocation(enclosingFunction, 50, "myField")); |
| + } |
| + |
| + public void test_accessByUnqualified_field() throws Exception { |
| + FunctionElement enclosingFunction = mock(FunctionElement.class); |
| + FieldElement fieldElement = mockElement(FieldElement.class, null, 10, "myField"); |
| + SimpleIdentifier field = mockSimpleIdentifier(fieldElement, 50, "myField"); |
| + // index |
| + index.enterScope(enclosingFunction); |
| + index.visitSimpleIdentifier(field); |
| + // verify |
| + List<RecordedRelation> relations = captureRecordedRelations(); |
| + assertThat(relations).hasSize(1); |
| + assertRecordedRelation( |
| + relations.get(0), |
| + fieldElement, |
| + IndexConstants.IS_ACCESSED_BY_UNQUALIFIED, |
| + new ExpectedLocation(enclosingFunction, 50, "myField")); |
| + } |
| + |
| + public void test_ClassDeclaration() throws Exception { |
|
Brian Wilkerson
2013/01/07 21:27:18
A more consistent naming scheme might be good. Som
scheglov
2013/01/07 22:45:41
Agree.
Actually after breaking this big test into
|
| + ClassDeclaration classNodeA = mock(ClassDeclaration.class); |
| + ElementLocation classLocationA = mock(ElementLocation.class); |
| + ClassElement classElementA = mockElement(ClassElement.class, classLocationA, 42, "ABCDE"); |
| + when(classNodeA.getElement()).thenReturn(classElementA); |
| + when(classElementA.getEnclosingElement()).thenReturn(unitElement); |
| + // class A {} |
| + { |
| + // index ClassDeclaration |
| + index.visitClassDeclaration(classNodeA); |
| + // verify |
| + List<RecordedRelation> relations = captureRecordedRelations(); |
| + assertThat(relations).hasSize(1); |
| + assertRecordedRelation( |
| + relations.get(0), |
| + libraryElement, |
| + IndexConstants.DEFINES_CLASS, |
| + new ExpectedLocation(classElementA, 42, "ABCDE")); |
| + } |
| + // class A extends B {} class B {} |
|
Brian Wilkerson
2013/01/07 21:27:18
You certainly don't have to, but my inclination wo
scheglov
2013/01/07 22:45:41
Done.
|
| + { |
| + // class B {} |
| + ElementLocation classLocationB = mock(ElementLocation.class); |
| + ClassElement classElementB = mockElement( |
| + ClassElement.class, |
| + libraryElement, |
| + classLocationB, |
| + 1042, |
| + "B"); |
| + Type typeB = mock(Type.class); |
| + when(typeB.getElement()).thenReturn(classElementB); |
| + // "extends B" |
| + TypeName extendsTypeNameA = mockTypeName(typeB, 142, "B"); |
| + ExtendsClause extendsClauseA = new ExtendsClause(null, extendsTypeNameA); |
| + when(classNodeA.getExtendsClause()).thenReturn(extendsClauseA); |
| + // index ClassDeclaration |
| + reset(store); |
| + index.visitClassDeclaration(classNodeA); |
| + // verify |
| + List<RecordedRelation> relations = captureRecordedRelations(); |
| + assertThat(relations).hasSize(2); |
| + assertRecordedRelation( |
| + relations.get(1), |
| + classElementB, |
| + IndexConstants.IS_EXTENDED_BY, |
| + new ExpectedLocation(classElementA, 142, "B")); |
| + } |
| + // class A implements MyInterface {} class MyInterface {} |
| + { |
| + // class B {} |
| + ElementLocation classLocationB = mock(ElementLocation.class); |
| + ClassElement classElementB = mockElement( |
| + ClassElement.class, |
| + libraryElement, |
| + classLocationB, |
| + 2042, |
| + "MyInterface"); |
| + Type typeB = mock(Type.class); |
| + when(typeB.getElement()).thenReturn(classElementB); |
| + // "implements B" |
| + when(classNodeA.getExtendsClause()).thenReturn(null); |
| + TypeName implementsTypeNameA = mockTypeName(typeB, 242, "MyInterface"); |
| + ImplementsClause implementsClauseA = new ImplementsClause( |
| + null, |
| + ImmutableList.of(implementsTypeNameA)); |
| + when(classNodeA.getImplementsClause()).thenReturn(implementsClauseA); |
| + // index ClassDeclaration |
| + reset(store); |
| + index.visitClassDeclaration(classNodeA); |
| + // verify |
| + List<RecordedRelation> relations = captureRecordedRelations(); |
| + assertThat(relations).hasSize(2); |
| + assertRecordedRelation( |
| + relations.get(1), |
| + classElementB, |
| + IndexConstants.IS_IMPLEMENTED_BY, |
| + new ExpectedLocation(classElementA, 242, "MyInterface")); |
| + } |
| + } |
| + |
| + public void test_createElementLocation() throws Exception { |
| + ElementLocation elementLocation = mock(ElementLocation.class); |
| + Element element = mockElement(Element.class, elementLocation, 42, "myName"); |
| + Location location = IndexContributor.createElementLocation(element); |
| + assertLocation(location, element, 42, "myName"); |
| + } |
| + |
| + public void test_createElementLocation_null() throws Exception { |
| + assertSame(null, IndexContributor.createElementLocation(null)); |
| + } |
| + |
| + public void test_FunctionDeclaration() throws Exception { |
| + FunctionElement functionElement = mockElement(FunctionElement.class, null, 42, "myFunction"); |
| + FunctionDeclaration function = mock(FunctionDeclaration.class); |
| + when(function.getElement()).thenReturn(functionElement); |
| + // index |
| + index.visitFunctionDeclaration(function); |
| + // verify |
| + List<RecordedRelation> relations = captureRecordedRelations(); |
| + assertThat(relations).hasSize(1); |
| + assertRecordedRelation( |
| + relations.get(0), |
| + libraryElement, |
| + IndexConstants.DEFINES_FUNCTION, |
| + new ExpectedLocation(functionElement, 42, "myFunction")); |
| + } |
| + |
| + public void test_FunctionTypeAlias() throws Exception { |
| + TypeAliasElement functionElement = mockElement(TypeAliasElement.class, null, 42, "MyFunction"); |
| + FunctionTypeAlias function = mock(FunctionTypeAlias.class); |
| + when(function.getElement()).thenReturn(functionElement); |
| + // index |
| + index.visitFunctionTypeAlias(function); |
| + // verify |
| + List<RecordedRelation> relations = captureRecordedRelations(); |
| + assertThat(relations).hasSize(1); |
| + assertRecordedRelation( |
| + relations.get(0), |
| + libraryElement, |
| + IndexConstants.DEFINES_FUNCTION_TYPE, |
| + new ExpectedLocation(functionElement, 42, "MyFunction")); |
| + } |
| + |
| + public void test_TopLevelVariableDeclaration() throws Exception { |
| + // VariableDeclaration |
| + ElementLocation varLocation = mock(ElementLocation.class); |
| + VariableElement varElement = mockElement(VariableElement.class, varLocation, 42, "myVar"); |
| + VariableDeclaration var = mock(VariableDeclaration.class); |
| + when(var.getElement()).thenReturn(varElement); |
| + // TopLevelVariableDeclaration |
| + TopLevelVariableDeclaration topDeclaration = mock(TopLevelVariableDeclaration.class); |
| + VariableDeclarationList variableList = mockVariableDeclaration(var); |
| + when(topDeclaration.getVariables()).thenReturn(variableList); |
| + index.visitTopLevelVariableDeclaration(topDeclaration); |
| + // verify |
| + List<RecordedRelation> relations = captureRecordedRelations(); |
| + assertThat(relations).hasSize(1); |
| + assertRecordedRelation( |
| + relations.get(0), |
| + libraryElement, |
| + IndexConstants.DEFINES_VARIABLE, |
| + new ExpectedLocation(varElement, 42, "myVar")); |
| + } |
| + |
| + public void test_unresolvedUnit() throws Exception { |
| + index = new IndexContributor(store); |
| + // no CompilationUnitElement, but no NPE |
| + unitNode = mock(CompilationUnit.class); |
| + index.visitCompilationUnit(unitNode); |
| + verify(unitNode).getElement(); |
| + verifyNoMoreInteractions(unitNode); |
| + // no enclosing element |
| + assertSame(null, index.peekElement()); |
| + } |
| + |
| + @Override |
| + protected void setUp() throws Exception { |
| + super.setUp(); |
| + when(libraryElement.getLocation()).thenReturn(libraryLocation); |
| + when(libraryElement.getDefiningCompilationUnit()).thenReturn(unitElement); |
| + when(unitNode.getElement()).thenReturn(unitElement); |
| + when(unitElement.getSource()).thenReturn(unitSource); |
| + when(unitElement.getEnclosingElement()).thenReturn(libraryElement); |
| + index.visitCompilationUnit(unitNode); |
| + } |
| + |
| + private List<RecordedRelation> captureRecordedRelations() { |
| + ArgumentCaptor<ElementProxy> argElement = ArgumentCaptor.forClass(ElementProxy.class); |
| + ArgumentCaptor<Relationship> argRel = ArgumentCaptor.forClass(Relationship.class); |
| + ArgumentCaptor<Location> argLocation = ArgumentCaptor.forClass(Location.class); |
| + verify(store, atLeast(0)).recordRelationship( |
| + argElement.capture(), |
| + argRel.capture(), |
| + argLocation.capture()); |
| + List<RecordedRelation> relations = Lists.newArrayList(); |
| + int count = argElement.getAllValues().size(); |
| + for (int i = 0; i < count; i++) { |
| + relations.add(new RecordedRelation( |
| + argElement.getAllValues().get(i), |
| + argRel.getAllValues().get(i), |
| + argLocation.getAllValues().get(i))); |
| + } |
| + return relations; |
| + } |
| +} |