| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library test.services.src.index.store.split_store; |
| |
| import 'dart:async'; |
| |
| import 'package:analysis_server/analysis/index/index_core.dart'; |
| import 'package:analysis_server/src/services/index/index.dart'; |
| import 'package:analysis_server/src/services/index/indexable_element.dart'; |
| import 'package:analysis_server/src/services/index/store/codec.dart'; |
| import 'package:analysis_server/src/services/index/store/memory_node_manager.dart'; |
| import 'package:analysis_server/src/services/index/store/split_store.dart'; |
| import 'package:analyzer/src/generated/element.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| import 'package:typed_mock/typed_mock.dart'; |
| import 'package:unittest/unittest.dart'; |
| |
| import '../../../mocks.dart'; |
| import '../../../utils.dart'; |
| import 'mocks.dart'; |
| import 'single_source_container.dart'; |
| |
| main() { |
| initializeTestEnvironment(); |
| defineReflectiveTests(_FileNodeManagerTest); |
| defineReflectiveTests(_IndexNodeTest); |
| defineReflectiveTests(_LocationDataTest); |
| defineReflectiveTests(_RelationKeyDataTest); |
| defineReflectiveTests(_SplitIndexStoreTest); |
| } |
| |
| void _assertHasLocation(List<LocationImpl> locations, IndexableElement element, |
| int offset, int length, |
| {bool isQualified: false, bool isResolved: true}) { |
| for (LocationImpl location in locations) { |
| if ((element == null || location.indexable == element) && |
| location.offset == offset && |
| location.length == length && |
| location.isQualified == isQualified && |
| location.isResolved == isResolved) { |
| return; |
| } |
| } |
| fail('Expected to find Location' |
| '(element=$element, offset=$offset, length=$length)'); |
| } |
| |
| void _assertHasLocationQ(List<LocationImpl> locations, IndexableElement element, |
| int offset, int length) { |
| _assertHasLocation(locations, element, offset, length, isQualified: true); |
| } |
| |
| @reflectiveTest |
| class _FileNodeManagerTest { |
| MockLogger logger = new MockLogger(); |
| StringCodec stringCodec = new StringCodec(); |
| RelationshipCodec relationshipCodec; |
| |
| AnalysisContext context = new MockAnalysisContext('context'); |
| ContextCodec contextCodec = new MockContextCodec(); |
| int contextId = 13; |
| |
| ElementCodec elementCodec = new MockElementCodec(); |
| int nextElementId = 0; |
| |
| FileNodeManager nodeManager; |
| FileManager fileManager = new _MockFileManager(); |
| |
| void setUp() { |
| relationshipCodec = new RelationshipCodec(stringCodec); |
| nodeManager = new FileNodeManager(fileManager, logger, stringCodec, |
| contextCodec, elementCodec, relationshipCodec); |
| when(contextCodec.encode(context)).thenReturn(contextId); |
| when(contextCodec.decode(contextId)).thenReturn(context); |
| } |
| |
| void test_clear() { |
| nodeManager.clear(); |
| verify(fileManager.clear()).once(); |
| } |
| |
| void test_getLocationCount_empty() { |
| expect(nodeManager.locationCount, 0); |
| } |
| |
| void test_getNode_contextNull() { |
| String name = '42.index'; |
| // record bytes |
| List<int> bytes; |
| when(fileManager.write(name, anyObject)).thenInvoke((name, bs) { |
| bytes = bs; |
| }); |
| // put Node |
| Future putFuture; |
| { |
| IndexNode node = new IndexNode(context, elementCodec, relationshipCodec); |
| putFuture = nodeManager.putNode(name, node); |
| } |
| // do in the "put" Future |
| putFuture.then((_) { |
| // force "null" context |
| when(contextCodec.decode(contextId)).thenReturn(null); |
| // prepare input bytes |
| when(fileManager.read(name)).thenReturn(new Future.value(bytes)); |
| // get Node |
| return nodeManager.getNode(name).then((IndexNode node) { |
| expect(node, isNull); |
| // no exceptions |
| verifyZeroInteractions(logger); |
| }); |
| }); |
| } |
| |
| test_getNode_invalidVersion() { |
| String name = '42.index'; |
| // prepare a stream with an invalid version |
| when(fileManager.read(name)) |
| .thenReturn(new Future.value([0x01, 0x02, 0x03, 0x04])); |
| // do in the Future |
| return nodeManager.getNode(name).then((IndexNode node) { |
| // no IndexNode |
| expect(node, isNull); |
| // failed |
| verify(logger.logError(anyObject, anyObject)).once(); |
| }); |
| } |
| |
| test_getNode_streamException() { |
| String name = '42.index'; |
| when(fileManager.read(name)).thenReturn(new Future(() { |
| return throw new Exception(); |
| })); |
| // do in the Future |
| return nodeManager.getNode(name).then((IndexNode node) { |
| expect(node, isNull); |
| // failed |
| verify(logger.logError(anyString, anyObject)).once(); |
| }); |
| } |
| |
| test_getNode_streamNull() { |
| String name = '42.index'; |
| when(fileManager.read(name)).thenReturn(new Future.value(null)); |
| // do in the Future |
| return nodeManager.getNode(name).then((IndexNode node) { |
| expect(node, isNull); |
| // OK |
| verifyZeroInteractions(logger); |
| }); |
| } |
| |
| void test_newNode() { |
| IndexNode node = nodeManager.newNode(context); |
| expect(node.context, context); |
| expect(node.locationCount, 0); |
| } |
| |
| test_putNode_getNode() { |
| String name = '42.index'; |
| // record bytes |
| List<int> bytes; |
| when(fileManager.write(name, anyObject)).thenInvoke((name, bs) { |
| bytes = bs; |
| }); |
| // prepare elements |
| IndexableElement elementA = _mockElement(); |
| IndexableElement elementB = _mockElement(); |
| IndexableElement elementC = _mockElement(); |
| RelationshipImpl relationship = |
| RelationshipImpl.getRelationship('my-relationship'); |
| // put Node |
| Future putFuture; |
| { |
| // prepare relations |
| int relationshipId = relationshipCodec.encode(relationship); |
| RelationKeyData key = |
| new RelationKeyData.forData(0, 1, 2, relationshipId); |
| List<LocationData> locations = [ |
| new LocationData.forData(3, 4, 5, 1, 10, 2), |
| new LocationData.forData(6, 7, 8, 2, 20, 3) |
| ]; |
| Map<RelationKeyData, List<LocationData>> relations = {key: locations}; |
| // prepare Node |
| IndexNode node = new _MockIndexNode(); |
| when(node.context).thenReturn(context); |
| when(node.relations).thenReturn(relations); |
| when(node.locationCount).thenReturn(2); |
| // put Node |
| putFuture = nodeManager.putNode(name, node); |
| } |
| // do in the Future |
| putFuture.then((_) { |
| // has locations |
| expect(nodeManager.locationCount, 2); |
| // prepare input bytes |
| when(fileManager.read(name)).thenReturn(new Future.value(bytes)); |
| // get Node |
| return nodeManager.getNode(name).then((IndexNode node) { |
| expect(2, node.locationCount); |
| { |
| List<LocationImpl> locations = |
| node.getRelationships(elementA, relationship); |
| expect(locations, hasLength(2)); |
| _assertHasLocation(locations, elementB, 1, 10); |
| _assertHasLocationQ(locations, elementC, 2, 20); |
| } |
| }); |
| }); |
| } |
| |
| test_putNode_streamException() { |
| String name = '42.index'; |
| Exception exception = new Exception(); |
| when(fileManager.write(name, anyObject)).thenReturn(new Future(() { |
| return throw exception; |
| })); |
| // prepare IndexNode |
| IndexNode node = new _MockIndexNode(); |
| when(node.context).thenReturn(context); |
| when(node.locationCount).thenReturn(0); |
| when(node.relations).thenReturn({}); |
| // try to put |
| return nodeManager.putNode(name, node).then((_) { |
| // failed |
| verify(logger.logError(anyString, anyObject)).once(); |
| }); |
| } |
| |
| void test_removeNode() { |
| String name = '42.index'; |
| nodeManager.removeNode(name); |
| verify(fileManager.delete(name)).once(); |
| } |
| |
| IndexableElement _mockElement() { |
| int id1 = nextElementId++; |
| int id2 = nextElementId++; |
| int id3 = nextElementId++; |
| Element element = new MockElement(); |
| IndexableObject indexable = new IndexableElement(element); |
| when(elementCodec.encode1(indexable)).thenReturn(id1); |
| when(elementCodec.encode2(indexable)).thenReturn(id2); |
| when(elementCodec.encode3(indexable)).thenReturn(id3); |
| when(elementCodec.decode(context, id1, id2, id3)).thenReturn(indexable); |
| return indexable; |
| } |
| } |
| |
| @reflectiveTest |
| class _IndexNodeTest { |
| AnalysisContext context = new MockAnalysisContext('context'); |
| ElementCodec elementCodec = new MockElementCodec(); |
| int nextElementId = 0; |
| IndexNode node; |
| RelationshipCodec relationshipCodec; |
| StringCodec stringCodec = new StringCodec(); |
| |
| void setUp() { |
| relationshipCodec = new RelationshipCodec(stringCodec); |
| node = new IndexNode(context, elementCodec, relationshipCodec); |
| } |
| |
| void test_getContext() { |
| expect(node.context, context); |
| } |
| |
| void test_recordRelationship() { |
| IndexableElement elementA = _mockElement(); |
| IndexableElement elementB = _mockElement(); |
| IndexableElement elementC = _mockElement(); |
| RelationshipImpl relationship = |
| RelationshipImpl.getRelationship('my-relationship'); |
| LocationImpl locationA = new LocationImpl(elementB, 1, 2); |
| LocationImpl locationB = new LocationImpl(elementC, 10, 20); |
| // empty initially |
| expect(node.locationCount, 0); |
| // record |
| node.recordRelationship(elementA, relationship, locationA); |
| expect(node.locationCount, 1); |
| node.recordRelationship(elementA, relationship, locationB); |
| expect(node.locationCount, 2); |
| // get relations |
| expect(node.getRelationships(elementB, relationship), isEmpty); |
| { |
| List<LocationImpl> locations = |
| node.getRelationships(elementA, relationship); |
| expect(locations, hasLength(2)); |
| _assertHasLocation(locations, null, 1, 2); |
| _assertHasLocation(locations, null, 10, 20); |
| } |
| // verify relations map |
| { |
| Map<RelationKeyData, List<LocationData>> relations = node.relations; |
| expect(relations, hasLength(1)); |
| List<LocationData> locations = relations.values.first; |
| expect(locations, hasLength(2)); |
| } |
| } |
| |
| void test_setRelations() { |
| IndexableElement elementA = _mockElement(); |
| IndexableElement elementB = _mockElement(); |
| IndexableElement elementC = _mockElement(); |
| RelationshipImpl relationship = |
| RelationshipImpl.getRelationship('my-relationship'); |
| // record |
| { |
| int relationshipId = relationshipCodec.encode(relationship); |
| RelationKeyData key = |
| new RelationKeyData.forData(0, 1, 2, relationshipId); |
| List<LocationData> locations = [ |
| new LocationData.forData(3, 4, 5, 1, 10, 2), |
| new LocationData.forData(6, 7, 8, 2, 20, 3) |
| ]; |
| node.relations = {key: locations}; |
| } |
| // request |
| List<LocationImpl> locations = |
| node.getRelationships(elementA, relationship); |
| expect(locations, hasLength(2)); |
| _assertHasLocation(locations, elementB, 1, 10); |
| _assertHasLocationQ(locations, elementC, 2, 20); |
| } |
| |
| IndexableElement _mockElement() { |
| int id1 = nextElementId++; |
| int id2 = nextElementId++; |
| int id3 = nextElementId++; |
| Element element = new MockElement(); |
| IndexableElement indexable = new IndexableElement(element); |
| when(elementCodec.encode1(indexable)).thenReturn(id1); |
| when(elementCodec.encode2(indexable)).thenReturn(id2); |
| when(elementCodec.encode3(indexable)).thenReturn(id3); |
| when(elementCodec.decode(context, id1, id2, id3)).thenReturn(indexable); |
| return indexable; |
| } |
| } |
| |
| @reflectiveTest |
| class _LocationDataTest { |
| AnalysisContext context = new MockAnalysisContext('context'); |
| ElementCodec elementCodec = new MockElementCodec(); |
| StringCodec stringCodec = new StringCodec(); |
| |
| void test_newForData() { |
| Element element = new MockElement(); |
| IndexableElement indexable = new IndexableElement(element); |
| when(elementCodec.decode(context, 11, 12, 13)).thenReturn(indexable); |
| LocationData locationData = new LocationData.forData(11, 12, 13, 1, 2, 0); |
| LocationImpl location = locationData.getLocation(context, elementCodec); |
| expect(location.indexable, indexable); |
| expect(location.offset, 1); |
| expect(location.length, 2); |
| expect(location.isQualified, isFalse); |
| expect(location.isResolved, isFalse); |
| } |
| |
| void test_newForObject() { |
| // prepare Element |
| Element element = new MockElement(); |
| IndexableElement indexable = new IndexableElement(element); |
| when(elementCodec.encode1(indexable)).thenReturn(11); |
| when(elementCodec.encode2(indexable)).thenReturn(12); |
| when(elementCodec.encode3(indexable)).thenReturn(13); |
| when(elementCodec.decode(context, 11, 12, 13)).thenReturn(indexable); |
| // create |
| LocationImpl location = new LocationImpl(indexable, 1, 2); |
| LocationData locationData = |
| new LocationData.forObject(elementCodec, location); |
| // touch 'hashCode' |
| locationData.hashCode; |
| // == |
| expect( |
| locationData == new LocationData.forData(11, 12, 13, 1, 2, 2), isTrue); |
| // getLocation() |
| { |
| LocationImpl newLocation = |
| locationData.getLocation(context, elementCodec); |
| expect(newLocation.indexable, indexable); |
| expect(newLocation.offset, 1); |
| expect(newLocation.length, 2); |
| } |
| // no Element - no Location |
| { |
| when(elementCodec.decode(context, 11, 12, 13)).thenReturn(null); |
| LocationImpl newLocation = |
| locationData.getLocation(context, elementCodec); |
| expect(newLocation, isNull); |
| } |
| } |
| } |
| |
| /** |
| * [LocationImpl] has no [==] and [hashCode], so to compare locations by value we |
| * need to wrap them into such object. |
| */ |
| class _LocationEqualsWrapper { |
| final LocationImpl location; |
| |
| _LocationEqualsWrapper(this.location); |
| |
| @override |
| int get hashCode { |
| return 31 * (31 * location.indexable.hashCode + location.offset) + |
| location.length; |
| } |
| |
| @override |
| bool operator ==(Object other) { |
| if (other is _LocationEqualsWrapper) { |
| return other.location.offset == location.offset && |
| other.location.length == location.length && |
| other.location.indexable == location.indexable; |
| } |
| return false; |
| } |
| } |
| |
| class _MockFileManager extends TypedMock implements FileManager { |
| noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); |
| } |
| |
| class _MockIndexNode extends TypedMock implements IndexNode { |
| noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); |
| } |
| |
| @reflectiveTest |
| class _RelationKeyDataTest { |
| AnalysisContext context = new MockAnalysisContext('context'); |
| ElementCodec elementCodec = new MockElementCodec(); |
| RelationshipCodec relationshipCodec = new MockRelationshipCodec(); |
| StringCodec stringCodec = new StringCodec(); |
| |
| void test_newFromData() { |
| RelationKeyData keyData = new RelationKeyData.forData(11, 12, 13, 2); |
| // equals |
| expect(keyData == this, isFalse); |
| expect(keyData == new RelationKeyData.forData(11, 12, 13, 20), isFalse); |
| expect(keyData == keyData, isTrue); |
| expect(keyData == new RelationKeyData.forData(11, 12, 13, 2), isTrue); |
| } |
| |
| void test_newFromObjects() { |
| // prepare Element |
| IndexableElement indexable; |
| { |
| Element element = new MockElement(); |
| indexable = new IndexableElement(element); |
| ElementLocation location = new ElementLocationImpl.con3(['foo', 'bar']); |
| when(element.location).thenReturn(location); |
| when(context.getElement(location)).thenReturn(indexable); |
| when(elementCodec.encode1(indexable)).thenReturn(11); |
| when(elementCodec.encode2(indexable)).thenReturn(12); |
| when(elementCodec.encode3(indexable)).thenReturn(13); |
| } |
| // prepare relationship |
| RelationshipImpl relationship = |
| RelationshipImpl.getRelationship('my-relationship'); |
| int relationshipId = 1; |
| when(relationshipCodec.encode(relationship)).thenReturn(relationshipId); |
| // create RelationKeyData |
| RelationKeyData keyData = new RelationKeyData.forObject( |
| elementCodec, relationshipCodec, indexable, relationship); |
| // touch |
| keyData.hashCode; |
| // equals |
| expect(keyData == this, isFalse); |
| expect(keyData == new RelationKeyData.forData(11, 12, 13, 20), isFalse); |
| expect(keyData == keyData, isTrue); |
| expect(keyData == new RelationKeyData.forData(11, 12, 13, relationshipId), |
| isTrue); |
| } |
| } |
| |
| @reflectiveTest |
| class _SplitIndexStoreTest { |
| AnalysisContext contextA = new MockAnalysisContext('contextA'); |
| AnalysisContext contextB = new MockAnalysisContext('contextB'); |
| AnalysisContext contextC = new MockAnalysisContext('contextC'); |
| |
| Element elementA = new MockElement('elementA'); |
| Element elementB = new MockElement('elementB'); |
| Element elementC = new MockElement('elementC'); |
| Element elementD = new MockElement('elementD'); |
| |
| IndexableElement indexableA; |
| IndexableElement indexableB; |
| IndexableElement indexableC; |
| IndexableElement indexableD; |
| |
| Source librarySource = new MockSource('librarySource'); |
| CompilationUnitElement libraryUnitElement = new MockCompilationUnitElement(); |
| LibraryElement libraryElement = new MockLibraryElement(); |
| |
| Source librarySourceB = new MockSource('librarySourceB'); |
| LibraryElement libraryElementB = new MockLibraryElement(); |
| CompilationUnitElement libraryUnitElementB = new MockCompilationUnitElement(); |
| |
| ElementCodec elementCodec = new MockElementCodec(); |
| MemoryNodeManager nodeManager = new MemoryNodeManager(); |
| RelationshipImpl relationship = |
| RelationshipImpl.getRelationship('test-relationship'); |
| Source sourceA = new MockSource('sourceA'); |
| Source sourceB = new MockSource('sourceB'); |
| Source sourceC = new MockSource('sourceC'); |
| Source sourceD = new MockSource('sourceD'); |
| SplitIndexStore store; |
| CompilationUnitElement unitElementA = new MockCompilationUnitElement(); |
| CompilationUnitElement unitElementB = new MockCompilationUnitElement(); |
| CompilationUnitElement unitElementC = new MockCompilationUnitElement(); |
| CompilationUnitElement unitElementD = new MockCompilationUnitElement(); |
| |
| void setUp() { |
| indexableA = new IndexableElement(elementA); |
| indexableB = new IndexableElement(elementB); |
| indexableC = new IndexableElement(elementC); |
| indexableD = new IndexableElement(elementD); |
| |
| nodeManager.elementCodec = elementCodec; |
| store = new SplitIndexStore(nodeManager, |
| <IndexObjectManager>[new DartUnitIndexObjectManager()]); |
| when(elementCodec.encode1(indexableA)).thenReturn(11); |
| when(elementCodec.encode2(indexableA)).thenReturn(12); |
| when(elementCodec.encode3(indexableA)).thenReturn(13); |
| when(elementCodec.encode1(indexableB)).thenReturn(21); |
| when(elementCodec.encode2(indexableB)).thenReturn(22); |
| when(elementCodec.encode3(indexableB)).thenReturn(23); |
| when(elementCodec.encode1(indexableC)).thenReturn(31); |
| when(elementCodec.encode2(indexableC)).thenReturn(32); |
| when(elementCodec.encode3(indexableC)).thenReturn(33); |
| when(elementCodec.encode1(indexableD)).thenReturn(41); |
| when(elementCodec.encode2(indexableD)).thenReturn(42); |
| when(elementCodec.encode3(indexableD)).thenReturn(43); |
| when(elementCodec.decode(contextA, 11, 12, 13)).thenReturn(indexableA); |
| when(elementCodec.decode(contextA, 21, 22, 23)).thenReturn(indexableB); |
| when(elementCodec.decode(contextA, 31, 32, 33)).thenReturn(indexableC); |
| when(elementCodec.decode(contextA, 41, 42, 43)).thenReturn(indexableD); |
| when(contextA.isDisposed).thenReturn(false); |
| when(contextB.isDisposed).thenReturn(false); |
| when(contextC.isDisposed).thenReturn(false); |
| when(sourceA.fullName).thenReturn('/home/user/sourceA.dart'); |
| when(sourceB.fullName).thenReturn('/home/user/sourceB.dart'); |
| when(sourceC.fullName).thenReturn('/home/user/sourceC.dart'); |
| when(sourceD.fullName).thenReturn('/home/user/sourceD.dart'); |
| when(elementA.context).thenReturn(contextA); |
| when(elementB.context).thenReturn(contextA); |
| when(elementC.context).thenReturn(contextA); |
| when(elementD.context).thenReturn(contextA); |
| when(elementA.enclosingElement).thenReturn(unitElementA); |
| when(elementB.enclosingElement).thenReturn(unitElementB); |
| when(elementC.enclosingElement).thenReturn(unitElementC); |
| when(elementD.enclosingElement).thenReturn(unitElementD); |
| when(elementA.source).thenReturn(sourceA); |
| when(elementB.source).thenReturn(sourceB); |
| when(elementC.source).thenReturn(sourceC); |
| when(elementD.source).thenReturn(sourceD); |
| when(elementA.library).thenReturn(libraryElement); |
| when(elementB.library).thenReturn(libraryElement); |
| when(elementC.library).thenReturn(libraryElement); |
| when(elementD.library).thenReturn(libraryElement); |
| when(unitElementA.source).thenReturn(sourceA); |
| when(unitElementB.source).thenReturn(sourceB); |
| when(unitElementC.source).thenReturn(sourceC); |
| when(unitElementD.source).thenReturn(sourceD); |
| when(unitElementA.library).thenReturn(libraryElement); |
| when(unitElementB.library).thenReturn(libraryElement); |
| when(unitElementC.library).thenReturn(libraryElement); |
| when(unitElementD.library).thenReturn(libraryElement); |
| // library |
| when(librarySource.fullName).thenReturn('/home/user/librarySource.dart'); |
| when(libraryUnitElement.library).thenReturn(libraryElement); |
| when(libraryUnitElement.source).thenReturn(librarySource); |
| when(libraryElement.source).thenReturn(librarySource); |
| when(libraryElement.definingCompilationUnit).thenReturn(libraryUnitElement); |
| // library B |
| when(librarySourceB.fullName).thenReturn('/home/user/librarySource.dart'); |
| when(libraryUnitElementB.library).thenReturn(libraryElementB); |
| when(libraryUnitElementB.source).thenReturn(librarySourceB); |
| when(libraryElementB.source).thenReturn(librarySourceB); |
| when(libraryElementB.definingCompilationUnit) |
| .thenReturn(libraryUnitElementB); |
| } |
| |
| void test_aboutToIndexDart_disposedContext() { |
| when(contextA.isDisposed).thenReturn(true); |
| expect(store.aboutToIndex(contextA, unitElementA), isFalse); |
| } |
| |
| Future test_aboutToIndexDart_library_first() { |
| when(libraryElement.parts) |
| .thenReturn(<CompilationUnitElement>[unitElementA, unitElementB]); |
| { |
| store.aboutToIndex(contextA, libraryUnitElement); |
| store.doneIndex(); |
| } |
| return store |
| .getRelationships(indexableA, relationship) |
| .then((List<LocationImpl> locations) { |
| assertLocations(locations, []); |
| }); |
| } |
| |
| test_aboutToIndexDart_library_secondWithoutOneUnit() { |
| LocationImpl locationA = mockLocation(indexableA); |
| LocationImpl locationB = mockLocation(indexableB); |
| { |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordRelationship(indexableA, relationship, locationA); |
| store.doneIndex(); |
| } |
| { |
| store.aboutToIndex(contextA, unitElementB); |
| store.recordRelationship(indexableA, relationship, locationB); |
| store.doneIndex(); |
| } |
| // "A" and "B" locations |
| return store |
| .getRelationships(indexableA, relationship) |
| .then((List<LocationImpl> locations) { |
| assertLocations(locations, [locationA, locationB]); |
| // apply "libraryUnitElement", only with "B" |
| when(libraryElement.parts).thenReturn([unitElementB]); |
| { |
| store.aboutToIndex(contextA, libraryUnitElement); |
| store.doneIndex(); |
| } |
| }).then((_) { |
| return store |
| .getRelationships(indexableA, relationship) |
| .then((List<LocationImpl> locations) { |
| assertLocations(locations, [locationB]); |
| }); |
| }); |
| } |
| |
| void test_aboutToIndexDart_nullLibraryElement() { |
| when(unitElementA.library).thenReturn(null); |
| expect(store.aboutToIndex(contextA, unitElementA), isFalse); |
| } |
| |
| void test_aboutToIndexDart_nullLibraryUnitElement() { |
| when(libraryElement.definingCompilationUnit).thenReturn(null); |
| expect(store.aboutToIndex(contextA, unitElementA), isFalse); |
| } |
| |
| void test_aboutToIndexDart_nullUnitElement() { |
| expect(store.aboutToIndex(contextA, null), isFalse); |
| } |
| |
| test_cancelIndexDart() { |
| LocationImpl locationA = mockLocation(indexableA); |
| LocationImpl locationB = mockLocation(indexableA); |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordRelationship(indexableA, relationship, locationA); |
| store.recordRelationship(indexableA, relationship, locationB); |
| store.recordTopLevelDeclaration(elementA); |
| store.cancelIndex(); |
| return store |
| .getRelationships(indexableA, relationship) |
| .then((List<LocationImpl> locations) { |
| assertLocations(locations, []); |
| expect(store.getTopLevelDeclarations((name) => true), isEmpty); |
| }); |
| } |
| |
| void test_clear() { |
| LocationImpl locationA = mockLocation(indexableA); |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordRelationship(indexableA, relationship, locationA); |
| store.doneIndex(); |
| expect(nodeManager.isEmpty(), isFalse); |
| // clear |
| store.clear(); |
| expect(nodeManager.isEmpty(), isTrue); |
| } |
| |
| test_getRelationships_empty() { |
| return store |
| .getRelationships(indexableA, relationship) |
| .then((List<LocationImpl> locations) { |
| expect(locations, isEmpty); |
| }); |
| } |
| |
| void test_getStatistics() { |
| // empty initially |
| { |
| String statistics = store.statistics; |
| expect(statistics, contains('0 locations')); |
| expect(statistics, contains('0 sources')); |
| } |
| // add 2 locations |
| LocationImpl locationA = mockLocation(indexableA); |
| LocationImpl locationB = mockLocation(indexableB); |
| { |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordRelationship(indexableA, relationship, locationA); |
| store.doneIndex(); |
| } |
| { |
| store.aboutToIndex(contextA, unitElementB); |
| store.recordRelationship(indexableA, relationship, locationB); |
| store.doneIndex(); |
| } |
| { |
| String statistics = store.statistics; |
| expect(statistics, contains('2 locations')); |
| expect(statistics, contains('3 sources')); |
| } |
| } |
| |
| void test_recordRelationship_multiplyDefinedElement() { |
| Element multiplyElement = |
| new MultiplyDefinedElementImpl(contextA, <Element>[elementA, elementB]); |
| LocationImpl location = mockLocation(indexableA); |
| store.recordRelationship( |
| new IndexableElement(multiplyElement), relationship, location); |
| store.doneIndex(); |
| expect(nodeManager.isEmpty(), isTrue); |
| } |
| |
| void test_recordRelationship_nullElement() { |
| LocationImpl locationA = mockLocation(indexableA); |
| store.recordRelationship(null, relationship, locationA); |
| store.doneIndex(); |
| expect(nodeManager.isEmpty(), isTrue); |
| } |
| |
| void test_recordRelationship_nullLocation() { |
| store.recordRelationship(indexableA, relationship, null); |
| store.doneIndex(); |
| expect(nodeManager.isEmpty(), isTrue); |
| } |
| |
| test_recordRelationship_oneElement_twoNodes() { |
| LocationImpl locationA = mockLocation(indexableA); |
| LocationImpl locationB = mockLocation(indexableB); |
| { |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordRelationship(indexableA, relationship, locationA); |
| store.doneIndex(); |
| } |
| { |
| store.aboutToIndex(contextA, unitElementB); |
| store.recordRelationship(indexableA, relationship, locationB); |
| store.doneIndex(); |
| } |
| return store |
| .getRelationships(indexableA, relationship) |
| .then((List<LocationImpl> locations) { |
| assertLocations(locations, [locationA, locationB]); |
| }); |
| } |
| |
| test_recordRelationship_oneLocation() { |
| LocationImpl locationA = mockLocation(indexableA); |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordRelationship(indexableA, relationship, locationA); |
| store.doneIndex(); |
| return store |
| .getRelationships(indexableA, relationship) |
| .then((List<LocationImpl> locations) { |
| assertLocations(locations, [locationA]); |
| }); |
| } |
| |
| test_recordRelationship_twoLocations() { |
| LocationImpl locationA = mockLocation(indexableA); |
| LocationImpl locationB = mockLocation(indexableA); |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordRelationship(indexableA, relationship, locationA); |
| store.recordRelationship(indexableA, relationship, locationB); |
| store.doneIndex(); |
| return store |
| .getRelationships(indexableA, relationship) |
| .then((List<LocationImpl> locations) { |
| assertLocations(locations, [locationA, locationB]); |
| }); |
| } |
| |
| test_removeContext() { |
| LocationImpl locationA = mockLocation(indexableA); |
| LocationImpl locationB = mockLocation(indexableB); |
| { |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordRelationship(indexableA, relationship, locationA); |
| store.doneIndex(); |
| } |
| { |
| store.aboutToIndex(contextA, unitElementB); |
| store.recordRelationship(indexableA, relationship, locationB); |
| store.doneIndex(); |
| } |
| // "A" and "B" locations |
| return store |
| .getRelationships(indexableA, relationship) |
| .then((List<LocationImpl> locations) { |
| assertLocations(locations, [locationA, locationB]); |
| // remove "A" context |
| store.removeContext(contextA); |
| }).then((_) { |
| return store |
| .getRelationships(indexableA, relationship) |
| .then((List<LocationImpl> locations) { |
| assertLocations(locations, []); |
| }); |
| }); |
| } |
| |
| void test_removeContext_nullContext() { |
| store.removeContext(null); |
| } |
| |
| test_removeSource_library() async { |
| when(elementB.library).thenReturn(libraryElementB); |
| when(unitElementB.library).thenReturn(libraryElementB); |
| LocationImpl locationA = mockLocation(indexableA); |
| LocationImpl locationB = mockLocation(indexableB); |
| LocationImpl locationC = mockLocation(indexableC); |
| { |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordRelationship(indexableD, relationship, locationA); |
| store.doneIndex(); |
| } |
| { |
| store.aboutToIndex(contextA, unitElementB); |
| store.recordRelationship(indexableD, relationship, locationB); |
| store.doneIndex(); |
| } |
| { |
| store.aboutToIndex(contextA, unitElementC); |
| store.recordRelationship(indexableD, relationship, locationC); |
| store.doneIndex(); |
| } |
| // "A", "B" and "C" locations |
| { |
| var locations = await store.getRelationships(indexableD, relationship); |
| assertLocations(locations, [locationA, locationB, locationC]); |
| } |
| // remove "librarySource" |
| store.removeSource(contextA, librarySource); |
| // only "B" location, which is in "librarySourceB" |
| { |
| var locations = await store.getRelationships(indexableD, relationship); |
| assertLocations(locations, [locationB]); |
| } |
| } |
| |
| void test_removeSource_nullContext() { |
| store.removeSource(null, sourceA); |
| } |
| |
| test_removeSource_unit() { |
| LocationImpl locationA = mockLocation(indexableA); |
| LocationImpl locationB = mockLocation(indexableB); |
| LocationImpl locationC = mockLocation(indexableC); |
| { |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordRelationship(indexableA, relationship, locationA); |
| store.doneIndex(); |
| } |
| { |
| store.aboutToIndex(contextA, unitElementB); |
| store.recordRelationship(indexableA, relationship, locationB); |
| store.doneIndex(); |
| } |
| { |
| store.aboutToIndex(contextA, unitElementC); |
| store.recordRelationship(indexableA, relationship, locationC); |
| store.doneIndex(); |
| } |
| // "A", "B" and "C" locations |
| return store |
| .getRelationships(indexableA, relationship) |
| .then((List<LocationImpl> locations) { |
| assertLocations(locations, [locationA, locationB, locationC]); |
| }).then((_) { |
| // remove "A" source |
| store.removeSource(contextA, sourceA); |
| return store |
| .getRelationships(indexableA, relationship) |
| .then((List<LocationImpl> locations) { |
| assertLocations(locations, [locationB, locationC]); |
| }); |
| }); |
| } |
| |
| test_removeSources_library() { |
| LocationImpl locationA = mockLocation(indexableA); |
| LocationImpl locationB = mockLocation(indexableB); |
| { |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordRelationship(indexableA, relationship, locationA); |
| store.doneIndex(); |
| } |
| { |
| store.aboutToIndex(contextA, unitElementB); |
| store.recordRelationship(indexableA, relationship, locationB); |
| store.doneIndex(); |
| } |
| // "A" and "B" locations |
| return store |
| .getRelationships(indexableA, relationship) |
| .then((List<LocationImpl> locations) { |
| assertLocations(locations, [locationA, locationB]); |
| }).then((_) { |
| // remove "librarySource" |
| store.removeSources(contextA, new SingleSourceContainer(librarySource)); |
| return store |
| .getRelationships(indexableA, relationship) |
| .then((List<LocationImpl> locations) { |
| assertLocations(locations, []); |
| }); |
| }); |
| } |
| |
| void test_removeSources_nullContext() { |
| store.removeSources(null, null); |
| } |
| |
| void test_removeSources_unit() { |
| { |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordTopLevelDeclaration(elementA); |
| store.doneIndex(); |
| } |
| { |
| store.aboutToIndex(contextA, unitElementB); |
| store.recordTopLevelDeclaration(elementB); |
| store.doneIndex(); |
| } |
| { |
| store.aboutToIndex(contextA, unitElementC); |
| store.recordTopLevelDeclaration(elementC); |
| store.doneIndex(); |
| } |
| // A, B, C elements |
| { |
| List<Element> elements = store.getTopLevelDeclarations(anyName); |
| expect(elements, unorderedEquals([elementA, elementB, elementC])); |
| } |
| // remove "A" source |
| store.removeSources(contextA, new SingleSourceContainer(sourceA)); |
| store.removeSource(contextA, sourceA); |
| { |
| List<Element> elements = store.getTopLevelDeclarations(anyName); |
| expect(elements, unorderedEquals([elementB, elementC])); |
| } |
| } |
| |
| void test_universe_aboutToIndex() { |
| when(elementCodec.decode(contextA, 11, 12, 13)) |
| .thenReturn(new IndexableElement(elementA)); |
| when(elementCodec.decode(contextB, 21, 22, 23)) |
| .thenReturn(new IndexableElement(elementB)); |
| { |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordTopLevelDeclaration(elementA); |
| store.doneIndex(); |
| } |
| { |
| store.aboutToIndex(contextB, unitElementB); |
| store.recordTopLevelDeclaration(elementB); |
| store.doneIndex(); |
| } |
| // elementA, elementB |
| { |
| List<Element> elements = store.getTopLevelDeclarations(anyName); |
| expect(elements, unorderedEquals([elementA, elementB])); |
| } |
| // re-index "unitElementA" |
| { |
| store.aboutToIndex(contextA, unitElementA); |
| store.doneIndex(); |
| } |
| { |
| List<Element> elements = store.getTopLevelDeclarations(anyName); |
| expect(elements, unorderedEquals([elementB])); |
| } |
| } |
| |
| void test_universe_clear() { |
| when(elementCodec.decode(contextA, 11, 12, 13)) |
| .thenReturn(new IndexableElement(elementA)); |
| when(elementCodec.decode(contextB, 21, 22, 23)) |
| .thenReturn(new IndexableElement(elementB)); |
| { |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordTopLevelDeclaration(elementA); |
| store.doneIndex(); |
| } |
| { |
| store.aboutToIndex(contextB, unitElementB); |
| store.recordTopLevelDeclaration(elementB); |
| store.doneIndex(); |
| } |
| // elementA, elementB |
| { |
| List<Element> elements = store.getTopLevelDeclarations(anyName); |
| expect(elements, unorderedEquals([elementA, elementB])); |
| } |
| // clear |
| store.clear(); |
| { |
| List<Element> elements = store.getTopLevelDeclarations(anyName); |
| expect(elements, isEmpty); |
| } |
| } |
| |
| void test_universe_removeContext() { |
| when(elementCodec.decode(contextA, 11, 12, 13)) |
| .thenReturn(new IndexableElement(elementA)); |
| when(elementCodec.decode(contextB, 21, 22, 23)) |
| .thenReturn(new IndexableElement(elementB)); |
| { |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordTopLevelDeclaration(elementA); |
| store.doneIndex(); |
| } |
| { |
| store.aboutToIndex(contextB, unitElementB); |
| store.recordTopLevelDeclaration(elementB); |
| store.doneIndex(); |
| } |
| // elementA, elementB |
| { |
| List<Element> elements = store.getTopLevelDeclarations(anyName); |
| expect(elements, unorderedEquals([elementA, elementB])); |
| } |
| // remove "contextA" |
| store.removeContext(contextA); |
| { |
| List<Element> elements = store.getTopLevelDeclarations(anyName); |
| expect(elements, unorderedEquals([elementB])); |
| } |
| } |
| |
| void test_universe_removeSource() { |
| when(elementCodec.decode(contextA, 11, 12, 13)) |
| .thenReturn(new IndexableElement(elementA)); |
| when(elementCodec.decode(contextB, 21, 22, 23)) |
| .thenReturn(new IndexableElement(elementB)); |
| { |
| store.aboutToIndex(contextA, unitElementA); |
| store.recordTopLevelDeclaration(elementA); |
| store.doneIndex(); |
| } |
| { |
| store.aboutToIndex(contextB, unitElementB); |
| store.recordTopLevelDeclaration(elementB); |
| store.doneIndex(); |
| } |
| // elementA, elementB |
| { |
| List<Element> elements = store.getTopLevelDeclarations(anyName); |
| expect(elements, unorderedEquals([elementA, elementB])); |
| } |
| // remove "sourceA" |
| store.removeSource(contextA, sourceA); |
| { |
| List<Element> elements = store.getTopLevelDeclarations(anyName); |
| expect(elements, unorderedEquals([elementB])); |
| } |
| } |
| |
| static bool anyName(String name) => true; |
| |
| /** |
| * Asserts that the [actual] locations have all the [expected] locations and |
| * only them. |
| */ |
| static void assertLocations( |
| List<LocationImpl> actual, List<LocationImpl> expected) { |
| List<_LocationEqualsWrapper> actualWrappers = wrapLocations(actual); |
| List<_LocationEqualsWrapper> expectedWrappers = wrapLocations(expected); |
| expect(actualWrappers, unorderedEquals(expectedWrappers)); |
| } |
| |
| /** |
| * @return the new [LocationImpl] mock. |
| */ |
| static LocationImpl mockLocation(IndexableElement indexable) { |
| LocationImpl location = new MockLocation(); |
| when(location.indexable).thenReturn(indexable); |
| when(location.offset).thenReturn(0); |
| when(location.length).thenReturn(0); |
| when(location.isQualified).thenReturn(true); |
| when(location.isResolved).thenReturn(true); |
| return location; |
| } |
| |
| /** |
| * Wraps the given locations into [LocationEqualsWrapper]. |
| */ |
| static List<_LocationEqualsWrapper> wrapLocations( |
| List<LocationImpl> locations) { |
| List<_LocationEqualsWrapper> wrappers = <_LocationEqualsWrapper>[]; |
| for (LocationImpl location in locations) { |
| wrappers.add(new _LocationEqualsWrapper(location)); |
| } |
| return wrappers; |
| } |
| } |