Emit value updates only on change (#1051)

This commit is contained in:
Dmitry Bushev 2020-08-03 17:38:39 +03:00 committed by GitHub
parent 359a768d2b
commit ef165e4f23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1106 additions and 192 deletions

View File

@ -75,7 +75,6 @@ class ContextEventsListenerSpec
Api.ExpressionValueUpdate(
Suggestions.method.externalId.get,
None,
None,
None
)
)
@ -117,7 +116,6 @@ class ContextEventsListenerSpec
Api.ExpressionValueUpdate(
Suggestions.method.externalId.get,
None,
None,
None
)
)
@ -129,7 +127,6 @@ class ContextEventsListenerSpec
Api.ExpressionValueUpdate(
Suggestions.local.externalId.get,
None,
None,
None
)
)

View File

@ -240,13 +240,11 @@ object Runtime {
*
* @param expressionId expression id.
* @param expressionType the type of expression.
* @param shortValue the value of expression.
* @param methodCall the pointer to a method definition.
*/
case class ExpressionValueUpdate(
expressionId: ExpressionId,
expressionType: Option[String],
shortValue: Option[String],
methodCall: Option[MethodPointer]
)

View File

@ -1,7 +1,6 @@
package org.enso.interpreter.instrument;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.FrameInstance;
import com.oracle.truffle.api.frame.FrameInstanceVisitor;
@ -19,7 +18,7 @@ import org.enso.pkg.QualifiedName;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Objects;
import java.util.UUID;
import java.util.function.Consumer;
@ -73,24 +72,46 @@ public class IdExecutionInstrument extends TruffleInstrument {
/** A class for notifications about identified expressions' values being computed. */
public static class ExpressionValue {
private final UUID expressionId;
private final String type;
private final Object value;
private final String type;
private final String cachedType;
private final FunctionCallInfo callInfo;
private final FunctionCallInfo cachedCallInfo;
/**
* Creates a new instance of this class.
*
* @param expressionId the id of the expression being computed.
* @param type of the computed expression.
* @param value the value returned by computing the expression.
* @param type the type of the returned value.
* @param cachedType the cached type of the value.
* @param callInfo the function call data.
*/
public ExpressionValue(
UUID expressionId, String type, Object value, FunctionCallInfo callInfo) {
UUID expressionId,
Object value,
String type,
String cachedType,
FunctionCallInfo callInfo,
FunctionCallInfo cachedCallInfo) {
this.expressionId = expressionId;
this.type = type;
this.value = value;
this.type = type;
this.cachedType = cachedType;
this.callInfo = callInfo;
this.cachedCallInfo = cachedCallInfo;
}
@Override
public String toString() {
return "ExpressionValue{" +
"expressionId=" + expressionId +
", value=" + value +
", type='" + type + '\'' +
", cachedType='" + cachedType + '\'' +
", callInfo=" + callInfo +
", cachedCallInfo=" + cachedCallInfo +
'}';
}
/** @return the id of the expression computed. */
@ -98,10 +119,14 @@ public class IdExecutionInstrument extends TruffleInstrument {
return expressionId;
}
/** @return the computed type of the expression. */
@CompilerDirectives.TruffleBoundary
public Optional<String> getType() {
return Optional.ofNullable(type);
/** @return the type of the returned value. */
public String getType() {
return type;
}
/** @return the cached type of the value. */
public String getCachedType() {
return cachedType;
}
/** @return the computed value of the expression. */
@ -113,6 +138,11 @@ public class IdExecutionInstrument extends TruffleInstrument {
public FunctionCallInfo getCallInfo() {
return callInfo;
}
/** @return the function call data previously associated with the expression. */
public FunctionCallInfo getCachedCallInfo() {
return cachedCallInfo;
}
}
/** Information about the function call. */
@ -145,6 +175,30 @@ public class IdExecutionInstrument extends TruffleInstrument {
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
FunctionCallInfo that = (FunctionCallInfo) o;
return Objects.equals(moduleName, that.moduleName) &&
Objects.equals(typeName, that.typeName) &&
functionName.equals(that.functionName);
}
@Override
public int hashCode() {
return Objects.hash(moduleName, typeName, functionName);
}
@Override
public String toString() {
return moduleName + "::" + typeName + "::" + functionName;
}
/** @return the name of the module this function was defined in, or null if not available. */
public QualifiedName getModuleName() {
return moduleName;
@ -165,8 +219,8 @@ public class IdExecutionInstrument extends TruffleInstrument {
private static class IdExecutionEventListener implements ExecutionEventListener {
private final CallTarget entryCallTarget;
private final Consumer<ExpressionCall> functionCallCallback;
private final Consumer<ExpressionValue> valueCallback;
private final Consumer<ExpressionValue> visualisationCallback;
private final Consumer<ExpressionValue> onComputedCallback;
private final Consumer<ExpressionValue> onCachedCallback;
private final RuntimeCache cache;
private final UUID nextExecutionItem;
private final Map<UUID, FunctionCallInfo> calls = new HashMap<>();
@ -178,22 +232,22 @@ public class IdExecutionInstrument extends TruffleInstrument {
* @param cache the precomputed expression values.
* @param nextExecutionItem the next item scheduled for execution.
* @param functionCallCallback the consumer of function call events.
* @param valueCallback the consumer of the node value events.
* @param visualisationCallback the consumer of the node visualisation events.
* @param onComputedCallback the consumer of the computed value events.
* @param onCachedCallback the consumer of the cached value events.
*/
public IdExecutionEventListener(
CallTarget entryCallTarget,
RuntimeCache cache,
UUID nextExecutionItem,
Consumer<ExpressionCall> functionCallCallback,
Consumer<ExpressionValue> valueCallback,
Consumer<ExpressionValue> visualisationCallback) {
Consumer<ExpressionValue> onComputedCallback,
Consumer<ExpressionValue> onCachedCallback) {
this.entryCallTarget = entryCallTarget;
this.cache = cache;
this.nextExecutionItem = nextExecutionItem;
this.functionCallCallback = functionCallCallback;
this.valueCallback = valueCallback;
this.visualisationCallback = visualisationCallback;
this.onComputedCallback = onComputedCallback;
this.onCachedCallback = onCachedCallback;
}
@Override
@ -221,9 +275,14 @@ public class IdExecutionInstrument extends TruffleInstrument {
// item in the `functionCallCallback`. We allow to execute the cached `stackTop` value to be
// able to continue the stack execution, and unwind later from the `onReturnValue` callback.
if (result != null && !nodeId.equals(nextExecutionItem)) {
visualisationCallback.accept(
onCachedCallback.accept(
new ExpressionValue(
nodeId, Types.getName(result).orElse(null), result, calls.get(nodeId)));
nodeId,
result,
cache.getType(nodeId),
Types.getName(result),
calls.get(nodeId),
cache.getCall(nodeId)));
throw context.createUnwind(result);
}
}
@ -257,10 +316,13 @@ public class IdExecutionInstrument extends TruffleInstrument {
}
} else if (node instanceof ExpressionNode) {
UUID nodeId = ((ExpressionNode) node).getId();
String resultType = Types.getName(result);
cache.offer(nodeId, result);
valueCallback.accept(
new ExpressionValue(
nodeId, Types.getName(result).orElse(null), result, calls.get(nodeId)));
String cachedType = cache.putType(nodeId, resultType);
FunctionCallInfo call = calls.get(nodeId);
FunctionCallInfo cachedCall = cache.putCall(nodeId, call);
onComputedCallback.accept(
new ExpressionValue(nodeId, result, resultType, cachedType, call, cachedCall));
}
}
@ -307,8 +369,8 @@ public class IdExecutionInstrument extends TruffleInstrument {
* @param funSourceLength the length of the observed source range.
* @param cache the precomputed expression values.
* @param nextExecutionItem the next item scheduled for execution.
* @param valueCallback the consumer of the node value events.
* @param visualisationCallback the consumer of the node visualisation events.
* @param onComputedCallback the consumer of the computed value events.
* @param onCachedCallback the consumer of the cached value events.
* @param functionCallCallback the consumer of function call events.
* @return a reference to the attached event listener.
*/
@ -318,8 +380,8 @@ public class IdExecutionInstrument extends TruffleInstrument {
int funSourceLength,
RuntimeCache cache,
UUID nextExecutionItem,
Consumer<ExpressionValue> valueCallback,
Consumer<IdExecutionInstrument.ExpressionValue> visualisationCallback,
Consumer<ExpressionValue> onComputedCallback,
Consumer<IdExecutionInstrument.ExpressionValue> onCachedCallback,
Consumer<ExpressionCall> functionCallCallback) {
SourceSectionFilter filter =
SourceSectionFilter.newBuilder()
@ -337,8 +399,8 @@ public class IdExecutionInstrument extends TruffleInstrument {
cache,
nextExecutionItem,
functionCallCallback,
valueCallback,
visualisationCallback));
onComputedCallback,
onCachedCallback));
return binding;
}
}

View File

@ -10,6 +10,8 @@ import java.util.UUID;
public class RuntimeCache {
private final Map<UUID, SoftReference<Object>> cache = new HashMap<>();
private final Map<UUID, String> types = new HashMap<>();
private final Map<UUID, IdExecutionInstrument.FunctionCallInfo> calls = new HashMap<>();
private Map<UUID, Double> weights = new HashMap<>();
/**
@ -50,6 +52,52 @@ public class RuntimeCache {
cache.clear();
}
/**
* Cache the type of expression.
*
* @return the previously cached type.
*/
public String putType(UUID key, String typeName) {
return types.put(key, typeName);
}
/** @return the cached type of the expression */
public String getType(UUID key) {
return types.get(key);
}
/**
* Cache the function call
*
* @param key the expression associated with the function call.
* @param call the function call.
* @return the function call that was previously associated with this expression.
*/
public IdExecutionInstrument.FunctionCallInfo putCall(
UUID key, IdExecutionInstrument.FunctionCallInfo call) {
return calls.put(key, call);
}
/** @return the cached function call associated with the expression. */
public IdExecutionInstrument.FunctionCallInfo getCall(UUID key) {
return calls.get(key);
}
/** Clear the cached calls. */
public void clearCalls() {
calls.clear();
}
/** Remove the type associated with the provided key. */
public void removeType(UUID key) {
types.remove(key);
}
/** Clear the cached types. */
public void clearTypes() {
types.clear();
}
/** @return the weights of this cache. */
public Map<UUID, Double> getWeights() {
return weights;

View File

@ -101,23 +101,27 @@ public class Types {
* @param value an object of interest.
* @return the string representation of object's type.
*/
public static Optional<String> getName(Object value) {
if (TypesGen.isLong(value)) {
return Optional.of("Number");
public static String getName(Object value) {
if (TypesGen.isLong(value) || TypesGen.isImplicitLong(value)) {
return "Number";
} else if (TypesGen.isBoolean(value)) {
return "Boolean";
} else if (TypesGen.isString(value)) {
return Optional.of("Text");
return "Text";
} else if (TypesGen.isFunction(value)) {
return Optional.of("Function");
return "Function";
} else if (TypesGen.isAtom(value)) {
return Optional.of(TypesGen.asAtom(value).getConstructor().getName());
return TypesGen.asAtom(value).getConstructor().getName();
} else if (TypesGen.isAtomConstructor(value)) {
return Optional.of(TypesGen.asAtomConstructor(value).getName());
return TypesGen.asAtomConstructor(value).getName();
} else if (TypesGen.isThunk(value)) {
return Optional.of("Thunk");
return "Thunk";
} else if (TypesGen.isRuntimeError(value)) {
return Optional.of("Error " + TypesGen.asRuntimeError(value).getPayload().toString());
return "Error " + TypesGen.asRuntimeError(value).getPayload().toString();
} else if (TypesGen.isVector(value)) {
return "Vector";
} else {
return Optional.empty();
return null;
}
}

View File

@ -84,16 +84,16 @@ public class ExecutionService {
* @param call the call metadata.
* @param cache the precomputed expression values.
* @param nextExecutionItem the next item scheduled for execution.
* @param valueCallback the consumer for expression value events.
* @param visualisationCallback the consumer of the node visualisation events.
* @param onComputedCallback the consumer of the computed value events.
* @param onCachedCallback the consumer of the cached value events.
* @param funCallCallback the consumer for function call events.
*/
public void execute(
FunctionCallInstrumentationNode.FunctionCall call,
RuntimeCache cache,
UUID nextExecutionItem,
Consumer<IdExecutionInstrument.ExpressionValue> valueCallback,
Consumer<IdExecutionInstrument.ExpressionValue> visualisationCallback,
Consumer<IdExecutionInstrument.ExpressionValue> onComputedCallback,
Consumer<IdExecutionInstrument.ExpressionValue> onCachedCallback,
Consumer<IdExecutionInstrument.ExpressionCall> funCallCallback)
throws UnsupportedMessageException, ArityException, UnsupportedTypeException {
@ -108,8 +108,8 @@ public class ExecutionService {
src.getCharLength(),
cache,
nextExecutionItem,
valueCallback,
visualisationCallback,
onComputedCallback,
onCachedCallback,
funCallCallback);
interopLibrary.execute(call);
listener.dispose();
@ -124,8 +124,8 @@ public class ExecutionService {
* @param methodName the method name.
* @param cache the precomputed expression values.
* @param nextExecutionItem the next item scheduled for execution.
* @param valueCallback the consumer for expression value events.
* @param visualisationCallback the consumer of the node visualisation events.
* @param onComputedCallback the consumer of the computed value events.
* @param onCachedCallback the consumer of the cached value events.
* @param funCallCallback the consumer for function call events.
*/
public void execute(
@ -134,8 +134,8 @@ public class ExecutionService {
String methodName,
RuntimeCache cache,
UUID nextExecutionItem,
Consumer<IdExecutionInstrument.ExpressionValue> valueCallback,
Consumer<IdExecutionInstrument.ExpressionValue> visualisationCallback,
Consumer<IdExecutionInstrument.ExpressionValue> onComputedCallback,
Consumer<IdExecutionInstrument.ExpressionValue> onCachedCallback,
Consumer<IdExecutionInstrument.ExpressionCall> funCallCallback)
throws UnsupportedMessageException, ArityException, UnsupportedTypeException {
Optional<FunctionCallInstrumentationNode.FunctionCall> callMay =
@ -149,8 +149,8 @@ public class ExecutionService {
callMay.get(),
cache,
nextExecutionItem,
valueCallback,
visualisationCallback,
onComputedCallback,
onCachedCallback,
funCallCallback);
}

View File

@ -33,10 +33,12 @@ final class Changeset[A: TextEditor: IndexedSource](val source: A, val ir: IR) {
DataflowAnalysis,
"Empty dataflow analysis metadata during changeset calculation."
)
invalidated(edits)
val direct = invalidated(edits)
val transitive = direct
.map(Changeset.toDataflowDependencyType)
.flatMap(metadata.getExternal)
.flatten
direct.flatMap(_.externalId) ++ transitive
}
/** Traverses the IR and returns a list of the most specific (the innermost)

View File

@ -31,6 +31,15 @@ object CacheInvalidation {
/** Invalidate value from indexes. */
case object All extends IndexSelector
/** Invalidate the types index. */
case object Types extends IndexSelector
/** Invalidate the calls index. */
case object Calls extends IndexSelector
/** Invalidate the weights index. */
case object Weights extends IndexSelector
}
/** Base trait for cache invalidation commands. Commands describe how the
@ -89,10 +98,13 @@ object CacheInvalidation {
/** Create an invalidation instruction using a stack selector and an
* invalidation command.
*
* @param elements the stack elements selector.
* @param command the invalidation command.
* @param elements the stack elements selector
* @param command the invalidation command
*/
def apply(elements: StackSelector, command: Command): CacheInvalidation =
def apply(
elements: StackSelector,
command: Command
): CacheInvalidation =
new CacheInvalidation(elements, command, Set())
/** Run a sequence of invalidation instructions on an execution stack.
@ -150,19 +162,38 @@ object CacheInvalidation {
command match {
case Command.InvalidateAll =>
cache.clear()
if (indexes.contains(IndexSelector.All)) cache.clearWeights()
indexes.foreach(clearIndex(_, cache))
case Command.InvalidateKeys(keys) =>
keys.foreach { key =>
cache.remove(key)
if (indexes.contains(IndexSelector.All)) cache.removeWeight(key)
indexes.foreach(clearIndex(_, cache))
}
case Command.InvalidateStale(scope) =>
val staleKeys = cache.getKeys.asScala.diff(scope.toSet)
staleKeys.foreach { key =>
cache.remove(key)
if (indexes.contains(IndexSelector.All)) cache.removeWeight(key)
indexes.foreach(clearIndex(_, cache))
}
case Command.SetMetadata(metadata) =>
cache.setWeights(metadata.asJavaWeights)
}
/** Clear the selected index.
*
* @param selector the selected index
* @param cache the cache to invalidate
*/
private def clearIndex(selector: IndexSelector, cache: RuntimeCache): Unit =
selector match {
case IndexSelector.All =>
cache.clearTypes()
cache.clearWeights()
cache.clearCalls()
case IndexSelector.Weights =>
cache.clearWeights()
case IndexSelector.Types =>
cache.clearTypes()
case IndexSelector.Calls =>
cache.clearCalls()
}
}

View File

@ -155,10 +155,15 @@ class EnsureCompiledJob(protected val files: List[File])
.map(_._2)
val invalidateStaleCommand =
CacheInvalidation.Command.InvalidateStale(scopeIds)
Seq(invalidateExpressionsCommand, invalidateStaleCommand).map(
Seq(
CacheInvalidation(
CacheInvalidation.StackSelector.All,
_,
invalidateExpressionsCommand,
Set(CacheInvalidation.IndexSelector.Weights)
),
CacheInvalidation(
CacheInvalidation.StackSelector.All,
invalidateStaleCommand,
Set(CacheInvalidation.IndexSelector.All)
)
)
@ -173,7 +178,7 @@ class EnsureCompiledJob(protected val files: List[File])
private def runInvalidationCommands(
invalidationCommands: Iterable[CacheInvalidation]
)(implicit ctx: RuntimeContext): Unit = {
ctx.contextManager.getAll.valuesIterator
ctx.contextManager.getAll.values
.collect {
case stack if stack.nonEmpty =>
CacheInvalidation.runAll(stack, invalidationCommands)

View File

@ -1,7 +1,7 @@
package org.enso.interpreter.instrument.job
import java.io.File
import java.util.UUID
import java.util.{Objects, UUID}
import java.util.function.Consumer
import java.util.logging.Level
@ -38,19 +38,19 @@ trait ProgramExecutionSupport {
*
* @param executionFrame an execution frame
* @param callStack a call stack
* @param valueCallback a listener of computed values
* @param visualisationCallback a listener of fired visualisations
* @param onComputedCallback a listener of computed values
* @param onCachedCallback a listener of cached values
*/
@scala.annotation.tailrec
final private def runProgram(
executionFrame: ExecutionFrame,
callStack: List[LocalCallFrame],
valueCallback: Consumer[ExpressionValue],
visualisationCallback: Consumer[ExpressionValue]
onComputedCallback: Consumer[ExpressionValue],
onCachedCallback: Consumer[ExpressionValue]
)(implicit ctx: RuntimeContext): Unit = {
var enterables: Map[UUID, FunctionCall] = Map()
val valsCallback: Consumer[ExpressionValue] =
if (callStack.isEmpty) valueCallback else _ => ()
val computedCallback: Consumer[ExpressionValue] =
if (callStack.isEmpty) onComputedCallback else _ => ()
val callablesCallback: Consumer[ExpressionCall] = fun =>
if (callStack.headOption.exists(_.expressionId == fun.getExpressionId)) {
enterables += fun.getExpressionId -> fun.getCall
@ -63,8 +63,8 @@ trait ProgramExecutionSupport {
function,
cache,
callStack.headOption.map(_.expressionId).orNull,
valsCallback,
visualisationCallback,
computedCallback,
onCachedCallback,
callablesCallback
)
case ExecutionFrame(ExecutionItem.CallData(callData), cache) =>
@ -72,8 +72,8 @@ trait ProgramExecutionSupport {
callData,
cache,
callStack.headOption.map(_.expressionId).orNull,
valsCallback,
visualisationCallback,
computedCallback,
onCachedCallback,
callablesCallback
)
}
@ -86,8 +86,8 @@ trait ProgramExecutionSupport {
runProgram(
ExecutionFrame(ExecutionItem.CallData(call), item.cache),
tail,
valueCallback,
visualisationCallback
onComputedCallback,
onCachedCallback
)
case None =>
()
@ -129,67 +129,66 @@ trait ProgramExecutionSupport {
case ExecutionItem.CallData(call) => call.getFunction.getName
}
val visualisationUpdateCallback: Consumer[ExpressionValue] = { value =>
if (updatedVisualisations.contains(value.getExpressionId))
onVisualisationUpdate(contextId, value)
val onCachedValueCallback: Consumer[ExpressionValue] = { value =>
if (updatedVisualisations.contains(value.getExpressionId)) {
ctx.executionService.getLogger.finer(s"ON_CACHED $value")
fireVisualisationUpdates(contextId, value)
}
}
val onComputedValueCallback: Consumer[ExpressionValue] = { value =>
ctx.executionService.getLogger.finer(s"ON_COMPUTED $value")
sendValueUpdate(contextId, value)
fireVisualisationUpdates(contextId, value)
}
val (explicitCallOpt, localCalls) = unwind(stack, Nil, Nil)
for {
stackItem <- Either.fromOption(explicitCallOpt, "stack is empty")
_ <- Either
.catchNonFatal(
runProgram(
stackItem,
localCalls,
onExpressionValueComputed(contextId, _),
visualisationUpdateCallback
_ <-
Either
.catchNonFatal(
runProgram(
stackItem,
localCalls,
onComputedValueCallback,
onCachedValueCallback
)
)
)
.leftMap { ex =>
ctx.executionService.getLogger.log(
Level.FINE,
s"Error executing a function '${getName(stackItem.item)}'",
ex
)
s"error in function: ${getName(stackItem.item)}"
}
.leftMap { ex =>
ctx.executionService.getLogger.log(
Level.FINE,
s"Error executing a function '${getName(stackItem.item)}'",
ex
)
s"error in function: ${getName(stackItem.item)}"
}
} yield ()
}
private def onVisualisationUpdate(
contextId: Api.ContextId,
value: ExpressionValue
)(implicit ctx: RuntimeContext): Unit =
fireVisualisationUpdates(contextId, value)
private def onExpressionValueComputed(
contextId: Api.ContextId,
value: ExpressionValue
)(implicit ctx: RuntimeContext): Unit = {
sendValueUpdate(contextId, value)
fireVisualisationUpdates(contextId, value)
}
private def sendValueUpdate(
contextId: ContextId,
value: ExpressionValue
)(implicit ctx: RuntimeContext): Unit = {
ctx.endpoint.sendToClient(
Api.Response(
Api.ExpressionValuesComputed(
contextId,
Vector(
Api.ExpressionValueUpdate(
value.getExpressionId,
OptionConverters.toScala(value.getType),
Some(value.getValue.toString),
toMethodPointer(value)
if (
!Objects.equals(value.getType, value.getCachedType) ||
!Objects.equals(value.getCallInfo, value.getCachedCallInfo)
) {
ctx.endpoint.sendToClient(
Api.Response(
Api.ExpressionValuesComputed(
contextId,
Vector(
Api.ExpressionValueUpdate(
value.getExpressionId,
Some(value.getType),
toMethodPointer(value)
)
)
)
)
)
)
}
}
private def fireVisualisationUpdates(
@ -253,10 +252,10 @@ trait ProgramExecutionSupport {
value: ExpressionValue
)(implicit ctx: RuntimeContext): Option[Api.MethodPointer] =
for {
call <- Option(value.getCallInfo)
moduleName <- Option(call.getModuleName)
functionName = call.getFunctionName
typeName <- Option(call.getTypeName).map(_.item)
call <- Option(value.getCallInfo)
moduleName <- Option(call.getModuleName)
functionName = call.getFunctionName
typeName <- Option(call.getTypeName).map(_.item)
module <- OptionConverters.toScala(
ctx.executionService.getContext.getTopScope
.getModule(moduleName.toString)

View File

@ -13,7 +13,7 @@ class RuntimeCacheTest extends AnyFlatSpec with Matchers {
"RutimeCache" should "cache items" in {
val cache = new RuntimeCache
val key = UUID.randomUUID
val key = UUID.randomUUID()
val obj = 42
cache.offer(key, obj) shouldEqual false
@ -28,7 +28,7 @@ class RuntimeCacheTest extends AnyFlatSpec with Matchers {
it should "remove items" in {
val cache = new RuntimeCache
val key = UUID.randomUUID
val key = UUID.randomUUID()
val obj = new Object
cache.setWeights(
@ -38,4 +38,16 @@ class RuntimeCacheTest extends AnyFlatSpec with Matchers {
cache.remove(key) shouldEqual obj
cache.get(key) shouldEqual null
}
it should "cache types" in {
val cache = new RuntimeCache
val key = UUID.randomUUID()
val obj = "Number"
cache.putType(key, obj) shouldEqual null
cache.putType(key, obj) shouldEqual obj
cache.removeType(key)
cache.putType(key, obj) shouldEqual null
}
}

View File

@ -28,7 +28,7 @@ class ProjectRenameActionSpec
with MockitoSugar
with FlakySpec {
"A project rename action" should "delegate request to the Language Server" in new TestCtx {
"A project rename action" should "delegate request to the Language Server" taggedAs Flaky in new TestCtx {
//given
val probe = TestProbe()
fakeServer.withBehaviour {