initial implementation of delta-based index cache
This commit is contained in:
@@ -0,0 +1,589 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.index.cache;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.DefaultValues;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
|
||||
import org.springframework.ide.vscode.commons.util.UriUtil;
|
||||
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class IndexCacheOnDiscDeltaBased implements IndexCache {
|
||||
|
||||
private final File cacheDirectory;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(IndexCacheOnDiscDeltaBased.class);
|
||||
|
||||
public IndexCacheOnDiscDeltaBased(File cacheDirectory) {
|
||||
this.cacheDirectory = cacheDirectory;
|
||||
|
||||
if (!this.cacheDirectory.exists()) {
|
||||
this.cacheDirectory.mkdirs();
|
||||
}
|
||||
|
||||
if (!this.cacheDirectory.exists()) {
|
||||
log.warn("symbol cache directory does not exist and cannot be created: " + this.cacheDirectory.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IndexCacheable> void store(IndexCacheKey cacheKey, String[] files, List<T> elements, Multimap<String, String> dependencies, Class<T> type) {
|
||||
if (dependencies == null) {
|
||||
dependencies = ImmutableMultimap.of();
|
||||
}
|
||||
|
||||
SortedMap<String, Long> timestampedFiles = new TreeMap<>();
|
||||
|
||||
timestampedFiles = Arrays.stream(files)
|
||||
.filter(file -> new File(file).exists())
|
||||
.collect(Collectors.toMap(file -> file, file -> {
|
||||
try {
|
||||
return Files.getLastModifiedTime(new File(file).toPath()).toMillis();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}, (v1,v2) -> { throw new RuntimeException(String.format("Duplicate key for values %s and %s", v1, v2));}, TreeMap::new));
|
||||
|
||||
IndexCacheStore<T> store = new IndexCacheStore<T>(timestampedFiles, elements, dependencies.asMap(), type);
|
||||
persist(cacheKey, new DeltaSnapshot<T>(store), false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends IndexCacheable> Pair<T[], Multimap<String, String>> retrieve(IndexCacheKey cacheKey, String[] files, Class<T> type) {
|
||||
File cacheStore = new File(cacheDirectory, cacheKey.toString() + ".json");
|
||||
if (cacheStore.exists()) {
|
||||
|
||||
try (JsonReader reader = new JsonReader(new FileReader(cacheStore))) {
|
||||
IndexCacheStore<T> store = retrieveStoreFromIncrementalStorage(cacheKey, type);
|
||||
|
||||
SortedMap<String, Long> timestampedFiles = Arrays.stream(files)
|
||||
.filter(file -> new File(file).exists())
|
||||
.collect(Collectors.toMap(file -> file, file -> {
|
||||
try {
|
||||
return Files.getLastModifiedTime(new File(file).toPath()).toMillis();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}, (v1,v2) -> { throw new RuntimeException(String.format("Duplicate key for values %s and %s", v1, v2));}, TreeMap::new));
|
||||
|
||||
if (isFileMatch(timestampedFiles, store.getTimestampedFiles())) {
|
||||
|
||||
List<T> symbols = store.getSymbols();
|
||||
|
||||
Map<String, Collection<String>> storedDependencies = store.getDependencies();
|
||||
Multimap<String, String> dependencies = MultimapBuilder.hashKeys().hashSetValues().build();
|
||||
|
||||
if (storedDependencies!=null && !storedDependencies.isEmpty()) {
|
||||
for (Entry<String, Collection<String>> entry : storedDependencies.entrySet()) {
|
||||
dependencies.replaceValues(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return Pair.of(
|
||||
(T[]) symbols.toArray((T[]) Array.newInstance(type, symbols.size())),
|
||||
MultimapBuilder.hashKeys().hashSetValues().build(dependencies)
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error reading cached symbols", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IndexCacheable> void removeFile(IndexCacheKey cacheKey, String file, Class<T> type) {
|
||||
persist(cacheKey, new DeltaDelete<T>(new String[] {file}), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IndexCacheable> void removeFiles(IndexCacheKey cacheKey, String[] files, Class<T> type) {
|
||||
persist(cacheKey, new DeltaDelete<T>(files), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(IndexCacheKey cacheKey) {
|
||||
File cacheStore = new File(cacheDirectory, cacheKey.toString() + ".json");
|
||||
if (cacheStore.exists()) {
|
||||
cacheStore.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IndexCacheable> void update(IndexCacheKey cacheKey, String file, long lastModified,
|
||||
List<T> generatedSymbols, Set<String> dependencies, Class<T> type) {
|
||||
if (dependencies == null) {
|
||||
dependencies = ImmutableSet.of();
|
||||
}
|
||||
|
||||
// creating and storing delta
|
||||
SortedMap<String, Long> timestampsDelta = new TreeMap<>();
|
||||
timestampsDelta.put(file, lastModified);
|
||||
|
||||
Map<String, Collection<String>> dependenciesDelta = new HashMap<>();
|
||||
dependenciesDelta.put(file, ImmutableSet.copyOf(dependencies));
|
||||
|
||||
IndexCacheStore<T> deltaStore = new IndexCacheStore<T>(timestampsDelta, generatedSymbols, dependenciesDelta, type);
|
||||
persist(cacheKey, new DeltaUpdate<T>(deltaStore), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IndexCacheable> void update(IndexCacheKey cacheKey, String[] files, long[] lastModified,
|
||||
List<T> generatedSymbols, Multimap<String, String> dependencies, Class<T> type) {
|
||||
if (dependencies == null) {
|
||||
dependencies = ImmutableMultimap.of();
|
||||
}
|
||||
|
||||
// creating and storing delta
|
||||
SortedMap<String, Long> timestampsDelta = new TreeMap<>();
|
||||
Map<String, Collection<String>> dependenciesDelta = new HashMap<>();
|
||||
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
timestampsDelta.put(files[i], lastModified[i]);
|
||||
dependenciesDelta.put(files[i], ImmutableSet.copyOf(dependencies.get(files[i])));
|
||||
}
|
||||
|
||||
IndexCacheStore<T> deltaStore = new IndexCacheStore<T>(timestampsDelta, generatedSymbols, dependenciesDelta, type);
|
||||
persist(cacheKey, new DeltaUpdate<T>(deltaStore), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getModificationTimestamp(IndexCacheKey cacheKey, String file) {
|
||||
// IndexCacheStore<? extends IndexCacheable> cacheStore = this.stores.get(cacheKey);
|
||||
//
|
||||
// if (cacheStore != null) {
|
||||
// Long result = cacheStore.getTimestampedFiles().get(file);
|
||||
// if (result != null) {
|
||||
// return result;
|
||||
// }
|
||||
// }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private boolean isFileMatch(SortedMap<String, Long> files1, SortedMap<String, Long> files2) {
|
||||
if (files1.size() != files2.size()) return false;
|
||||
|
||||
for (String file : files1.keySet()) {
|
||||
if (!files2.containsKey(file)) return false;
|
||||
if (!files1.get(file).equals(files2.get(file))) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void cleanupCache(IndexCacheKey cacheKey) {
|
||||
File[] cacheFiles = this.cacheDirectory.listFiles();
|
||||
|
||||
for (int i = 0; i < cacheFiles.length; i++) {
|
||||
String fileName = cacheFiles[i].getName();
|
||||
IndexCacheKey key = IndexCacheKey.parse(fileName);
|
||||
|
||||
if (key != null && !key.equals(cacheKey)
|
||||
&& key.getProject().equals(cacheKey.getProject())
|
||||
&& key.getIndexer().equals(cacheKey.getIndexer())
|
||||
&& key.getCategory().equals(cacheKey.getCategory())) {
|
||||
cacheFiles[i].delete();
|
||||
}
|
||||
// cleanup old cache files without category information (pre 4.19.1 release)
|
||||
else if (key != null && !key.equals(cacheKey)
|
||||
&& key.getProject().equals(cacheKey.getProject())
|
||||
&& key.getIndexer().equals(cacheKey.getIndexer())
|
||||
&& key.getCategory().equals("")) {
|
||||
cacheFiles[i].delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends IndexCacheable> void persist(IndexCacheKey cacheKey, DeltaElement<T> delta, boolean append) {
|
||||
DeltaStorage<T> deltaStorage = new DeltaStorage<T>(delta);
|
||||
|
||||
try (FileWriter writer = new FileWriter(new File(cacheDirectory, cacheKey.toString() + ".json"), append))
|
||||
{
|
||||
Gson gson = createGson();
|
||||
gson.toJson(deltaStorage, writer);
|
||||
|
||||
writer.write("\n");
|
||||
|
||||
cleanupCache(cacheKey);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("cannot write symbol cache", e);
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends IndexCacheable> IndexCacheStore<T> retrieveStoreFromIncrementalStorage(IndexCacheKey cacheKey, Class<T> type) {
|
||||
IndexCacheStore<T> store = new IndexCacheStore<>(new TreeMap<>(), new ArrayList<T>(), new HashMap<>(), type);
|
||||
|
||||
File cacheStore = new File(cacheDirectory, cacheKey.toString() + ".json");
|
||||
if (cacheStore.exists()) {
|
||||
|
||||
Gson gson = createGson();
|
||||
|
||||
|
||||
try (JsonReader reader = new JsonReader(new FileReader(cacheStore))) {
|
||||
|
||||
DeltaStorage<T> readElement;
|
||||
while ((readElement = gson.fromJson(reader, DeltaStorage.class)) != null) {
|
||||
DeltaElement<T> delta = readElement.storedElement;
|
||||
store = delta.apply(store);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error reading cached symbols", e);
|
||||
}
|
||||
}
|
||||
return store;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static Gson createGson() {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapter(DeltaStorage.class, new DeltaStorageAdapter())
|
||||
.registerTypeAdapter(SymbolAddOnInformation.class, new SymbolAddOnInformationAdapter())
|
||||
.registerTypeAdapter(Bean.class, new BeanJsonAdapter())
|
||||
.registerTypeAdapter(InjectionPoint.class, new InjectionPointJsonAdapter())
|
||||
.registerTypeAdapter(IndexCacheStore.class, new IndexCacheStoreAdapter())
|
||||
.create();
|
||||
}
|
||||
|
||||
private static record DeltaStorage<T extends IndexCacheable> (DeltaElement<T> storedElement) {}
|
||||
|
||||
private static interface DeltaElement<T extends IndexCacheable> {
|
||||
public IndexCacheStore<T> apply(IndexCacheStore<T> store);
|
||||
}
|
||||
|
||||
private static class DeltaDelete<T extends IndexCacheable> implements DeltaElement<T> {
|
||||
|
||||
private final String[] files;
|
||||
|
||||
public DeltaDelete(String[] files) {
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexCacheStore<T> apply(IndexCacheStore<T> store) {
|
||||
SortedMap<String, Long> timestampedFiles = store.getTimestampedFiles();
|
||||
Map<String, Collection<String>> changedDeps = store.getDependencies();
|
||||
|
||||
Set<String> docURIs = new HashSet<>();
|
||||
|
||||
for (String file : files) {
|
||||
String docURI = UriUtil.toUri(new File(file)).toASCIIString();
|
||||
docURIs.add(docURI);
|
||||
|
||||
timestampedFiles.remove(file);
|
||||
changedDeps.remove(file);
|
||||
}
|
||||
|
||||
List<T> symbols = store.getSymbols();
|
||||
for (Iterator<T> iterator = symbols.iterator(); iterator.hasNext();) {
|
||||
T t = iterator.next();
|
||||
|
||||
if (docURIs.contains(t.getDocURI())) {
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class DeltaUpdate<T extends IndexCacheable> implements DeltaElement<T> {
|
||||
|
||||
private IndexCacheStore<T> deltaStore;
|
||||
|
||||
public DeltaUpdate(IndexCacheStore<T> deltaStore) {
|
||||
this.deltaStore = deltaStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexCacheStore<T> apply(IndexCacheStore<T> store) {
|
||||
SortedMap<String, Long> deltaTimestamps = deltaStore.getTimestampedFiles();
|
||||
SortedMap<String, Long> storeTimestamps = store.getTimestampedFiles();
|
||||
|
||||
Map<String, Collection<String>> deltaDependencies = deltaStore.getDependencies();
|
||||
Map<String, Collection<String>> storeDependencies = store.getDependencies();
|
||||
|
||||
List<T> deltaSymbols = deltaStore.getSymbols();
|
||||
List<T> storeSymbols = store.getSymbols();
|
||||
|
||||
Set<String> allDocURIs = new HashSet<>();
|
||||
|
||||
for (Iterator<String> iterator = deltaTimestamps.keySet().iterator(); iterator.hasNext();) {
|
||||
String file = iterator.next();
|
||||
long timestamp = deltaTimestamps.get(file);
|
||||
|
||||
// update cache internal map of timestamps per file
|
||||
String docURI = UriUtil.toUri(new File(file)).toASCIIString();
|
||||
allDocURIs.add(docURI);
|
||||
|
||||
storeTimestamps.put(file, timestamp);
|
||||
|
||||
// update cache internal map of dependencies per file
|
||||
Collection<String> updatedDependencies = deltaDependencies.get(file);
|
||||
if (updatedDependencies == null || updatedDependencies.isEmpty()) {
|
||||
storeDependencies.remove(file);
|
||||
} else {
|
||||
storeDependencies.put(file, ImmutableSet.copyOf(updatedDependencies));
|
||||
}
|
||||
|
||||
// update cache internal list of cached symbols (by removing old ones and adding all new ones)
|
||||
for (Iterator<T> symbols = storeSymbols.iterator(); symbols.hasNext();) {
|
||||
if (allDocURIs.contains(symbols.next().getDocURI())) {
|
||||
symbols.remove();
|
||||
}
|
||||
}
|
||||
|
||||
storeSymbols.addAll(deltaSymbols);
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class DeltaSnapshot<T extends IndexCacheable> implements DeltaElement<T> {
|
||||
|
||||
private IndexCacheStore<T> store;
|
||||
|
||||
public DeltaSnapshot(IndexCacheStore<T> store) {
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexCacheStore<T> apply(IndexCacheStore<T> store) {
|
||||
return this.store;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* internal storage structure
|
||||
*/
|
||||
private static class IndexCacheStore<T extends IndexCacheable> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final String elementType;
|
||||
|
||||
private final SortedMap<String, Long> timestampedFiles;
|
||||
private final List<T> elements;
|
||||
private final Map<String, Collection<String>> dependencies;
|
||||
|
||||
public IndexCacheStore(SortedMap<String, Long> timestampedFiles, List<T> elements, Map<String, Collection<String>> dependencies, Class<T> elementType) {
|
||||
this.timestampedFiles = timestampedFiles;
|
||||
this.elements = elements;
|
||||
this.dependencies = dependencies;
|
||||
this.elementType = elementType.getName();
|
||||
}
|
||||
|
||||
public Map<String, Collection<String>> getDependencies() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
public List<T> getSymbols() {
|
||||
return elements;
|
||||
}
|
||||
|
||||
public SortedMap<String, Long> getTimestampedFiles() {
|
||||
return timestampedFiles;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class IndexCacheStoreAdapter implements JsonDeserializer<IndexCacheStore<?>> {
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Override
|
||||
public IndexCacheStore<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
JsonObject parsedObject = json.getAsJsonObject();
|
||||
|
||||
String className = parsedObject.get("elementType").getAsString();
|
||||
|
||||
try {
|
||||
Class<?> elementType = Class.forName(className);
|
||||
|
||||
JsonElement elementsObject = parsedObject.get("elements");
|
||||
Type elementListType = TypeToken.getParameterized(List.class, elementType).getType();
|
||||
List elements = context.deserialize(elementsObject, elementListType);
|
||||
|
||||
JsonElement timestampedFilesObject = parsedObject.get("timestampedFiles");
|
||||
Type timestampsMapType = TypeToken.getParameterized(SortedMap.class, String.class, Long.class).getType();
|
||||
SortedMap timestampedFiles = context.deserialize(timestampedFilesObject, timestampsMapType);
|
||||
|
||||
JsonElement dependenciesObject = parsedObject.get("dependencies");
|
||||
Map dependencies = context.deserialize(dependenciesObject, HashMap.class);
|
||||
|
||||
return new IndexCacheStore(timestampedFiles, elements, dependencies, elementType);
|
||||
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new JsonParseException("cannot parse data from index cache with element type: " + className, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* gson adapter to store subtype information for symbol addon informations
|
||||
*/
|
||||
private static class DeltaStorageAdapter implements JsonSerializer<DeltaStorage>, JsonDeserializer<DeltaStorage> {
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(DeltaStorage deltaElement, Type typeOfSrc, JsonSerializationContext context) {
|
||||
JsonObject result = new JsonObject();
|
||||
result.add("type", new JsonPrimitive(deltaElement.storedElement.getClass().getName()));
|
||||
result.add("data", context.serialize(deltaElement.storedElement));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeltaStorage deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject parsedObject = json.getAsJsonObject();
|
||||
String className = parsedObject.get("type").getAsString();
|
||||
JsonElement element = parsedObject.get("data");
|
||||
|
||||
try {
|
||||
return new DeltaStorage(context.deserialize(element, Class.forName(className)));
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
throw new JsonParseException("cannot parse data from unknown SymbolAddOnInformation subtype: " + type, cnfe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gson adapter to store subtype information for symbol addon informations
|
||||
*/
|
||||
private static class SymbolAddOnInformationAdapter implements JsonSerializer<SymbolAddOnInformation>, JsonDeserializer<SymbolAddOnInformation> {
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(SymbolAddOnInformation addonInfo, Type typeOfSrc, JsonSerializationContext context) {
|
||||
JsonObject result = new JsonObject();
|
||||
result.add("type", new JsonPrimitive(addonInfo.getClass().getName()));
|
||||
result.add("data", context.serialize(addonInfo));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymbolAddOnInformation deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject parsedObject = json.getAsJsonObject();
|
||||
String className = parsedObject.get("type").getAsString();
|
||||
JsonElement element = parsedObject.get("data");
|
||||
|
||||
try {
|
||||
return context.deserialize(element, Class.forName(className));
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
throw new JsonParseException("cannot parse data from unknown SymbolAddOnInformation subtype: " + type, cnfe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gson adapter to store subtype information for beans
|
||||
*/
|
||||
private static class BeanJsonAdapter implements JsonDeserializer<Bean> {
|
||||
|
||||
@Override
|
||||
public Bean deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject parsedObject = json.getAsJsonObject();
|
||||
|
||||
String beanName = parsedObject.get("name").getAsString();
|
||||
String beanType = parsedObject.get("type").getAsString();
|
||||
|
||||
JsonElement locationObject = parsedObject.get("location");
|
||||
Location location = context.deserialize(locationObject, Location.class);
|
||||
|
||||
JsonElement injectionPointObject = parsedObject.get("injectionPoints");
|
||||
InjectionPoint[] injectionPoints = context.deserialize(injectionPointObject, InjectionPoint[].class);
|
||||
|
||||
JsonElement supertypesObject = parsedObject.get("supertypes");
|
||||
Set<String> supertypes = context.deserialize(supertypesObject, Set.class);
|
||||
|
||||
JsonElement annotationsObject = parsedObject.get("annotations");
|
||||
AnnotationMetadata[] annotations = annotationsObject == null ? DefaultValues.EMPTY_ANNOTATIONS : context.deserialize(annotationsObject, AnnotationMetadata[].class);
|
||||
|
||||
return new Bean(beanName, beanType, location, injectionPoints, supertypes, annotations);
|
||||
}
|
||||
}
|
||||
|
||||
private static class InjectionPointJsonAdapter implements JsonDeserializer<InjectionPoint> {
|
||||
|
||||
@Override
|
||||
public InjectionPoint deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject parsedObject = json.getAsJsonObject();
|
||||
|
||||
String injectionPointName = parsedObject.get("name").getAsString();
|
||||
String injectionPointType = parsedObject.get("type").getAsString();
|
||||
|
||||
JsonElement locationObject = parsedObject.get("location");
|
||||
Location location = context.deserialize(locationObject, Location.class);
|
||||
|
||||
JsonElement annotationsObject = parsedObject.get("annotations");
|
||||
AnnotationMetadata[] annotations = annotationsObject == null ? DefaultValues.EMPTY_ANNOTATIONS : context.deserialize(annotationsObject, AnnotationMetadata[].class);
|
||||
|
||||
return new InjectionPoint(injectionPointName, injectionPointType, location, annotations);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,704 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.index.cache.test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.SymbolKind;
|
||||
import org.eclipse.lsp4j.WorkspaceSymbol;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ide.vscode.boot.index.cache.AbstractIndexCacheable;
|
||||
import org.springframework.ide.vscode.boot.index.cache.IndexCacheKey;
|
||||
import org.springframework.ide.vscode.boot.index.cache.IndexCacheOnDiscDeltaBased;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
|
||||
import org.springframework.ide.vscode.boot.java.requestmapping.WebfluxElementsInformation;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
|
||||
import org.springframework.ide.vscode.commons.util.UriUtil;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
public class IndexCacheOnDiscDeltaBasedTest {
|
||||
|
||||
private static final String STORAGE_FILE_EXTENSION = ".json";
|
||||
|
||||
private static final IndexCacheKey CACHE_KEY_VERSION_1 = new IndexCacheKey("someProject", "someIndexer", "someCategory", "1");
|
||||
|
||||
private Path tempDir;
|
||||
private IndexCacheOnDiscDeltaBased cache;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
tempDir = Files.createTempDirectory("cachetest");
|
||||
cache = new IndexCacheOnDiscDeltaBased(tempDir.toFile());
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void deleteTempDir() throws Exception {
|
||||
FileUtils.deleteDirectory(tempDir.toFile());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyCache() throws Exception {
|
||||
Pair<CachedSymbol[], Multimap<String, String>> result = cache.retrieve(new IndexCacheKey("something", "someIndexer", "someCategory", "0"), new String[0], CachedSymbol.class);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimpleValidCache() throws Exception {
|
||||
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
|
||||
Path file2 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile2");
|
||||
Path file3 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile3");
|
||||
|
||||
Files.createFile(file1);
|
||||
Files.createFile(file2);
|
||||
Files.createFile(file3);
|
||||
|
||||
FileTime timeFile1 = Files.getLastModifiedTime(file1);
|
||||
String[] files = {file1.toString(), file2.toString(), file3.toString()};
|
||||
|
||||
List<CachedSymbol> generatedSymbols = new ArrayList<>();
|
||||
WorkspaceSymbol symbol = new WorkspaceSymbol("symbol1", SymbolKind.Field, Either.forLeft(new Location("docURI", new Range(new Position(3, 10), new Position(3, 20)))));
|
||||
EnhancedSymbolInformation enhancedSymbol = new EnhancedSymbolInformation(symbol, null);
|
||||
generatedSymbols.add(new CachedSymbol("", timeFile1.toMillis(), enhancedSymbol));
|
||||
|
||||
cache.store(CACHE_KEY_VERSION_1, files, generatedSymbols, ImmutableMultimap.of(
|
||||
file1.toString(), "file1dep1",
|
||||
file2.toString(), "file2dep1",
|
||||
file2.toString(), "file2dep2"
|
||||
), CachedSymbol.class);
|
||||
|
||||
Pair<CachedSymbol[], Multimap<String, String>> result = cache.retrieve(CACHE_KEY_VERSION_1, files, CachedSymbol.class);
|
||||
|
||||
CachedSymbol[] cachedSymbols = result.getLeft();
|
||||
assertNotNull(cachedSymbols);
|
||||
assertEquals(1, cachedSymbols.length);
|
||||
|
||||
assertEquals("symbol1", cachedSymbols[0].getEnhancedSymbol().getSymbol().getName());
|
||||
assertEquals(SymbolKind.Field, cachedSymbols[0].getEnhancedSymbol().getSymbol().getKind());
|
||||
assertEquals(new Location("docURI", new Range(new Position(3, 10), new Position(3, 20))), cachedSymbols[0].getEnhancedSymbol().getSymbol().getLocation().getLeft());
|
||||
assertNull(cachedSymbols[0].getEnhancedSymbol().getAdditionalInformation());
|
||||
|
||||
Multimap<String, String> dependencies = result.getRight();
|
||||
assertEquals(2, dependencies.keySet().size());
|
||||
assertEquals(dependencies.get(file1.toString()), ImmutableSet.of("file1dep1"));
|
||||
assertEquals(dependencies.get(file2.toString()), ImmutableSet.of("file2dep1", "file2dep2"));
|
||||
|
||||
assertEquals(timeFile1.toMillis(), cache.getModificationTimestamp(CACHE_KEY_VERSION_1, file1.toString()));
|
||||
assertEquals(0, cache.getModificationTimestamp(CACHE_KEY_VERSION_1, "random-non-existing-file"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDifferentCacheKey() throws Exception {
|
||||
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
|
||||
Path file2 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile2");
|
||||
Path file3 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile3");
|
||||
|
||||
Files.createFile(file1);
|
||||
Files.createFile(file2);
|
||||
Files.createFile(file3);
|
||||
|
||||
FileTime timeFile1 = Files.getLastModifiedTime(file1);
|
||||
String[] files = {file1.toString(), file2.toString(), file3.toString()};
|
||||
|
||||
List<CachedSymbol> generatedSymbols = new ArrayList<>();
|
||||
WorkspaceSymbol symbol = new WorkspaceSymbol("symbol1", SymbolKind.Field, Either.forLeft(new Location("docURI", new Range(new Position(3, 10), new Position(3, 20)))));
|
||||
EnhancedSymbolInformation enhancedSymbol = new EnhancedSymbolInformation(symbol, null);
|
||||
generatedSymbols.add(new CachedSymbol("", timeFile1.toMillis(), enhancedSymbol));
|
||||
|
||||
cache.store(CACHE_KEY_VERSION_1, files, generatedSymbols, null, CachedSymbol.class);
|
||||
|
||||
Pair<CachedSymbol[], Multimap<String, String>> result = cache.retrieve(new IndexCacheKey("someOtherProject", "someOtherIndexer", "someOtherCategory", "1"), files, CachedSymbol.class);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFileTouched() throws Exception {
|
||||
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
|
||||
Path file2 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile2");
|
||||
Path file3 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile3");
|
||||
|
||||
Files.createFile(file1);
|
||||
Files.createFile(file2);
|
||||
Files.createFile(file3);
|
||||
|
||||
FileTime timeFile1 = Files.getLastModifiedTime(file1);
|
||||
String[] files = {file1.toString(), file2.toString(), file3.toString()};
|
||||
|
||||
List<CachedSymbol> generatedSymbols = new ArrayList<>();
|
||||
WorkspaceSymbol symbol = new WorkspaceSymbol("symbol1", SymbolKind.Field, Either.forLeft(new Location("docURI", new Range(new Position(3, 10), new Position(3, 20)))));
|
||||
EnhancedSymbolInformation enhancedSymbol = new EnhancedSymbolInformation(symbol, null);
|
||||
generatedSymbols.add(new CachedSymbol("", timeFile1.toMillis(), enhancedSymbol));
|
||||
|
||||
cache.store(CACHE_KEY_VERSION_1, files, generatedSymbols, ImmutableMultimap.of(
|
||||
file1.toString(), "file1dep",
|
||||
file2.toString(), "file2dep"
|
||||
), CachedSymbol.class);
|
||||
|
||||
assertTrue(file1.toFile().setLastModified(timeFile1.toMillis() + 1000));
|
||||
|
||||
Pair<CachedSymbol[], Multimap<String, String>> result = cache.retrieve(CACHE_KEY_VERSION_1, files, CachedSymbol.class);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMoreFiles() throws Exception {
|
||||
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
|
||||
Path file2 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile2");
|
||||
Path file3 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile3");
|
||||
|
||||
Files.createFile(file1);
|
||||
Files.createFile(file2);
|
||||
Files.createFile(file3);
|
||||
|
||||
String[] files = {file1.toString(), file2.toString()};
|
||||
cache.store(CACHE_KEY_VERSION_1, files, new ArrayList<>(), null, CachedSymbol.class);
|
||||
|
||||
String[] moreFiles = {file1.toString(), file2.toString(), file3.toString()};
|
||||
assertNull(cache.retrieve(CACHE_KEY_VERSION_1, moreFiles, CachedSymbol.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFewerFiles() throws Exception {
|
||||
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
|
||||
Path file2 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile2");
|
||||
Path file3 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile3");
|
||||
|
||||
Files.createFile(file1);
|
||||
Files.createFile(file2);
|
||||
Files.createFile(file3);
|
||||
|
||||
String[] files = {file1.toString(), file2.toString(), file3.toString()};
|
||||
cache.store(CACHE_KEY_VERSION_1, files, new ArrayList<>(), null, CachedSymbol.class);
|
||||
|
||||
String[] fewerFiles = {file1.toString(), file2.toString()};
|
||||
assertNull(cache.retrieve(CACHE_KEY_VERSION_1, fewerFiles, CachedSymbol.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteOldCacheFileIfNewOneIsStored() throws Exception {
|
||||
IndexCacheKey key1 = CACHE_KEY_VERSION_1;
|
||||
cache.store(key1, new String[0], new ArrayList<>(), null, CachedSymbol.class);
|
||||
assertTrue(Files.exists(tempDir.resolve(Paths.get(key1.toString() + STORAGE_FILE_EXTENSION))));
|
||||
|
||||
IndexCacheKey key2 = new IndexCacheKey("someProject", "someIndexer", "someCategory", "2");
|
||||
cache.store(key2, new String[0], new ArrayList<>(), null, CachedSymbol.class);
|
||||
assertTrue(Files.exists(tempDir.resolve(Paths.get(key2.toString() + STORAGE_FILE_EXTENSION))));
|
||||
assertFalse(Files.exists(tempDir.resolve(Paths.get(key1.toString() + STORAGE_FILE_EXTENSION))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoNotRetrieveOldCacheDataIfNewerVersionIsStored() throws Exception {
|
||||
IndexCacheKey key1 = CACHE_KEY_VERSION_1;
|
||||
IndexCacheKey key2 = new IndexCacheKey("someProject", "someIndexer", "someCategory", "2");
|
||||
|
||||
cache.store(key1, new String[0], new ArrayList<>(), null, CachedSymbol.class);
|
||||
assertNotNull(cache.retrieve(key1, new String[0], CachedSymbol.class));
|
||||
assertNull(cache.retrieve(key2, new String[0], CachedSymbol.class));
|
||||
|
||||
cache.store(key2, new String[0], new ArrayList<>(), null, CachedSymbol.class);
|
||||
assertNull(cache.retrieve(key1, new String[0], CachedSymbol.class));
|
||||
assertNotNull(cache.retrieve(key2, new String[0], CachedSymbol.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteOldCacheFileFromPreviousReleasesIfNewOneIsStored() throws Exception {
|
||||
IndexCacheKey key1 = new IndexCacheKey("someProject", "someIndexer", "", "2");
|
||||
cache.store(key1, new String[0], new ArrayList<>(), null, CachedSymbol.class);
|
||||
assertTrue(Files.exists(tempDir.resolve(Paths.get(key1.toString() + STORAGE_FILE_EXTENSION))));
|
||||
|
||||
IndexCacheKey key2 = new IndexCacheKey("someProject", "someIndexer", "someCategory", "2");
|
||||
cache.store(key2, new String[0], new ArrayList<>(), null, CachedSymbol.class);
|
||||
assertTrue(Files.exists(tempDir.resolve(Paths.get(key2.toString() + STORAGE_FILE_EXTENSION))));
|
||||
assertFalse(Files.exists(tempDir.resolve(Paths.get(key1.toString() + STORAGE_FILE_EXTENSION))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoNotDeleteCacheFileFromOtherCategory() throws Exception {
|
||||
IndexCacheKey key1 = new IndexCacheKey("someProject", "someIndexer", "someCategory", "2");
|
||||
cache.store(key1, new String[0], new ArrayList<>(), null, CachedSymbol.class);
|
||||
assertTrue(Files.exists(tempDir.resolve(Paths.get(key1.toString() + STORAGE_FILE_EXTENSION))));
|
||||
|
||||
IndexCacheKey key2 = new IndexCacheKey("someProject", "someIndexer", "otherCategory", "2");
|
||||
cache.store(key2, new String[0], new ArrayList<>(), null, CachedSymbol.class);
|
||||
assertTrue(Files.exists(tempDir.resolve(Paths.get(key2.toString() + STORAGE_FILE_EXTENSION))));
|
||||
assertTrue(Files.exists(tempDir.resolve(Paths.get(key1.toString() + STORAGE_FILE_EXTENSION))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEnhancedInformationSubclasses() throws Exception {
|
||||
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
|
||||
Files.createFile(file1);
|
||||
|
||||
FileTime timeFile1 = Files.getLastModifiedTime(file1);
|
||||
String[] files = {file1.toString()};
|
||||
String doc1URI = UriUtil.toUri(file1.toFile()).toString();
|
||||
|
||||
List<CachedSymbol> generatedSymbols = new ArrayList<>();
|
||||
|
||||
WorkspaceSymbol symbol = new WorkspaceSymbol("symbol1", SymbolKind.Field, Either.forLeft(new Location(doc1URI, new Range(new Position(3, 10), new Position(3, 20)))));
|
||||
WebfluxElementsInformation addon = new WebfluxElementsInformation(new Range(new Position(4, 4), new Position(5, 5)), new Range(new Position(6, 6), new Position(7, 7)));
|
||||
EnhancedSymbolInformation enhancedSymbol = new EnhancedSymbolInformation(symbol, new SymbolAddOnInformation[]{addon});
|
||||
|
||||
generatedSymbols.add(new CachedSymbol(doc1URI, timeFile1.toMillis(), enhancedSymbol));
|
||||
|
||||
cache.store(CACHE_KEY_VERSION_1, files, generatedSymbols, null, CachedSymbol.class);
|
||||
|
||||
CachedSymbol[] cachedSymbols = cache.retrieveSymbols(CACHE_KEY_VERSION_1, files, CachedSymbol.class);
|
||||
assertNotNull(cachedSymbols);
|
||||
assertEquals(1, cachedSymbols.length);
|
||||
|
||||
assertEquals("symbol1", cachedSymbols[0].getEnhancedSymbol().getSymbol().getName());
|
||||
assertEquals(SymbolKind.Field, cachedSymbols[0].getEnhancedSymbol().getSymbol().getKind());
|
||||
assertEquals(new Location(doc1URI, new Range(new Position(3, 10), new Position(3, 20))), cachedSymbols[0].getEnhancedSymbol().getSymbol().getLocation().getLeft());
|
||||
|
||||
SymbolAddOnInformation[] retrievedAddOns = cachedSymbols[0].getEnhancedSymbol().getAdditionalInformation();
|
||||
assertNotNull(retrievedAddOns);
|
||||
assertEquals(1, retrievedAddOns.length);
|
||||
assertTrue(retrievedAddOns[0] instanceof WebfluxElementsInformation);
|
||||
|
||||
Range[] ranges = ((WebfluxElementsInformation) retrievedAddOns[0]).getRanges();
|
||||
assertEquals(2, ranges.length);
|
||||
assertEquals(new Range(new Position(4, 4), new Position(5, 5)), ranges[0]);
|
||||
assertEquals(new Range(new Position(6, 6), new Position(7, 7)), ranges[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSymbolAddedToExistingFile() throws Exception {
|
||||
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
|
||||
|
||||
Files.createFile(file1);
|
||||
|
||||
FileTime timeFile1 = Files.getLastModifiedTime(file1);
|
||||
String[] files = {file1.toAbsolutePath().toString()};
|
||||
|
||||
String doc1URI = UriUtil.toUri(file1.toFile()).toString();
|
||||
|
||||
List<CachedSymbol> generatedSymbols1 = new ArrayList<>();
|
||||
WorkspaceSymbol symbol1 = new WorkspaceSymbol("symbol1", SymbolKind.Field, Either.forLeft(new Location("docURI", new Range(new Position(3, 10), new Position(3, 20)))));
|
||||
EnhancedSymbolInformation enhancedSymbol1 = new EnhancedSymbolInformation(symbol1, null);
|
||||
generatedSymbols1.add(new CachedSymbol(doc1URI, timeFile1.toMillis(), enhancedSymbol1));
|
||||
|
||||
cache.store(CACHE_KEY_VERSION_1, files, generatedSymbols1, null, CachedSymbol.class);
|
||||
|
||||
List<CachedSymbol> generatedSymbols2 = new ArrayList<>();
|
||||
symbol1 = new WorkspaceSymbol("symbol1", SymbolKind.Field, Either.forLeft(new Location(doc1URI, new Range(new Position(3, 10), new Position(3, 20)))));
|
||||
enhancedSymbol1 = new EnhancedSymbolInformation(symbol1, null);
|
||||
|
||||
WorkspaceSymbol symbol2 = new WorkspaceSymbol("symbol2", SymbolKind.Interface, Either.forLeft(new Location(doc1URI, new Range(new Position(5, 5), new Position(5, 10)))));
|
||||
EnhancedSymbolInformation enhancedSymbol2 = new EnhancedSymbolInformation(symbol2, null);
|
||||
|
||||
generatedSymbols2.add(new CachedSymbol(doc1URI, timeFile1.toMillis() + 2000, enhancedSymbol1));
|
||||
generatedSymbols2.add(new CachedSymbol(doc1URI, timeFile1.toMillis() + 2000, enhancedSymbol2));
|
||||
|
||||
assertTrue(file1.toFile().setLastModified(timeFile1.toMillis() + 2000));
|
||||
cache.update(CACHE_KEY_VERSION_1, file1.toAbsolutePath().toString(), timeFile1.toMillis() + 2000, generatedSymbols2, null, CachedSymbol.class);
|
||||
|
||||
AbstractIndexCacheable[] cachedSymbols = cache.retrieveSymbols(CACHE_KEY_VERSION_1, files, CachedSymbol.class);
|
||||
assertNotNull(cachedSymbols);
|
||||
assertEquals(2, cachedSymbols.length);
|
||||
|
||||
assertEquals(timeFile1.toMillis() + 2000, cache.getModificationTimestamp(CACHE_KEY_VERSION_1, file1.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSymbolsAddedToMultipleFiles() throws Exception {
|
||||
|
||||
// create 3 files with one symbol each
|
||||
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
|
||||
Path file2 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile2");
|
||||
Path file3 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile3");
|
||||
|
||||
Files.createFile(file1);
|
||||
Files.createFile(file2);
|
||||
Files.createFile(file3);
|
||||
|
||||
FileTime timeFile1 = Files.getLastModifiedTime(file1);
|
||||
FileTime timeFile2 = Files.getLastModifiedTime(file2);
|
||||
FileTime timeFile3 = Files.getLastModifiedTime(file3);
|
||||
|
||||
String[] files = {file1.toAbsolutePath().toString(), file2.toAbsolutePath().toString(), file3.toAbsolutePath().toString()};
|
||||
|
||||
String doc1URI = UriUtil.toUri(file1.toFile()).toString();
|
||||
String doc2URI = UriUtil.toUri(file2.toFile()).toString();
|
||||
String doc3URI = UriUtil.toUri(file3.toFile()).toString();
|
||||
|
||||
List<CachedSymbol> generatedSymbols = new ArrayList<>();
|
||||
WorkspaceSymbol symbol1 = new WorkspaceSymbol("symbol1", SymbolKind.Field, Either.forLeft(new Location(doc1URI, new Range(new Position(3, 10), new Position(3, 20)))));
|
||||
EnhancedSymbolInformation enhancedSymbol1 = new EnhancedSymbolInformation(symbol1, null);
|
||||
generatedSymbols.add(new CachedSymbol(doc1URI, timeFile1.toMillis(), enhancedSymbol1));
|
||||
|
||||
WorkspaceSymbol symbol2 = new WorkspaceSymbol("symbol2", SymbolKind.Field, Either.forLeft(new Location(doc2URI, new Range(new Position(3, 10), new Position(3, 20)))));
|
||||
EnhancedSymbolInformation enhancedSymbol2 = new EnhancedSymbolInformation(symbol2, null);
|
||||
generatedSymbols.add(new CachedSymbol(doc2URI, timeFile2.toMillis(), enhancedSymbol2));
|
||||
|
||||
WorkspaceSymbol symbol3 = new WorkspaceSymbol("symbol3", SymbolKind.Field, Either.forLeft(new Location(doc3URI, new Range(new Position(3, 10), new Position(3, 20)))));
|
||||
EnhancedSymbolInformation enhancedSymbol3 = new EnhancedSymbolInformation(symbol3, null);
|
||||
generatedSymbols.add(new CachedSymbol(doc3URI, timeFile3.toMillis(), enhancedSymbol3));
|
||||
|
||||
// store original version of the symbols to the cache
|
||||
cache.store(CACHE_KEY_VERSION_1, files, generatedSymbols, null, CachedSymbol.class);
|
||||
|
||||
|
||||
// create updated and new symbols
|
||||
List<CachedSymbol> updatedSymbols = new ArrayList<>();
|
||||
|
||||
WorkspaceSymbol updatedSymbol1 = new WorkspaceSymbol("symbol1", SymbolKind.Field, Either.forLeft(new Location(doc1URI, new Range(new Position(3, 10), new Position(3, 20)))));
|
||||
EnhancedSymbolInformation updatedEnhancedSymbol1 = new EnhancedSymbolInformation(updatedSymbol1, null);
|
||||
|
||||
WorkspaceSymbol newSymbol1 = new WorkspaceSymbol("symbol1-new", SymbolKind.Interface, Either.forLeft(new Location(doc1URI, new Range(new Position(5, 5), new Position(5, 10)))));
|
||||
EnhancedSymbolInformation newEnhancedSymbol1 = new EnhancedSymbolInformation(newSymbol1, null);
|
||||
|
||||
updatedSymbols.add(new CachedSymbol(doc1URI, timeFile1.toMillis() + 2000, updatedEnhancedSymbol1));
|
||||
updatedSymbols.add(new CachedSymbol(doc1URI, timeFile1.toMillis() + 2000, newEnhancedSymbol1));
|
||||
assertTrue(file1.toFile().setLastModified(timeFile1.toMillis() + 2000));
|
||||
|
||||
WorkspaceSymbol updatedSymbol2 = new WorkspaceSymbol("symbol2-updated", SymbolKind.Field, Either.forLeft(new Location(doc2URI, new Range(new Position(3, 10), new Position(3, 20)))));
|
||||
EnhancedSymbolInformation updatedEnhancedSymbol2 = new EnhancedSymbolInformation(updatedSymbol2, null);
|
||||
updatedSymbols.add(new CachedSymbol(doc2URI, timeFile2.toMillis() + 3000, updatedEnhancedSymbol2));
|
||||
assertTrue(file2.toFile().setLastModified(timeFile2.toMillis() + 3000));
|
||||
|
||||
String[] updatedFiles = new String[]{file1.toAbsolutePath().toString(), file2.toAbsolutePath().toString()};
|
||||
long[] updatedModificationTimestamps = new long[]{timeFile1.toMillis() + 2000, timeFile2.toMillis() + 3000};
|
||||
|
||||
// update multiple files in the cache
|
||||
cache.update(CACHE_KEY_VERSION_1, updatedFiles, updatedModificationTimestamps, updatedSymbols, null, CachedSymbol.class);
|
||||
|
||||
// double check whether all changes got stored and retrieved correctly
|
||||
CachedSymbol[] cachedSymbols = cache.retrieveSymbols(CACHE_KEY_VERSION_1, files, CachedSymbol.class);
|
||||
assertNotNull(cachedSymbols);
|
||||
assertEquals(4, cachedSymbols.length);
|
||||
|
||||
assertSymbol(updatedEnhancedSymbol1, cachedSymbols);
|
||||
assertSymbol(newEnhancedSymbol1, cachedSymbols);
|
||||
assertSymbol(updatedEnhancedSymbol2, cachedSymbols);
|
||||
assertSymbol(enhancedSymbol3, cachedSymbols);
|
||||
|
||||
assertEquals(timeFile1.toMillis() + 2000, cache.getModificationTimestamp(CACHE_KEY_VERSION_1, file1.toString()));
|
||||
assertEquals(timeFile2.toMillis() + 3000, cache.getModificationTimestamp(CACHE_KEY_VERSION_1, file2.toString()));
|
||||
assertEquals(timeFile3.toMillis(), cache.getModificationTimestamp(CACHE_KEY_VERSION_1, file3.toString()));
|
||||
}
|
||||
|
||||
private void assertSymbol(EnhancedSymbolInformation enhancedSymbol, CachedSymbol[] cachedSymbols) {
|
||||
for (CachedSymbol cachedSymbol : cachedSymbols) {
|
||||
WorkspaceSymbol symbol = cachedSymbol.getEnhancedSymbol().getSymbol();
|
||||
|
||||
if (symbol.toString().equals(enhancedSymbol.getSymbol().toString())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fail("symbol not found: " + enhancedSymbol.getSymbol().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDependencyAddedToExistingFile() throws Exception {
|
||||
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
|
||||
|
||||
Files.createFile(file1);
|
||||
|
||||
FileTime timeFile1 = Files.getLastModifiedTime(file1);
|
||||
String[] files = {file1.toAbsolutePath().toString()};
|
||||
|
||||
List<CachedSymbol> generatedSymbols = ImmutableList.of();
|
||||
|
||||
Multimap<String, String> dependencies = ImmutableMultimap.of(file1.toString(), "dep1");
|
||||
cache.store(CACHE_KEY_VERSION_1, files, generatedSymbols, dependencies, CachedSymbol.class);
|
||||
|
||||
assertTrue(file1.toFile().setLastModified(timeFile1.toMillis() + 2000));
|
||||
Set<String> dependencies2 = ImmutableSet.of("dep1", "dep2");
|
||||
cache.update(CACHE_KEY_VERSION_1, file1.toAbsolutePath().toString(), timeFile1.toMillis() + 2000, generatedSymbols, dependencies2, CachedSymbol.class);
|
||||
|
||||
Pair<CachedSymbol[], Multimap<String, String>> result = cache.retrieve(CACHE_KEY_VERSION_1, files, CachedSymbol.class);
|
||||
assertNotNull(result);
|
||||
assertEquals(ImmutableSet.of("dep1", "dep2"), result.getRight().get(file1.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSymbolRemovedFromExistingFile() throws Exception {
|
||||
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
|
||||
|
||||
Files.createFile(file1);
|
||||
|
||||
FileTime timeFile1 = Files.getLastModifiedTime(file1);
|
||||
String[] files = {file1.toAbsolutePath().toString()};
|
||||
|
||||
String doc1URI = UriUtil.toUri(file1.toFile()).toString();
|
||||
|
||||
List<CachedSymbol> generatedSymbols1 = new ArrayList<>();
|
||||
WorkspaceSymbol symbol1 = new WorkspaceSymbol("symbol1", SymbolKind.Field, Either.forLeft(new Location("docURI", new Range(new Position(3, 10), new Position(3, 20)))));
|
||||
EnhancedSymbolInformation enhancedSymbol1 = new EnhancedSymbolInformation(symbol1, null);
|
||||
generatedSymbols1.add(new CachedSymbol(doc1URI, timeFile1.toMillis(), enhancedSymbol1));
|
||||
|
||||
cache.store(CACHE_KEY_VERSION_1, files, generatedSymbols1, null, CachedSymbol.class);
|
||||
|
||||
List<CachedSymbol> generatedSymbols2 = new ArrayList<>();
|
||||
assertTrue(file1.toFile().setLastModified(timeFile1.toMillis() + 2000));
|
||||
|
||||
cache.update(CACHE_KEY_VERSION_1, file1.toAbsolutePath().toString(), timeFile1.toMillis() + 2000, generatedSymbols2, null, CachedSymbol.class);
|
||||
|
||||
AbstractIndexCacheable[] cachedSymbols = cache.retrieveSymbols(CACHE_KEY_VERSION_1, files, CachedSymbol.class);
|
||||
assertNotNull(cachedSymbols);
|
||||
assertEquals(0, cachedSymbols.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDependencyRemovedFromExistingFile() throws Exception {
|
||||
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
|
||||
|
||||
Files.createFile(file1);
|
||||
|
||||
FileTime timeFile1 = Files.getLastModifiedTime(file1);
|
||||
String[] files = {file1.toAbsolutePath().toString()};
|
||||
|
||||
List<CachedSymbol> generatedSymbols1 = ImmutableList.of();
|
||||
|
||||
ImmutableMultimap<String, String> dependencies1 = ImmutableMultimap.of(
|
||||
file1.toString(), "dep1",
|
||||
file1.toString(), "dep2"
|
||||
);
|
||||
|
||||
cache.store(CACHE_KEY_VERSION_1, files, generatedSymbols1, dependencies1, CachedSymbol.class);
|
||||
|
||||
List<CachedSymbol> generatedSymbols2 = new ArrayList<>();
|
||||
assertTrue(file1.toFile().setLastModified(timeFile1.toMillis() + 2000));
|
||||
|
||||
Set<String> dependencies2 = ImmutableSet.of("dep2");
|
||||
cache.update(CACHE_KEY_VERSION_1, file1.toAbsolutePath().toString(), timeFile1.toMillis() + 2000, generatedSymbols2, dependencies2, CachedSymbol.class);
|
||||
|
||||
Pair<CachedSymbol[], Multimap<String, String>> result = cache.retrieve(CACHE_KEY_VERSION_1, files, CachedSymbol.class);
|
||||
assertNotNull(result);
|
||||
assertEquals(ImmutableSet.of("dep2"), result.getRight().get(file1.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSymbolAddedToNewFile() throws Exception {
|
||||
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
|
||||
Path file2 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile2");
|
||||
|
||||
Files.createFile(file1);
|
||||
Files.createFile(file2);
|
||||
|
||||
FileTime timeFile1 = Files.getLastModifiedTime(file1);
|
||||
FileTime timeFile2 = Files.getLastModifiedTime(file2);
|
||||
String[] files = {file1.toString()};
|
||||
|
||||
String doc1URI = UriUtil.toUri(file1.toFile()).toString();
|
||||
String doc2URI = UriUtil.toUri(file2.toFile()).toString();
|
||||
|
||||
List<CachedSymbol> generatedSymbols1 = new ArrayList<>();
|
||||
WorkspaceSymbol symbol1 = new WorkspaceSymbol("symbol1", SymbolKind.Field, Either.forLeft(new Location(doc1URI, new Range(new Position(3, 10), new Position(3, 20)))));
|
||||
EnhancedSymbolInformation enhancedSymbol1 = new EnhancedSymbolInformation(symbol1, null);
|
||||
generatedSymbols1.add(new CachedSymbol(doc1URI, timeFile1.toMillis(), enhancedSymbol1));
|
||||
|
||||
cache.store(CACHE_KEY_VERSION_1, files, generatedSymbols1, null, CachedSymbol.class);
|
||||
|
||||
List<CachedSymbol> generatedSymbols2 = new ArrayList<>();
|
||||
WorkspaceSymbol symbol2 = new WorkspaceSymbol("symbol2", SymbolKind.Interface, Either.forLeft(new Location(doc2URI, new Range(new Position(5, 5), new Position(5, 10)))));
|
||||
EnhancedSymbolInformation enhancedSymbol2 = new EnhancedSymbolInformation(symbol2, null);
|
||||
|
||||
generatedSymbols2.add(new CachedSymbol(doc2URI, timeFile2.toMillis(), enhancedSymbol2));
|
||||
|
||||
cache.update(CACHE_KEY_VERSION_1, file2.toString(), timeFile2.toMillis(), generatedSymbols2, null, CachedSymbol.class);
|
||||
|
||||
AbstractIndexCacheable[] cachedSymbols = cache.retrieveSymbols(CACHE_KEY_VERSION_1, new String[]{file1.toString(), file2.toString()}, CachedSymbol.class);
|
||||
assertNotNull(cachedSymbols);
|
||||
assertEquals(2, cachedSymbols.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDependencyAddedToNewFile() throws Exception {
|
||||
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
|
||||
Path file2 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile2");
|
||||
|
||||
Files.createFile(file1);
|
||||
Files.createFile(file2);
|
||||
|
||||
FileTime timeFile2 = Files.getLastModifiedTime(file2);
|
||||
String[] files = {file1.toString()};
|
||||
|
||||
List<CachedSymbol> generatedSymbols1 = ImmutableList.of();
|
||||
Multimap<String, String> dependencies1 = ImmutableMultimap.of(
|
||||
file1.toString(), "dep1"
|
||||
);
|
||||
cache.store(CACHE_KEY_VERSION_1, files, generatedSymbols1, dependencies1, CachedSymbol.class);
|
||||
|
||||
Set<String> dependencies2 = ImmutableSet.of("dep2");
|
||||
cache.update(CACHE_KEY_VERSION_1, file2.toString(), timeFile2.toMillis(), generatedSymbols1, dependencies2, CachedSymbol.class);
|
||||
|
||||
Pair<CachedSymbol[], Multimap<String, String>> result = cache.retrieve(CACHE_KEY_VERSION_1, new String[]{file1.toString(), file2.toString()}, CachedSymbol.class);
|
||||
assertNotNull(result);
|
||||
assertEquals(ImmutableSet.of("dep2"), result.getRight().get(file2.toString()));
|
||||
assertEquals(ImmutableSet.of("dep1"), result.getRight().get(file1.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProjectDeleted() throws Exception {
|
||||
IndexCacheKey key1 = CACHE_KEY_VERSION_1;
|
||||
cache.store(key1, new String[0], new ArrayList<>(), null, CachedSymbol.class);
|
||||
assertTrue(Files.exists(tempDir.resolve(Paths.get(key1.toString() + STORAGE_FILE_EXTENSION))));
|
||||
|
||||
cache.remove(key1);
|
||||
assertFalse(Files.exists(tempDir.resolve(Paths.get(key1.toString() + STORAGE_FILE_EXTENSION))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFileDeleted() throws Exception {
|
||||
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
|
||||
Path file2 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile2");
|
||||
|
||||
Files.createFile(file1);
|
||||
Files.createFile(file2);
|
||||
|
||||
FileTime timeFile1 = Files.getLastModifiedTime(file1);
|
||||
FileTime timeFile2 = Files.getLastModifiedTime(file2);
|
||||
String[] files = {file1.toAbsolutePath().toString(), file2.toAbsolutePath().toString()};
|
||||
|
||||
String doc1URI = UriUtil.toUri(file1.toFile()).toASCIIString();
|
||||
String doc2URI = UriUtil.toUri(file2.toFile()).toASCIIString();
|
||||
|
||||
List<CachedSymbol> generatedSymbols = new ArrayList<>();
|
||||
|
||||
WorkspaceSymbol symbol1 = new WorkspaceSymbol("symbol1", SymbolKind.Field, Either.forLeft(new Location(doc1URI, new Range(new Position(3, 10), new Position(3, 20)))));
|
||||
EnhancedSymbolInformation enhancedSymbol1 = new EnhancedSymbolInformation(symbol1, null);
|
||||
|
||||
WorkspaceSymbol symbol2 = new WorkspaceSymbol("symbol2", SymbolKind.Field, Either.forLeft(new Location(doc2URI, new Range(new Position(5, 10), new Position(5, 20)))));
|
||||
EnhancedSymbolInformation enhancedSymbol2 = new EnhancedSymbolInformation(symbol2, null);
|
||||
|
||||
generatedSymbols.add(new CachedSymbol(doc1URI, timeFile1.toMillis(), enhancedSymbol1));
|
||||
generatedSymbols.add(new CachedSymbol(doc2URI, timeFile2.toMillis(), enhancedSymbol2));
|
||||
|
||||
Multimap<String, String> dependencies = ImmutableMultimap.of(
|
||||
file1.toString(), "dep1",
|
||||
file2.toString(), "dep2"
|
||||
);
|
||||
cache.store(CACHE_KEY_VERSION_1, files, generatedSymbols, dependencies, CachedSymbol.class);
|
||||
cache.removeFile(CACHE_KEY_VERSION_1, file1.toAbsolutePath().toString(), CachedSymbol.class);
|
||||
|
||||
files = new String[]{file2.toAbsolutePath().toString()};
|
||||
Pair<CachedSymbol[], Multimap<String, String>> result = cache.retrieve(CACHE_KEY_VERSION_1, files, CachedSymbol.class);
|
||||
CachedSymbol[] cachedSymbols = result.getLeft();
|
||||
assertNotNull(result);
|
||||
assertEquals(1, cachedSymbols.length);
|
||||
|
||||
assertEquals("symbol2", cachedSymbols[0].getEnhancedSymbol().getSymbol().getName());
|
||||
assertEquals(SymbolKind.Field, cachedSymbols[0].getEnhancedSymbol().getSymbol().getKind());
|
||||
assertEquals(new Location(doc2URI, new Range(new Position(5, 10), new Position(5, 20))), cachedSymbols[0].getEnhancedSymbol().getSymbol().getLocation().getLeft());
|
||||
assertNull(cachedSymbols[0].getEnhancedSymbol().getAdditionalInformation());
|
||||
|
||||
Multimap<String, String> cachedDependencies = result.getRight();
|
||||
assertEquals(ImmutableSet.of(), cachedDependencies.get(file1.toString()));
|
||||
assertEquals(ImmutableSet.of("dep2"), cachedDependencies.get(file2.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleFilesDeleted() throws Exception {
|
||||
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
|
||||
Path file2 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile2");
|
||||
Path file3 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile3");
|
||||
Path file4 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile4");
|
||||
|
||||
Files.createFile(file1);
|
||||
Files.createFile(file2);
|
||||
Files.createFile(file3);
|
||||
Files.createFile(file4);
|
||||
|
||||
FileTime timeFile1 = Files.getLastModifiedTime(file1);
|
||||
FileTime timeFile2 = Files.getLastModifiedTime(file2);
|
||||
FileTime timeFile3 = Files.getLastModifiedTime(file3);
|
||||
FileTime timeFile4 = Files.getLastModifiedTime(file4);
|
||||
String[] files = {
|
||||
file1.toAbsolutePath().toString(),
|
||||
file2.toAbsolutePath().toString(),
|
||||
file3.toAbsolutePath().toString(),
|
||||
file4.toAbsolutePath().toString()
|
||||
};
|
||||
|
||||
String doc1URI = UriUtil.toUri(file1.toFile()).toASCIIString();
|
||||
String doc2URI = UriUtil.toUri(file2.toFile()).toASCIIString();
|
||||
String doc3URI = UriUtil.toUri(file3.toFile()).toASCIIString();
|
||||
String doc4URI = UriUtil.toUri(file4.toFile()).toASCIIString();
|
||||
|
||||
List<CachedSymbol> generatedSymbols = new ArrayList<>();
|
||||
|
||||
WorkspaceSymbol symbol1 = new WorkspaceSymbol("symbol1", SymbolKind.Field, Either.forLeft(new Location(doc1URI, new Range(new Position(3, 10), new Position(3, 20)))));
|
||||
EnhancedSymbolInformation enhancedSymbol1 = new EnhancedSymbolInformation(symbol1, null);
|
||||
|
||||
WorkspaceSymbol symbol2 = new WorkspaceSymbol("symbol2", SymbolKind.Field, Either.forLeft(new Location(doc2URI, new Range(new Position(5, 10), new Position(5, 20)))));
|
||||
EnhancedSymbolInformation enhancedSymbol2 = new EnhancedSymbolInformation(symbol2, null);
|
||||
|
||||
WorkspaceSymbol symbol3 = new WorkspaceSymbol("symbol3", SymbolKind.Field, Either.forLeft(new Location(doc3URI, new Range(new Position(20, 11), new Position(20, 30)))));
|
||||
EnhancedSymbolInformation enhancedSymbol3 = new EnhancedSymbolInformation(symbol3, null);
|
||||
|
||||
WorkspaceSymbol symbol4 = new WorkspaceSymbol("symbol4", SymbolKind.Field, Either.forLeft(new Location(doc4URI, new Range(new Position(4, 4), new Position(5, 5)))));
|
||||
EnhancedSymbolInformation enhancedSymbol4 = new EnhancedSymbolInformation(symbol4, null);
|
||||
|
||||
generatedSymbols.add(new CachedSymbol(doc1URI, timeFile1.toMillis(), enhancedSymbol1));
|
||||
generatedSymbols.add(new CachedSymbol(doc2URI, timeFile2.toMillis(), enhancedSymbol2));
|
||||
generatedSymbols.add(new CachedSymbol(doc3URI, timeFile3.toMillis(), enhancedSymbol3));
|
||||
generatedSymbols.add(new CachedSymbol(doc4URI, timeFile4.toMillis(), enhancedSymbol4));
|
||||
|
||||
Multimap<String, String> dependencies = ImmutableMultimap.of(
|
||||
file1.toString(), "dep1",
|
||||
file2.toString(), "dep2"
|
||||
);
|
||||
cache.store(CACHE_KEY_VERSION_1, files, generatedSymbols, dependencies, CachedSymbol.class);
|
||||
// cache.removeFile(CACHE_KEY_VERSION_1, file1.toAbsolutePath().toString(), CachedSymbol.class);
|
||||
// cache.removeFile(CACHE_KEY_VERSION_1, file3.toAbsolutePath().toString(), CachedSymbol.class);
|
||||
cache.removeFiles(CACHE_KEY_VERSION_1, new String[] {file1.toAbsolutePath().toString(), file3.toAbsolutePath().toString()}, CachedSymbol.class);
|
||||
|
||||
files = new String[]{file2.toAbsolutePath().toString(), file4.toAbsolutePath().toString()};
|
||||
Pair<CachedSymbol[], Multimap<String, String>> result = cache.retrieve(CACHE_KEY_VERSION_1, files, CachedSymbol.class);
|
||||
CachedSymbol[] cachedSymbols = result.getLeft();
|
||||
assertNotNull(result);
|
||||
assertEquals(2, cachedSymbols.length);
|
||||
|
||||
assertEquals("symbol2", cachedSymbols[0].getEnhancedSymbol().getSymbol().getName());
|
||||
assertEquals(SymbolKind.Field, cachedSymbols[0].getEnhancedSymbol().getSymbol().getKind());
|
||||
assertEquals(new Location(doc2URI, new Range(new Position(5, 10), new Position(5, 20))), cachedSymbols[0].getEnhancedSymbol().getSymbol().getLocation().getLeft());
|
||||
assertNull(cachedSymbols[0].getEnhancedSymbol().getAdditionalInformation());
|
||||
|
||||
assertEquals("symbol4", cachedSymbols[1].getEnhancedSymbol().getSymbol().getName());
|
||||
assertEquals(SymbolKind.Field, cachedSymbols[1].getEnhancedSymbol().getSymbol().getKind());
|
||||
assertEquals(new Location(doc4URI, new Range(new Position(4, 4), new Position(5, 5))), cachedSymbols[1].getEnhancedSymbol().getSymbol().getLocation().getLeft());
|
||||
assertNull(cachedSymbols[1].getEnhancedSymbol().getAdditionalInformation());
|
||||
|
||||
Multimap<String, String> cachedDependencies = result.getRight();
|
||||
assertEquals(ImmutableSet.of(), cachedDependencies.get(file1.toString()));
|
||||
assertEquals(ImmutableSet.of("dep2"), cachedDependencies.get(file2.toString()));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user