From 081c8c889c58e458a72bcc4c0261a6f57a60aab3 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Tue, 30 Jan 2024 01:13:43 +0100 Subject: [PATCH] Don't cancel pending visualization's upserts (#8853) Uniqueness check of `UpsertVisualizationJob` only involved expressionId. Apparently now GUI sends mutliple visualizations for the same expressions and expects all of them to exist. Since previously we would cancel duplicate jobs, this was problematic. This change makes sure that uniqueness also takes into account visualization id. Fixed a few logs that were not passing arguments properly. Closes #8801 # Important Notes I have not noticed any more problems with loading visualizations so the issue appears to be resolved with this change. Added a unit test case that would previously fail due to cancellation of a job that upserts visualization. --- build.sbt | 14 +- .../websocket/json/LibrariesTest.scala | 2 +- .../command/AttachVisualizationCmd.scala | 16 +- .../command/ModifyVisualizationCmd.scala | 2 +- .../DeserializeLibrarySuggestionsJob.scala | 3 +- .../instrument/job/EnsureCompiledJob.scala | 3 +- .../job/UpsertVisualizationJob.scala | 8 +- .../interpreter/instrument/MockHandler.java | 4 +- .../interpreter/instrument/job/JobsTest.java | 37 + .../interpreter/runtime/ModuleTestUtils.java | 15 + .../compiler/test/CompilerTestSetup.scala | 228 + .../test/context/ChangesetBuilderTest.scala | 9 +- .../command/MockedCommandFactory.scala | 15 +- .../command/SlowAttachVisualizationCmd.scala | 35 + .../instrument/command/SlowEditFileCmd.scala | 4 +- .../job/SlowUpsertVisualizationJob.scala | 33 + .../src/test/resources/application-test.conf | 25 + .../RuntimeVisualizationsTest.scala | 6493 +++++++++-------- 18 files changed, 3800 insertions(+), 3146 deletions(-) create mode 100644 engine/runtime-instrument-common/src/test/java/org/enso/interpreter/instrument/job/JobsTest.java create mode 100644 engine/runtime-instrument-common/src/test/java/org/enso/interpreter/runtime/ModuleTestUtils.java create mode 100644 engine/runtime-instrument-common/src/test/scala/org/enso/compiler/test/CompilerTestSetup.scala create mode 100644 engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/command/SlowAttachVisualizationCmd.scala create mode 100644 engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/job/SlowUpsertVisualizationJob.scala diff --git a/build.sbt b/build.sbt index 2dd1b2fb053..5e69fbedd9c 100644 --- a/build.sbt +++ b/build.sbt @@ -1747,9 +1747,10 @@ lazy val runtime = (project in file("engine/runtime")) "ENSO_TEST_DISABLE_IR_CACHE" -> "false", "ENSO_EDITION_PATH" -> file("distribution/editions").getCanonicalPath ), - Test / compile := (Test / compile) - .dependsOn(`runtime-fat-jar` / Compile / compileModuleInfo) - .value + Test / compile := { + (LocalProject("runtime-instrument-common") / Test / compile).value + (Test / compile).value + } ) .settings( (Compile / javacOptions) ++= Seq( @@ -1898,13 +1899,18 @@ lazy val `runtime-instrument-common` = Test / fork := true, Test / envVars ++= distributionEnvironmentOverrides ++ Map( "ENSO_TEST_DISABLE_IR_CACHE" -> "false" + ), + libraryDependencies ++= Seq( + "junit" % "junit" % junitVersion % Test, + "com.github.sbt" % "junit-interface" % junitIfVersion % Test, + "org.scalatest" %% "scalatest" % scalatestVersion % Test ) ) .dependsOn(`refactoring-utils`) .dependsOn( LocalProject( "runtime" - ) % "compile->compile;test->test;runtime->runtime;bench->bench" + ) % "compile->compile;runtime->runtime;bench->bench" ) lazy val `runtime-instrument-id-execution` = diff --git a/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/LibrariesTest.scala b/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/LibrariesTest.scala index 8176c14ff91..06014b89ffb 100644 --- a/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/LibrariesTest.scala +++ b/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/LibrariesTest.scala @@ -59,7 +59,7 @@ class LibrariesTest ) "LocalLibraryManager" should { - "create a library project and include it on the list of local projects" taggedAs Flaky in { + "create a library project and include it on the list of local projects" taggedAs SkipOnFailure in { val client = getInitialisedWsClient() val testLibraryName = LibraryName("user", "My_Local_Lib") diff --git a/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/command/AttachVisualizationCmd.scala b/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/command/AttachVisualizationCmd.scala index df9d2baec70..1bfe1f5a649 100644 --- a/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/command/AttachVisualizationCmd.scala +++ b/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/command/AttachVisualizationCmd.scala @@ -34,7 +34,7 @@ class AttachVisualizationCmd( ) val maybeFutureExecutable = ctx.jobProcessor.run( - new UpsertVisualizationJob( + upsertVisualization( maybeRequestId, request.visualizationId, request.expressionId, @@ -48,6 +48,20 @@ class AttachVisualizationCmd( } } + def upsertVisualization( + maybeRequestId: Option[Api.RequestId], + visualizationId: Api.VisualizationId, + expressionId: Api.ExpressionId, + config: Api.VisualizationConfiguration + ): UpsertVisualizationJob = { + new UpsertVisualizationJob( + maybeRequestId, + visualizationId, + expressionId, + config + ) + } + override def toString: String = { "AttachVisualizationCmd(visualizationId: " + request.visualizationId + ",expressionId=" + request.expressionId + ")" } diff --git a/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/command/ModifyVisualizationCmd.scala b/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/command/ModifyVisualizationCmd.scala index d2abe393b29..8dc048b3ac4 100644 --- a/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/command/ModifyVisualizationCmd.scala +++ b/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/command/ModifyVisualizationCmd.scala @@ -33,7 +33,7 @@ class ModifyVisualizationCmd( ctx.executionService.getLogger.log( Level.FINE, "Modify visualization cmd for request id [{}] and visualization id [{}]", - Array(maybeRequestId, request.visualizationId) + Array[Object](maybeRequestId, request.visualizationId) ) val existingVisualization = ctx.contextManager.getVisualizationById( request.visualizationConfig.executionContextId, diff --git a/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/DeserializeLibrarySuggestionsJob.scala b/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/DeserializeLibrarySuggestionsJob.scala index a10b4865fa7..77e2950162a 100644 --- a/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/DeserializeLibrarySuggestionsJob.scala +++ b/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/DeserializeLibrarySuggestionsJob.scala @@ -30,7 +30,8 @@ final class DeserializeLibrarySuggestionsJob( override def run(implicit ctx: RuntimeContext): Unit = { ctx.executionService.getLogger.log( Level.FINE, - s"Deserializing suggestions for library [$libraryName]." + "Deserializing suggestions for library [{}].", + libraryName ) val serializationManager = SerializationManager( ctx.executionService.getContext.getCompiler.context diff --git a/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/EnsureCompiledJob.scala b/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/EnsureCompiledJob.scala index c085e7c5f81..a36e2c5b995 100644 --- a/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/EnsureCompiledJob.scala +++ b/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/EnsureCompiledJob.scala @@ -429,7 +429,8 @@ final class EnsureCompiledJob( if (invalidatedVisualizations.nonEmpty) { ctx.executionService.getLogger.log( Level.FINEST, - s"Invalidated visualizations [${invalidatedVisualizations.map(_.id)}]" + "Invalidated visualizations [{}]", + invalidatedVisualizations.map(_.id) ) } diff --git a/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/UpsertVisualizationJob.scala b/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/UpsertVisualizationJob.scala index d1fe27e0552..d3c227ce00e 100644 --- a/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/UpsertVisualizationJob.scala +++ b/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/UpsertVisualizationJob.scala @@ -53,7 +53,7 @@ class UpsertVisualizationJob( override def equalsTo(that: UniqueJob[_]): Boolean = that match { case that: UpsertVisualizationJob => - this.expressionId == that.expressionId + this.expressionId == that.expressionId && this.visualizationId == that.visualizationId case _ => false } @@ -146,7 +146,7 @@ class UpsertVisualizationJob( ctx.executionService.getLogger.log( Level.SEVERE, "Visualization for expression {0} failed: {1} (evaluation result: {2})", - Array(expressionId, message, executionResult) + Array[Object](expressionId, message, executionResult) ) ctx.endpoint.sendToClient( Api.Response( @@ -159,6 +159,10 @@ class UpsertVisualizationJob( ) } + override def toString: String = { + s"UpsertVisualizationJob(visualizationId=$visualizationId, expressionId=$expressionId)" + } + } object UpsertVisualizationJob { diff --git a/engine/runtime-instrument-common/src/test/java/org/enso/interpreter/instrument/MockHandler.java b/engine/runtime-instrument-common/src/test/java/org/enso/interpreter/instrument/MockHandler.java index 479127dd828..20840b43228 100644 --- a/engine/runtime-instrument-common/src/test/java/org/enso/interpreter/instrument/MockHandler.java +++ b/engine/runtime-instrument-common/src/test/java/org/enso/interpreter/instrument/MockHandler.java @@ -1,11 +1,11 @@ package org.enso.interpreter.instrument; import org.enso.interpreter.instrument.command.CommandFactory; -import org.enso.interpreter.instrument.command.MockedCommandFactory$; +import org.enso.interpreter.instrument.command.MockedCommandFactory; public class MockHandler extends Handler { - private CommandFactory _cmdFactory = MockedCommandFactory$.MODULE$; + private CommandFactory _cmdFactory = new MockedCommandFactory(); @Override public CommandFactory cmdFactory() { diff --git a/engine/runtime-instrument-common/src/test/java/org/enso/interpreter/instrument/job/JobsTest.java b/engine/runtime-instrument-common/src/test/java/org/enso/interpreter/instrument/job/JobsTest.java new file mode 100644 index 00000000000..dc59bdae000 --- /dev/null +++ b/engine/runtime-instrument-common/src/test/java/org/enso/interpreter/instrument/job/JobsTest.java @@ -0,0 +1,37 @@ +package org.enso.interpreter.instrument.job; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.UUID; +import org.enso.polyglot.runtime.Runtime$Api$VisualizationConfiguration; +import org.enso.polyglot.runtime.Runtime$Api$VisualizationExpression$Text; +import org.junit.Test; +import scala.Option; + +public class JobsTest { + @Test + public void upsertJobUniqueness() { + var config = + new Runtime$Api$VisualizationConfiguration( + UUID.randomUUID(), new Runtime$Api$VisualizationExpression$Text("foo", "bar"), "test"); + var expression1 = UUID.randomUUID(); + var expression2 = UUID.randomUUID(); + var visualization1 = UUID.randomUUID(); + var visualization2 = UUID.randomUUID(); + var visualization3 = UUID.randomUUID(); + + var upsert1 = new UpsertVisualizationJob(Option.empty(), visualization1, expression1, config); + var upsert2 = new UpsertVisualizationJob(Option.empty(), visualization2, expression1, config); + var upsert3 = new UpsertVisualizationJob(Option.empty(), visualization3, expression2, config); + var upsert4 = new UpsertVisualizationJob(Option.empty(), visualization1, expression1, config); + var upsert5 = new UpsertVisualizationJob(Option.empty(), visualization1, expression2, config); + + assertFalse(upsert1.equalsTo(upsert2)); + assertFalse(upsert2.equalsTo(upsert3)); + assertFalse(upsert1.equalsTo(upsert3)); + assertTrue(upsert1.equalsTo(upsert4)); + assertFalse(upsert3.equalsTo(upsert4)); + assertFalse(upsert5.equalsTo(upsert3)); + } +} diff --git a/engine/runtime-instrument-common/src/test/java/org/enso/interpreter/runtime/ModuleTestUtils.java b/engine/runtime-instrument-common/src/test/java/org/enso/interpreter/runtime/ModuleTestUtils.java new file mode 100644 index 00000000000..5e6c24f59cd --- /dev/null +++ b/engine/runtime-instrument-common/src/test/java/org/enso/interpreter/runtime/ModuleTestUtils.java @@ -0,0 +1,15 @@ +package org.enso.interpreter.runtime; + +import org.enso.polyglot.CompilationStage; + +public final class ModuleTestUtils { + private ModuleTestUtils() {} + + public static void unsafeSetIr(Module m, org.enso.compiler.core.ir.Module ir) { + m.unsafeSetIr(ir); + } + + public static void unsafeSetCompilationStage(Module m, CompilationStage s) { + m.unsafeSetCompilationStage(s); + } +} diff --git a/engine/runtime-instrument-common/src/test/scala/org/enso/compiler/test/CompilerTestSetup.scala b/engine/runtime-instrument-common/src/test/scala/org/enso/compiler/test/CompilerTestSetup.scala new file mode 100644 index 00000000000..3c64093d646 --- /dev/null +++ b/engine/runtime-instrument-common/src/test/scala/org/enso/compiler/test/CompilerTestSetup.scala @@ -0,0 +1,228 @@ +package org.enso.compiler.test + +import org.enso.compiler.context.{FreshNameSupply, InlineContext, ModuleContext} +import org.enso.compiler.core.EnsoParser +import org.enso.compiler.core.Implicits.AsMetadata +import org.enso.compiler.core.ir.{Expression, Module} +import org.enso.compiler.core.ir.MetadataStorage.MetadataPair +import org.enso.compiler.data.BindingsMap.ModuleReference +import org.enso.compiler.data.{BindingsMap, CompilerConfig} +import org.enso.compiler.pass.analyse.BindingAnalysis +import org.enso.compiler.pass.{PassConfiguration, PassManager} +import org.enso.interpreter.runtime +import org.enso.interpreter.runtime.ModuleTestUtils +import org.enso.compiler.context.LocalScope +import org.enso.pkg.QualifiedName +import org.enso.polyglot.CompilationStage + +/** A reduced version of [[org.enso.compiler.test.CompilerRunner]] that avoids introducing a cyclic dependency + * to `runtime-instrument-common` subject. + */ +trait CompilerTestSetup { + // === IR Utilities ========================================================= + + /** An extension method to allow converting string source code to IR as a + * module. + * + * @param source the source code to convert + */ + implicit private class ToIrModule(source: String) { + + /** Converts program text to a top-level Enso module. + * + * @return the [[IR]] representing [[source]] + */ + def toIrModule: Module = { + val compiler = new EnsoParser() + try compiler.compile(source) + finally compiler.close() + } + } + + /** An extension method to allow converting string source code to IR as an + * expression. + * + * @param source the source code to convert + */ + implicit private class ToIrExpression(source: String) { + + /** Converts the program text to an Enso expression. + * + * @return the [[IR]] representing [[source]], if it is a valid expression + */ + def toIrExpression: Option[Expression] = { + val compiler = new EnsoParser() + try compiler.generateIRInline(compiler.parse(source)) + finally compiler.close() + } + } + + /** Provides an extension method allowing the running of a specified list of + * passes on the provided IR. + * + * @param ir the IR to run the passes on + */ + implicit private class RunPassesOnModule(ir: Module) { + + /** Executes the passes using `passManager` on the input [[ir]]. + * + * @param passManager the pass configuration + * @param moduleContext the module context it is executing in + * @return the result of executing the passes in `passManager` on [[ir]] + */ + def runPasses( + passManager: PassManager, + moduleContext: ModuleContext + ): Module = { + passManager.runPassesOnModule(ir, moduleContext) + } + } + + /** Provides an extension method allowing the running of a specified list of + * passes on the provided IR. + * + * @param ir the IR to run the passes on + */ + implicit private class RunPassesOnExpression(ir: Expression) { + + /** Executes the passes using `passManager` on the input [[ir]]. + * + * @param passManager the pass configuration + * @param inlineContext the inline context it is executing in + * @return the result of executing the passes in `passManager` on [[ir]] + */ + def runPasses( + passManager: PassManager, + inlineContext: InlineContext + ): Expression = { + passManager.runPassesInline(ir, inlineContext) + } + } + + /** Adds an extension method to preprocess the source as IR. + * + * @param source the source code to preprocess + */ + implicit class Preprocess(source: String)(implicit + passManager: PassManager + ) { + + /** Translates the source code into appropriate IR for testing this pass. + * + * @return IR appropriate for testing the alias analysis pass as a module + */ + def preprocessModule(implicit moduleContext: ModuleContext): Module = { + source.toIrModule.runPasses(passManager, moduleContext) + } + + /** Translates the source code into appropriate IR for testing this pass + * + * @return IR appropriate for testing the alias analysis pass as an + * expression + */ + def preprocessExpression(implicit + inlineContext: InlineContext + ): Option[Expression] = { + source.toIrExpression.map(_.runPasses(passManager, inlineContext)) + } + } + + // === IR Testing Utils ===================================================== + + /** Builds a module context with a mocked module for testing purposes. + * + * @param moduleName the name of the test module. + * @param freshNameSupply the fresh name supply to use in tests. + * @param passConfiguration any additional pass configuration. + * @return an instance of module context. + */ + def buildModuleContext( + moduleName: QualifiedName = QualifiedName.simpleName("Test_Module"), + freshNameSupply: Option[FreshNameSupply] = None, + passConfiguration: Option[PassConfiguration] = None, + compilerConfig: CompilerConfig = defaultConfig, + isGeneratingDocs: Boolean = false + ): ModuleContext = buildModuleContextModule( + moduleName, + freshNameSupply, + passConfiguration, + compilerConfig, + isGeneratingDocs + )._1 + + /** Builds a module context with a mocked module for testing purposes. + * + * @param moduleName the name of the test module. + * @param freshNameSupply the fresh name supply to use in tests. + * @param passConfiguration any additional pass configuration. + * @return an pair of module context and module. + */ + def buildModuleContextModule( + moduleName: QualifiedName = QualifiedName.simpleName("Test_Module"), + freshNameSupply: Option[FreshNameSupply] = None, + passConfiguration: Option[PassConfiguration] = None, + compilerConfig: CompilerConfig = defaultConfig, + isGeneratingDocs: Boolean = false + ): (ModuleContext, runtime.Module) = { + val mod = runtime.Module.empty(moduleName, null) + val ctx = ModuleContext( + module = mod.asCompilerModule(), + freshNameSupply = freshNameSupply, + passConfiguration = passConfiguration, + compilerConfig = compilerConfig, + isGeneratingDocs = isGeneratingDocs + ) + (ctx, mod) + } + + /** Builds an inline context with a mocked module for testing purposes. + * + * @param localScope the local scope for variable resolution. + * @param isInTailPosition whether the expression is being evaluated in + * a tail position. + * @param freshNameSupply the fresh name supply to use for name generation. + * @param passConfiguration any additional pass configuration. + * @return an instance of inline context. + */ + def buildInlineContext( + localScope: Option[LocalScope] = None, + isInTailPosition: Option[Boolean] = None, + freshNameSupply: Option[FreshNameSupply] = None, + passConfiguration: Option[PassConfiguration] = None, + compilerConfig: CompilerConfig = defaultConfig + ): InlineContext = { + val mod = + runtime.Module.empty(QualifiedName.simpleName("Test_Module"), null) + ModuleTestUtils.unsafeSetIr( + mod, + Module(List(), List(), List(), false, None) + .updateMetadata( + new MetadataPair( + BindingAnalysis, + BindingsMap( + List(), + ModuleReference.Concrete(mod.asCompilerModule()) + ) + ) + ) + ) + ModuleTestUtils.unsafeSetCompilationStage( + mod, + CompilationStage.AFTER_CODEGEN + ) + val mc = ModuleContext( + module = mod.asCompilerModule(), + compilerConfig = compilerConfig + ) + InlineContext( + module = mc, + freshNameSupply = freshNameSupply, + passConfiguration = passConfiguration, + localScope = localScope, + isInTailPosition = isInTailPosition, + compilerConfig = compilerConfig + ) + } + + val defaultConfig: CompilerConfig = CompilerConfig() +} diff --git a/engine/runtime-instrument-common/src/test/scala/org/enso/compiler/test/context/ChangesetBuilderTest.scala b/engine/runtime-instrument-common/src/test/scala/org/enso/compiler/test/context/ChangesetBuilderTest.scala index feef93103b4..f50c44759fe 100644 --- a/engine/runtime-instrument-common/src/test/scala/org/enso/compiler/test/context/ChangesetBuilderTest.scala +++ b/engine/runtime-instrument-common/src/test/scala/org/enso/compiler/test/context/ChangesetBuilderTest.scala @@ -13,15 +13,20 @@ import org.enso.compiler.core.ir.expression.Application import org.enso.compiler.core.ir.expression.errors import org.enso.compiler.core.ir.module.scope.definition import org.enso.compiler.pass.PassManager -import org.enso.compiler.test.CompilerTest +import org.enso.compiler.test.CompilerTestSetup import org.enso.compiler.context.LocalScope import org.enso.text.buffer.Rope import org.enso.text.editing.JavaEditorAdapter import org.enso.text.editing.model.{Position, Range, TextEdit} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike import java.util.UUID -class ChangesetBuilderTest extends CompilerTest { +class ChangesetBuilderTest + extends AnyWordSpecLike + with Matchers + with CompilerTestSetup { implicit val passManager: PassManager = new Passes(defaultConfig).passManager diff --git a/engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/command/MockedCommandFactory.scala b/engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/command/MockedCommandFactory.scala index 0e78fffd974..b251a96a19c 100644 --- a/engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/command/MockedCommandFactory.scala +++ b/engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/command/MockedCommandFactory.scala @@ -1,16 +1,25 @@ package org.enso.interpreter.instrument.command import org.enso.polyglot.runtime.Runtime.Api -object MockedCommandFactory extends CommandFactory { +class MockedCommandFactory extends CommandFactory { - private var editRequestCounter = 0 + private var editRequestCounter = 0 + private var attachVisualizationCounter = 0 override def createCommand(request: Api.Request): Command = { request.payload match { case payload: Api.EditFileNotification => - val cmd = new SlowEditFileCmd(payload, editRequestCounter) + val cmd = new SlowEditFileCmd(payload, editRequestCounter % 2 == 0) editRequestCounter += 1 cmd + case payload: Api.AttachVisualization => + val cmd = new SlowAttachVisualizationCmd( + request.requestId, + payload, + attachVisualizationCounter % 2 == 0 + ) + attachVisualizationCounter += 1 + cmd case _ => super.createCommand(request) } diff --git a/engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/command/SlowAttachVisualizationCmd.scala b/engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/command/SlowAttachVisualizationCmd.scala new file mode 100644 index 00000000000..d1c328889a9 --- /dev/null +++ b/engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/command/SlowAttachVisualizationCmd.scala @@ -0,0 +1,35 @@ +package org.enso.interpreter.instrument.command + +import org.enso.interpreter.instrument.job.{ + SlowUpsertVisualizationJob, + UpsertVisualizationJob +} +import org.enso.polyglot.runtime.Runtime.Api +import org.slf4j.LoggerFactory + +class SlowAttachVisualizationCmd( + maybeRequestId: Option[Api.RequestId], + request: Api.AttachVisualization, + delay: Boolean +) extends AttachVisualizationCmd(maybeRequestId, request) { + + private val logger = + LoggerFactory.getLogger(classOf[SlowAttachVisualizationCmd]) + + override def upsertVisualization( + maybeRequestId: Option[Api.RequestId], + visualizationId: Api.VisualizationId, + expressionId: Api.ExpressionId, + config: Api.VisualizationConfiguration + ): UpsertVisualizationJob = { + + logger.info("Delaying upsert for {}: {}", request.visualizationId, delay) + new SlowUpsertVisualizationJob( + maybeRequestId, + visualizationId, + expressionId, + config, + delay + ) + } +} diff --git a/engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/command/SlowEditFileCmd.scala b/engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/command/SlowEditFileCmd.scala index 9f85c279a32..a1ae78c2628 100644 --- a/engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/command/SlowEditFileCmd.scala +++ b/engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/command/SlowEditFileCmd.scala @@ -5,7 +5,7 @@ import org.enso.polyglot.runtime.Runtime.Api import scala.concurrent.ExecutionContext -class SlowEditFileCmd(request: Api.EditFileNotification, counter: Int) +class SlowEditFileCmd(request: Api.EditFileNotification, delay: Boolean) extends EditFileCmd(request) { override def executeSynchronously(implicit @@ -13,7 +13,7 @@ class SlowEditFileCmd(request: Api.EditFileNotification, counter: Int) ec: ExecutionContext ): Unit = { if ( - ctx.executionService.getContext.isRandomDelayedCommandExecution && counter % 2 == 0 + ctx.executionService.getContext.isRandomDelayedCommandExecution && delay ) { try { Thread.sleep(2000) diff --git a/engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/job/SlowUpsertVisualizationJob.scala b/engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/job/SlowUpsertVisualizationJob.scala new file mode 100644 index 00000000000..0f1834648c7 --- /dev/null +++ b/engine/runtime-instrument-common/src/test/scala/org/enso/interpreter/instrument/job/SlowUpsertVisualizationJob.scala @@ -0,0 +1,33 @@ +package org.enso.interpreter.instrument.job + +import org.enso.interpreter.instrument.execution.{Executable, RuntimeContext} +import org.enso.polyglot.runtime.Runtime.Api + +import scala.annotation.unused + +class SlowUpsertVisualizationJob( + @unused requestId: Option[Api.RequestId], + visualizationId: Api.VisualizationId, + expressionId: Api.ExpressionId, + config: Api.VisualizationConfiguration, + delay: Boolean +) extends UpsertVisualizationJob( + requestId, + visualizationId, + expressionId, + config + ) { + + override val isCancellable: Boolean = true + override val mayInterruptIfRunning: Boolean = true + + override def run(implicit ctx: RuntimeContext): Option[Executable] = { + if ( + ctx.executionService.getContext.isRandomDelayedCommandExecution && delay + ) { + Thread.sleep(1000) + } + + super.run(ctx) + } +} diff --git a/engine/runtime/src/test/resources/application-test.conf b/engine/runtime/src/test/resources/application-test.conf index c646131acb5..6f5255389f6 100644 --- a/engine/runtime/src/test/resources/application-test.conf +++ b/engine/runtime/src/test/resources/application-test.conf @@ -1 +1,26 @@ akka.coordinated-shutdown.run-by-actor-system-terminate = off +logging-service { + logger { + akka.actor = info + akka.event = error + akka.routing = error + akka.io = error + akka.stream = error + slick.jdbc.JdbcBackend.statement = error # log SQL queries on debug level + slick."*" = error + org.eclipse.jgit = error + io.methvin.watcher = error + } + appenders = [ + { + name = "memory" + forward-to = console + }, + { + name = "console" + pattern = "[%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n" + } + ] + default-appender = memory + log-level = "error" +} diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala index 8a6529aa4a1..9f195c28c70 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala @@ -8,7 +8,6 @@ import org.enso.polyglot.runtime.Runtime.Api import org.enso.text.editing.model import org.enso.text.editing.model.TextEdit import org.graalvm.polyglot.Context -import org.scalatest.BeforeAndAfterEach import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers @@ -19,16 +18,11 @@ import java.util.UUID import java.util.logging.Level @scala.annotation.nowarn("msg=multiarg infix syntax") -class RuntimeVisualizationsTest - extends AnyFlatSpec - with Matchers - with BeforeAndAfterEach { +class RuntimeVisualizationsTest extends AnyFlatSpec with Matchers { // === Test Utilities ======================================================= - var context: TestContext = _ - - class TestContext(packageName: String) + class TestContext(packageName: String, sequentialExecution: Boolean) extends InstrumentTestContext(packageName) { val out: ByteArrayOutputStream = new ByteArrayOutputStream() @@ -39,7 +33,14 @@ class RuntimeVisualizationsTest .allowAllAccess(true) .option(RuntimeOptions.PROJECT_ROOT, pkg.root.getAbsolutePath) .option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName()) - .option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true") + .option( + RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, + sequentialExecution.toString + ) + .option( + RuntimeOptions.INTERPRETER_RANDOM_DELAYED_COMMAND_EXECUTION, + (!sequentialExecution).toString + ) .option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false") .option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false") .option(RuntimeOptions.ENABLE_EXECUTION_TIMER, "false") @@ -83,7 +84,7 @@ class RuntimeVisualizationsTest // === The Tests ========================================================== - object Main { + object Main { context => val metadata = new Metadata @@ -301,797 +302,812 @@ class RuntimeVisualizationsTest } - override protected def beforeEach(): Unit = { - context = new TestContext("Test") - context.init() - val Some(Api.Response(_, Api.InitializedNotification())) = context.receive - } - - override protected def afterEach(): Unit = { - if (context != null) { - context.close() - context.out.reset() - context = null + def withContext( + sequentialExecution: Boolean = true + )(f: TestContext => Unit): Unit = { + val context = new TestContext("Test", sequentialExecution) + try { + context.init() + val Some(Api.Response(_, Api.InitializedNotification())) = context.receive + f(context) + } finally { + if (context != null) { + context.close() + context.out.reset() + } } } - it should "emit visualization update when expression is computed" in { - val idMainRes = context.Main.metadata.addItem(99, 1) - val contents = context.Main.code - val mainFile = context.writeMain(context.Main.code) - val moduleName = "Enso_Test.Test.Main" - val visualizationFile = - context.writeInSrcDir("Visualization", context.Visualization.code) + it should "emit visualization update when expression is computed" in withContext() { + context => + val idMainRes = context.Main.metadata.addItem(99, 1) + val contents = context.Main.code + val mainFile = context.writeMain(context.Main.code) + val moduleName = "Enso_Test.Test.Main" + val visualizationFile = + context.writeInSrcDir("Visualization", context.Visualization.code) - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() - context.send( - Api.Request( - requestId, - Api.OpenFileRequest( - visualizationFile, - context.Visualization.code - ) - ) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 6 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.mainX(contextId), - context.Main.Update.mainY(contextId), - context.Main.Update.mainZ(contextId), - TestMessages.update(contextId, idMainRes, ConstantsGen.INTEGER), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - idMainRes, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - "Enso_Test.Test.Visualization", - "x -> encode x" - ), - "Enso_Test.Test.Visualization" + context.send( + Api.Request( + requestId, + Api.OpenFileRequest( + visualizationFile, + context.Visualization.code ) ) ) - ) - val attachVisualizationResponses = - context.receiveNIgnoreExpressionUpdates(3) - attachVisualizationResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMainRes` - ), - data - ) - ) => - data - } - data.sameElements("50".getBytes) shouldBe true - - // recompute - context.send( - Api.Request(requestId, Api.RecomputeContextRequest(contextId, None, None)) - ) - - val recomputeResponses = context.receiveNIgnoreExpressionUpdates(3) - recomputeResponses should contain allOf ( - Api.Response(requestId, Api.RecomputeContextResponse(contextId)), - context.executionComplete(contextId) - ) - val Some(data2) = recomputeResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMainRes` - ), - data - ) - ) => - data - } - data2.sameElements("50".getBytes) shouldBe true - } - - it should "emit visualization update when expression is cached" in { - val contents = context.Main.code - val mainFile = context.writeMain(context.Main.code) - val moduleName = "Enso_Test.Test.Main" - val visualizationFile = - context.writeInSrcDir("Visualization", context.Visualization.code) - - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - - context.send( - Api.Request( - requestId, - Api.OpenFileRequest( - visualizationFile, - context.Visualization.code - ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) ) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 5 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.mainX(contextId), - context.Main.Update.mainY(contextId), - context.Main.Update.mainZ(contextId), - context.executionComplete(contextId) - ) + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 6 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + TestMessages.update(contextId, idMainRes, ConstantsGen.INTEGER), + context.executionComplete(contextId) + ) - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - context.Main.idMainX, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - "Enso_Test.Test.Visualization", - "x -> encode x" - ), - "Enso_Test.Test.Visualization" + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + idMainRes, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Enso_Test.Test.Visualization", + "x -> encode x" + ), + "Enso_Test.Test.Visualization" + ) ) ) ) - ) - val attachVisualizationResponses = context.receiveN(2) - attachVisualizationResponses should contain( - Api.Response(requestId, Api.VisualizationAttached()) - ) - val expectedExpressionId = context.Main.idMainX - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `expectedExpressionId` + val attachVisualizationResponses = + context.receiveNIgnoreExpressionUpdates(3) + attachVisualizationResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) + ) + val Some(data) = attachVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMainRes` + ), + data + ) + ) => + data + } + data.sameElements("50".getBytes) shouldBe true + + // recompute + context.send( + Api.Request( + requestId, + Api.RecomputeContextRequest(contextId, None, None) + ) + ) + + val recomputeResponses = context.receiveNIgnoreExpressionUpdates(3) + recomputeResponses should contain allOf ( + Api.Response(requestId, Api.RecomputeContextResponse(contextId)), + context.executionComplete(contextId) + ) + val Some(data2) = recomputeResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMainRes` + ), + data + ) + ) => + data + } + data2.sameElements("50".getBytes) shouldBe true + } + + it should "emit visualization update when expression is cached" in withContext() { + context => + val contents = context.Main.code + val mainFile = context.writeMain(context.Main.code) + val moduleName = "Enso_Test.Test.Main" + val visualizationFile = + context.writeInSrcDir("Visualization", context.Visualization.code) + + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + + context.send( + Api.Request( + requestId, + Api.OpenFileRequest( + visualizationFile, + context.Visualization.code + ) + ) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 5 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + context.Main.idMainX, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Enso_Test.Test.Visualization", + "x -> encode x" ), - data + "Enso_Test.Test.Visualization" ) - ) => - data - } - data.sameElements("6".getBytes) shouldBe true + ) + ) + ) + val attachVisualizationResponses = context.receiveN(2) + attachVisualizationResponses should contain( + Api.Response(requestId, Api.VisualizationAttached()) + ) + val expectedExpressionId = context.Main.idMainX + val Some(data) = attachVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `expectedExpressionId` + ), + data + ) + ) => + data + } + data.sameElements("6".getBytes) shouldBe true - // recompute - context.send( - Api.Request(requestId, Api.RecomputeContextRequest(contextId, None, None)) - ) - context.receiveNIgnoreExpressionUpdates(2) should contain allOf ( - Api.Response(requestId, Api.RecomputeContextResponse(contextId)), - context.executionComplete(contextId) - ) + // recompute + context.send( + Api.Request( + requestId, + Api.RecomputeContextRequest(contextId, None, None) + ) + ) + context.receiveNIgnoreExpressionUpdates(2) should contain allOf ( + Api.Response(requestId, Api.RecomputeContextResponse(contextId)), + context.executionComplete(contextId) + ) - // recompute invalidating x - context.send( - Api.Request( - requestId, - Api.RecomputeContextRequest( + // recompute invalidating x + context.send( + Api.Request( + requestId, + Api.RecomputeContextRequest( + contextId, + Some( + Api.InvalidatedExpressions.Expressions( + Vector(context.Main.idMainX) + ) + ), + None + ) + ) + ) + val recomputeResponses2 = context.receiveNIgnoreExpressionUpdates(3) + recomputeResponses2 should contain allOf ( + Api.Response(requestId, Api.RecomputeContextResponse(contextId)), + context.executionComplete(contextId) + ) + val Some(data2) = recomputeResponses2.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `expectedExpressionId` + ), + data + ) + ) => + data + } + data2.sameElements("6".getBytes) shouldBe true + } + + it should "emit visualization update when expression is modified" in withContext() { + context => + val contents = context.Main.code + val moduleName = "Enso_Test.Test.Main" + val mainFile = context.writeMain(contents) + val visualizationFile = + context.writeInSrcDir("Visualization", context.Visualization.code) + + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + + // open files + context.send( + Api.Request( + requestId, + Api.OpenFileRequest( + visualizationFile, + context.Visualization.code + ) + ) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + + context.receiveNIgnorePendingExpressionUpdates( + 5 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + context.Main.idMainX, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Enso_Test.Test.Visualization", + "x -> encode x" + ), + "Enso_Test.Test.Visualization" + ) + ) + ) + ) + val attachVisualizationResponses = context.receiveN(2) + attachVisualizationResponses should contain( + Api.Response(requestId, Api.VisualizationAttached()) + ) + val expectedExpressionId = context.Main.idMainX + val Some(data) = attachVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `expectedExpressionId` + ), + data + ) + ) => + data + } + data.sameElements("6".getBytes) shouldBe true + + // Modify the file + context.send( + Api.Request( + Api.EditFileNotification( + mainFile, + Seq( + TextEdit( + model.Range(model.Position(4, 8), model.Position(4, 9)), + "5" + ) + ), + execute = true + ) + ) + ) + + val editFileResponse = context.receiveNIgnoreExpressionUpdates(2) + editFileResponse should contain( + context.executionComplete(contextId) + ) + val Some(data1) = editFileResponse.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `expectedExpressionId` + ), + data + ) + ) => + data + } + data1.sameElements("5".getBytes) shouldBe true + } + + it should "emit visualization update when transitive expression is modified" in withContext() { + context => + val contents = context.Main.code + val moduleName = "Enso_Test.Test.Main" + val mainFile = context.writeMain(contents) + val visualizationFile = + context.writeInSrcDir("Visualization", context.Visualization.code) + + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + + // open files + context.send( + Api.Request( + requestId, + Api.OpenFileRequest( + visualizationFile, + context.Visualization.code + ) + ) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + + context.receiveNIgnorePendingExpressionUpdates( + 5 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + context.Main.idMainZ, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Enso_Test.Test.Visualization", + "encode" + ), + "Enso_Test.Test.Visualization" + ) + ) + ) + ) + val attachVisualizationResponses = context.receiveN(2) + attachVisualizationResponses should contain( + Api.Response(requestId, Api.VisualizationAttached()) + ) + val expectedExpressionId = context.Main.idMainZ + val Some(data) = attachVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `expectedExpressionId` + ), + data + ) + ) => + data + } + data.sameElements("50".getBytes) shouldBe true + + // Modify the file + context.send( + Api.Request( + Api.EditFileNotification( + mainFile, + Seq( + TextEdit( + model.Range(model.Position(4, 8), model.Position(4, 9)), + "5" + ) + ), + execute = true + ) + ) + ) + + val editFileResponse = context.receiveNIgnoreExpressionUpdates(2) + editFileResponse should contain( + context.executionComplete(contextId) + ) + val Some(data1) = editFileResponse.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `expectedExpressionId` + ), + data + ) + ) => + data + } + data1.sameElements("45".getBytes) shouldBe true + } + + it should "emit visualization update when frame popped" in withContext() { + context => + val contents = context.Main.code + val moduleName = "Enso_Test.Test.Main" + val mainFile = context.writeMain(contents) + val visualizationFile = + context.writeInSrcDir("Visualization", context.Visualization.code) + + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + + // open files + context.send( + Api.Request( + requestId, + Api.OpenFileRequest( + visualizationFile, + context.Visualization.code + ) + ) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + + context.receiveNIgnorePendingExpressionUpdates( + 5 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + context.Main.idMainZ, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Enso_Test.Test.Visualization", + "encode" + ), + "Enso_Test.Test.Visualization" + ) + ) + ) + ) + val attachVisualizationResponses = context.receiveN(2) + attachVisualizationResponses should contain( + Api.Response(requestId, Api.VisualizationAttached()) + ) + val expectedExpressionId = context.Main.idMainZ + val Some(data) = attachVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `expectedExpressionId` + ), + data + ) + ) => + data + } + new String(data) shouldEqual "50" + + // push foo call + val item2 = Api.StackItem.LocalCall(context.Main.idMainY) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item2)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 4 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.fooY(contextId), + context.Main.Update.fooZ(contextId), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + context.Main.idFooZ, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Enso_Test.Test.Visualization", + "encode" + ), + "Enso_Test.Test.Visualization" + ) + ) + ) + ) + val attachVisualizationResponses2 = context.receiveN(2) + attachVisualizationResponses2 should contain( + Api.Response(requestId, Api.VisualizationAttached()) + ) + val expectedExpressionId2 = context.Main.idFooZ + val Some(data2) = attachVisualizationResponses2.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `expectedExpressionId2` + ), + data + ) + ) => + data + } + new String(data2) shouldEqual "45" + + // Modify the file + context.send( + Api.Request( + Api.EditFileNotification( + mainFile, + Seq( + TextEdit( + model.Range(model.Position(10, 15), model.Position(10, 16)), + "5" + ) + ), + execute = true + ) + ) + ) + + val editFileResponse = context.receiveNIgnorePendingExpressionUpdates(4) + editFileResponse should contain allOf ( + TestMessages.update( contextId, - Some( - Api.InvalidatedExpressions.Expressions(Vector(context.Main.idMainX)) - ), - None - ) - ) - ) - val recomputeResponses2 = context.receiveNIgnoreExpressionUpdates(3) - recomputeResponses2 should contain allOf ( - Api.Response(requestId, Api.RecomputeContextResponse(contextId)), - context.executionComplete(contextId) - ) - val Some(data2) = recomputeResponses2.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `expectedExpressionId` - ), - data - ) - ) => - data - } - data2.sameElements("6".getBytes) shouldBe true - } - - it should "emit visualization update when expression is modified" in { - val contents = context.Main.code - val moduleName = "Enso_Test.Test.Main" - val mainFile = context.writeMain(contents) - val visualizationFile = - context.writeInSrcDir("Visualization", context.Visualization.code) - - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - - // open files - context.send( - Api.Request( - requestId, - Api.OpenFileRequest( - visualizationFile, - context.Visualization.code - ) - ) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - - context.receiveNIgnorePendingExpressionUpdates( - 5 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.mainX(contextId), - context.Main.Update.mainY(contextId), - context.Main.Update.mainZ(contextId), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - context.Main.idMainX, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - "Enso_Test.Test.Visualization", - "x -> encode x" - ), - "Enso_Test.Test.Visualization" - ) - ) - ) - ) - val attachVisualizationResponses = context.receiveN(2) - attachVisualizationResponses should contain( - Api.Response(requestId, Api.VisualizationAttached()) - ) - val expectedExpressionId = context.Main.idMainX - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `expectedExpressionId` - ), - data - ) - ) => - data - } - data.sameElements("6".getBytes) shouldBe true - - // Modify the file - context.send( - Api.Request( - Api.EditFileNotification( - mainFile, - Seq( - TextEdit( - model.Range(model.Position(4, 8), model.Position(4, 9)), - "5" + context.Main.idFooY, + ConstantsGen.INTEGER, + methodCall = Some( + Api.MethodCall( + Api.MethodPointer( + "Standard.Base.Data.Numbers", + "Standard.Base.Data.Numbers.Integer", + "+" + ) ) ), - execute = true - ) - ) - ) - - val editFileResponse = context.receiveNIgnoreExpressionUpdates(2) - editFileResponse should contain( - context.executionComplete(contextId) - ) - val Some(data1) = editFileResponse.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `expectedExpressionId` - ), - data - ) - ) => - data - } - data1.sameElements("5".getBytes) shouldBe true - } - - it should "emit visualization update when transitive expression is modified" in { - val contents = context.Main.code - val moduleName = "Enso_Test.Test.Main" - val mainFile = context.writeMain(contents) - val visualizationFile = - context.writeInSrcDir("Visualization", context.Visualization.code) - - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - - // open files - context.send( - Api.Request( - requestId, - Api.OpenFileRequest( - visualizationFile, - context.Visualization.code - ) - ) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - - context.receiveNIgnorePendingExpressionUpdates( - 5 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.mainX(contextId), - context.Main.Update.mainY(contextId), - context.Main.Update.mainZ(contextId), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - context.Main.idMainZ, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - "Enso_Test.Test.Visualization", - "encode" - ), - "Enso_Test.Test.Visualization" - ) - ) - ) - ) - val attachVisualizationResponses = context.receiveN(2) - attachVisualizationResponses should contain( - Api.Response(requestId, Api.VisualizationAttached()) - ) - val expectedExpressionId = context.Main.idMainZ - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `expectedExpressionId` - ), - data - ) - ) => - data - } - data.sameElements("50".getBytes) shouldBe true - - // Modify the file - context.send( - Api.Request( - Api.EditFileNotification( - mainFile, - Seq( - TextEdit( - model.Range(model.Position(4, 8), model.Position(4, 9)), - "5" - ) - ), - execute = true - ) - ) - ) - - val editFileResponse = context.receiveNIgnoreExpressionUpdates(2) - editFileResponse should contain( - context.executionComplete(contextId) - ) - val Some(data1) = editFileResponse.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `expectedExpressionId` - ), - data - ) - ) => - data - } - data1.sameElements("45".getBytes) shouldBe true - } - - it should "emit visualization update when frame popped" in { - val contents = context.Main.code - val moduleName = "Enso_Test.Test.Main" - val mainFile = context.writeMain(contents) - val visualizationFile = - context.writeInSrcDir("Visualization", context.Visualization.code) - - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - - // open files - context.send( - Api.Request( - requestId, - Api.OpenFileRequest( - visualizationFile, - context.Visualization.code - ) - ) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - - context.receiveNIgnorePendingExpressionUpdates( - 5 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.mainX(contextId), - context.Main.Update.mainY(contextId), - context.Main.Update.mainZ(contextId), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - context.Main.idMainZ, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - "Enso_Test.Test.Visualization", - "encode" - ), - "Enso_Test.Test.Visualization" - ) - ) - ) - ) - val attachVisualizationResponses = context.receiveN(2) - attachVisualizationResponses should contain( - Api.Response(requestId, Api.VisualizationAttached()) - ) - val expectedExpressionId = context.Main.idMainZ - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `expectedExpressionId` - ), - data - ) - ) => - data - } - new String(data) shouldEqual "50" - - // push foo call - val item2 = Api.StackItem.LocalCall(context.Main.idMainY) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item2)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 4 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.fooY(contextId), - context.Main.Update.fooZ(contextId), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, + typeChanged = false + ), + TestMessages.update( + contextId, context.Main.idFooZ, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - "Enso_Test.Test.Visualization", - "encode" - ), - "Enso_Test.Test.Visualization" - ) - ) - ) - ) - val attachVisualizationResponses2 = context.receiveN(2) - attachVisualizationResponses2 should contain( - Api.Response(requestId, Api.VisualizationAttached()) - ) - val expectedExpressionId2 = context.Main.idFooZ - val Some(data2) = attachVisualizationResponses2.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `expectedExpressionId2` - ), - data - ) - ) => - data - } - new String(data2) shouldEqual "45" - - // Modify the file - context.send( - Api.Request( - Api.EditFileNotification( - mainFile, - Seq( - TextEdit( - model.Range(model.Position(10, 15), model.Position(10, 16)), - "5" + ConstantsGen.INTEGER, + methodCall = Some( + Api.MethodCall( + Api.MethodPointer( + "Standard.Base.Data.Numbers", + "Standard.Base.Data.Numbers.Integer", + "*" + ) ) ), - execute = true - ) + typeChanged = false + ), + context.executionComplete(contextId) ) - ) + val Some(data3) = editFileResponse.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `expectedExpressionId2` + ), + data + ) + ) => + data + } + new String(data3) shouldEqual "55" - val editFileResponse = context.receiveNIgnorePendingExpressionUpdates(4) - editFileResponse should contain allOf ( - TestMessages.update( - contextId, - context.Main.idFooY, - ConstantsGen.INTEGER, - methodCall = Some( - Api.MethodCall( - Api.MethodPointer( - "Standard.Base.Data.Numbers", - "Standard.Base.Data.Numbers.Integer", - "+" - ) - ) - ), - typeChanged = false - ), - TestMessages.update( - contextId, - context.Main.idFooZ, - ConstantsGen.INTEGER, - methodCall = Some( - Api.MethodCall( - Api.MethodPointer( - "Standard.Base.Data.Numbers", - "Standard.Base.Data.Numbers.Integer", - "*" - ) - ) - ), - typeChanged = false - ), - context.executionComplete(contextId) - ) - val Some(data3) = editFileResponse.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `expectedExpressionId2` - ), - data - ) - ) => - data - } - new String(data3) shouldEqual "55" + // pop foo call + context.send(Api.Request(requestId, Api.PopContextRequest(contextId))) + val popContextResponses = context.receiveNIgnorePendingExpressionUpdates( + 5 + ) + popContextResponses should contain allOf ( + Api.Response(requestId, Api.PopContextResponse(contextId)), + context.Main.Update.mainY(contextId, typeChanged = false), + context.Main.Update.mainZ(contextId, typeChanged = false), + context.executionComplete(contextId) + ) - // pop foo call - context.send(Api.Request(requestId, Api.PopContextRequest(contextId))) - val popContextResponses = context.receiveNIgnorePendingExpressionUpdates( - 5 - ) - popContextResponses should contain allOf ( - Api.Response(requestId, Api.PopContextResponse(contextId)), - context.Main.Update.mainY(contextId, typeChanged = false), - context.Main.Update.mainZ(contextId, typeChanged = false), - context.executionComplete(contextId) - ) - - val Some(data4) = popContextResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `expectedExpressionId` - ), - data - ) - ) => - data - } - new String(data4) shouldEqual "60" + val Some(data4) = popContextResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `expectedExpressionId` + ), + data + ) + ) => + data + } + new String(data4) shouldEqual "60" } - it should "be able to modify visualizations" in { + it should "be able to modify visualizations" in withContext() { context => val contents = context.Main.code val mainFile = context.writeMain(contents) val visualizationFile = @@ -1226,20 +1242,24 @@ class RuntimeVisualizationsTest dataAfterModification.sameElements("7".getBytes) shouldBe true } - it should "not emit visualization update when visualization is detached" in { + it should "be able to modify visualizations for pending visualizations" in withContext( + sequentialExecution = false + ) { context => val contents = context.Main.code val mainFile = context.writeMain(contents) val visualizationFile = context.writeInSrcDir("Visualization", context.Visualization.code) - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + val visualizationId2 = UUID.randomUUID() // open files context.send( Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) ) + context.receive shouldEqual Some( Api.Response(Some(requestId), Api.OpenFileResponse) ) @@ -1262,7 +1282,26 @@ class RuntimeVisualizationsTest Api.Response(requestId, Api.CreateContextResponse(contextId)) ) - // attach visualization + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer("Enso_Test.Test.Main", "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 5 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + context.executionComplete(contextId) + ) + + // attach visualizations context.send( Api.Request( requestId, @@ -1280,36 +1319,348 @@ class RuntimeVisualizationsTest ) ) ) - context.receiveN(2) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.VisualizationAttached()), - Api.Response( - Api.ExecutionFailed( - contextId, - Api.ExecutionResult.Failure("Execution stack is empty.", None) + + context.send( + Api.Request( + Api.EditFileNotification( + mainFile, + Seq( + TextEdit( + model.Range(model.Position(4, 8), model.Position(4, 9)), + "7" + ) + ), + execute = true ) ) ) - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer("Enso_Test.Test.Main", "Enso_Test.Test.Main", "main"), - None, - Vector() - ) context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId2, + context.Main.idMainX, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Enso_Test.Test.Visualization", + "x -> encode x" + ), + "Enso_Test.Test.Visualization" + ) + ) + ) ) - val pushResponses = context.receiveNIgnorePendingExpressionUpdates(6) - pushResponses should contain allOf ( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.mainX(contextId), - context.Main.Update.mainY(contextId), - context.Main.Update.mainZ(contextId), - context.executionComplete(contextId) + + val attachVisualizationResponses = context.receiveN(2) + + attachVisualizationResponses.filter( + _.payload.isInstanceOf[Api.VisualizationAttached] + ) shouldEqual List( + Api.Response(requestId, Api.VisualizationAttached()), + Api.Response(requestId, Api.VisualizationAttached()) ) + + val responses = context.receiveNIgnoreExpressionUpdates(3) + val visualizationUpdatesResponses = + responses.filter(_.payload.isInstanceOf[Api.VisualizationUpdate]) val expectedExpressionId = context.Main.idMainX - val Some(data) = - pushResponses.collectFirst { + val visualizationUpdates = visualizationUpdatesResponses.map( + _.payload.asInstanceOf[Api.VisualizationUpdate] + ) + val visContexts = visualizationUpdates.map(_.visualizationContext) + visContexts should contain allOf ( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `expectedExpressionId` + ), + Api.VisualizationContext( + `visualizationId2`, + `contextId`, + `expectedExpressionId` + ), + ) + val data = visualizationUpdates.head.data + data.sameElements("6".getBytes) shouldBe true + + // modify visualization + context.send( + Api.Request( + requestId, + Api.ModifyVisualization( + visualizationId, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Enso_Test.Test.Visualization", + "x -> incAndEncode x" + ), + "Enso_Test.Test.Visualization" + ) + ) + ) + ) + val modifyVisualizationResponses = context.receiveN(5) + modifyVisualizationResponses should contain( + Api.Response(requestId, Api.VisualizationModified()) + ) + val Some((dataAfterModification, foundVisualizatonId)) = + modifyVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + modifiedId, + `contextId`, + `expectedExpressionId` + ), + data + ) + ) => + (data, modifiedId) + } + + List(visualizationId, visualizationId2) should contain(foundVisualizatonId) + + dataAfterModification.sameElements("7".getBytes) shouldBe true + } + + it should "not emit visualization update when visualization is detached" in withContext() { + context => + val contents = context.Main.code + val mainFile = context.writeMain(contents) + val visualizationFile = + context.writeInSrcDir("Visualization", context.Visualization.code) + + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + + // open files + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + context.send( + Api.Request( + requestId, + Api.OpenFileRequest( + visualizationFile, + context.Visualization.code + ) + ) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + context.Main.idMainX, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Enso_Test.Test.Visualization", + "x -> encode x" + ), + "Enso_Test.Test.Visualization" + ) + ) + ) + ) + context.receiveN(2) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.VisualizationAttached()), + Api.Response( + Api.ExecutionFailed( + contextId, + Api.ExecutionResult.Failure("Execution stack is empty.", None) + ) + ) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer("Enso_Test.Test.Main", "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + val pushResponses = context.receiveNIgnorePendingExpressionUpdates(6) + pushResponses should contain allOf ( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + context.executionComplete(contextId) + ) + val expectedExpressionId = context.Main.idMainX + val Some(data) = + pushResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `expectedExpressionId` + ), + data + ) + ) => + data + } + data.sameElements("6".getBytes) shouldBe true + + // detach visualization + context.send( + Api.Request( + requestId, + Api.DetachVisualization( + contextId, + visualizationId, + context.Main.idMainX + ) + ) + ) + context.receive shouldEqual Some( + Api.Response(requestId, Api.VisualizationDetached()) + ) + + // recompute + context.send( + Api.Request( + requestId, + Api.RecomputeContextRequest(contextId, None, None) + ) + ) + context.receiveNIgnoreExpressionUpdates( + 2 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.RecomputeContextResponse(contextId)), + context.executionComplete(contextId) + ) + + // recompute invalidating x + context.send( + Api.Request( + requestId, + Api.RecomputeContextRequest( + contextId, + Some( + Api.InvalidatedExpressions.Expressions( + Vector(context.Main.idMainX) + ) + ), + None + ) + ) + ) + context.receiveNIgnoreExpressionUpdates( + 2 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.RecomputeContextResponse(contextId)), + context.executionComplete(contextId) + ) + } + + it should "not emit visualization update when expression is not affected by the change" in withContext() { + context => + val contents = context.Main.code + val moduleName = "Enso_Test.Test.Main" + val mainFile = context.writeMain(contents) + val visualizationFile = + context.writeInSrcDir("Visualization", context.Visualization.code) + + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + + // open files + context.send( + Api.Request( + requestId, + Api.OpenFileRequest( + visualizationFile, + context.Visualization.code + ) + ) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + + context.receiveNIgnorePendingExpressionUpdates( + 5 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + context.Main.idMainX, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Enso_Test.Test.Visualization", + "encode" + ), + "Enso_Test.Test.Visualization" + ) + ) + ) + ) + val attachVisualizationResponses = context.receiveN(2) + attachVisualizationResponses should contain( + Api.Response(requestId, Api.VisualizationAttached()) + ) + val expectedExpressionId = context.Main.idMainX + val Some(data) = attachVisualizationResponses.collectFirst { case Api.Response( None, Api.VisualizationUpdate( @@ -1323,175 +1674,32 @@ class RuntimeVisualizationsTest ) => data } - data.sameElements("6".getBytes) shouldBe true + data.sameElements("6".getBytes) shouldBe true - // detach visualization - context.send( - Api.Request( - requestId, - Api.DetachVisualization( - contextId, - visualizationId, - context.Main.idMainX - ) - ) - ) - context.receive shouldEqual Some( - Api.Response(requestId, Api.VisualizationDetached()) - ) - - // recompute - context.send( - Api.Request(requestId, Api.RecomputeContextRequest(contextId, None, None)) - ) - context.receiveNIgnoreExpressionUpdates( - 2 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.RecomputeContextResponse(contextId)), - context.executionComplete(contextId) - ) - - // recompute invalidating x - context.send( - Api.Request( - requestId, - Api.RecomputeContextRequest( - contextId, - Some( - Api.InvalidatedExpressions.Expressions(Vector(context.Main.idMainX)) - ), - None - ) - ) - ) - context.receiveNIgnoreExpressionUpdates( - 2 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.RecomputeContextResponse(contextId)), - context.executionComplete(contextId) - ) - } - - it should "not emit visualization update when expression is not affected by the change" in { - val contents = context.Main.code - val moduleName = "Enso_Test.Test.Main" - val mainFile = context.writeMain(contents) - val visualizationFile = - context.writeInSrcDir("Visualization", context.Visualization.code) - - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - - // open files - context.send( - Api.Request( - requestId, - Api.OpenFileRequest( - visualizationFile, - context.Visualization.code - ) - ) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - - context.receiveNIgnorePendingExpressionUpdates( - 5 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.mainX(contextId), - context.Main.Update.mainY(contextId), - context.Main.Update.mainZ(contextId), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - context.Main.idMainX, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - "Enso_Test.Test.Visualization", - "encode" + // Modify the file + context.send( + Api.Request( + Api.EditFileNotification( + mainFile, + Seq( + TextEdit( + model.Range(model.Position(6, 12), model.Position(6, 13)), + "6" + ) ), - "Enso_Test.Test.Visualization" + execute = true ) ) ) - ) - val attachVisualizationResponses = context.receiveN(2) - attachVisualizationResponses should contain( - Api.Response(requestId, Api.VisualizationAttached()) - ) - val expectedExpressionId = context.Main.idMainX - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `expectedExpressionId` - ), - data - ) - ) => - data - } - data.sameElements("6".getBytes) shouldBe true - // Modify the file - context.send( - Api.Request( - Api.EditFileNotification( - mainFile, - Seq( - TextEdit( - model.Range(model.Position(6, 12), model.Position(6, 13)), - "6" - ) - ), - execute = true - ) + context.receiveNIgnoreExpressionUpdates( + 1 + ) should contain theSameElementsAs Seq( + context.executionComplete(contextId) ) - ) - - context.receiveNIgnoreExpressionUpdates( - 1 - ) should contain theSameElementsAs Seq( - context.executionComplete(contextId) - ) } - it should "not reorder visualization commands" in { + it should "not reorder visualization commands" in withContext() { context => val contents = context.Main.code val mainFile = context.writeMain(contents) val visualizationFile = @@ -1638,710 +1846,719 @@ class RuntimeVisualizationsTest dataAfterModification.sameElements("7".getBytes) shouldBe true } - it should "return ModuleNotFound error when attaching visualization" in { - val idMain = context.Main.metadata.addItem(99, 1) - val contents = context.Main.code - val mainFile = context.writeMain(context.Main.code) - val moduleName = "Enso_Test.Test.Main" + it should "return ModuleNotFound error when attaching visualization" in withContext() { + context => + val idMain = context.Main.metadata.addItem(99, 1) + val contents = context.Main.code + val mainFile = context.writeMain(context.Main.code) + val moduleName = "Enso_Test.Test.Main" - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 6 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.mainX(contextId), - context.Main.Update.mainY(contextId), - context.Main.Update.mainZ(contextId), - TestMessages.update(contextId, idMain, ConstantsGen.INTEGER), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - idMain, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - "Test.Undefined", - "x -> x" - ), - "Test.Undefined" - ) - ) + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) ) - ) - context.receiveN(2) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.VisualizationAttached()), - Api.Response(Api.ModuleNotFound("Test.Undefined")) - ) - } - it should "be able to use external libraries if they are needed by the visualization" in { - val idMain = context.Main.metadata.addItem(99, 1) - val contents = context.Main.code - val mainFile = context.writeMain(context.Main.code) - val moduleName = "Enso_Test.Test.Main" - - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 6 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.mainX(contextId), - context.Main.Update.mainY(contextId), - context.Main.Update.mainZ(contextId), - TestMessages.update(contextId, idMain, ConstantsGen.INTEGER), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - idMain, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - "Standard.Visualization.Main", - "x -> x.default_visualization.to_text" - ), - "Standard.Visualization.Main" - ) - ) + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) ) - ) - val attachVisualizationResponses = context.receiveN(8) - attachVisualizationResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 6 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + TestMessages.update(contextId, idMain, ConstantsGen.INTEGER), + context.executionComplete(contextId) + ) - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMain` + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + idMain, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Test.Undefined", + "x -> x" ), - data + "Test.Undefined" ) - ) => - data - } - - data.sameElements("(Builtin 'JSON')".getBytes) shouldBe true - - val loadedLibraries = attachVisualizationResponses - .collect { - case Api.Response(None, Api.LibraryLoaded(namespace, name, _, _)) => - Some((namespace, name)) - case _ => None - } - .filter(_.isDefined) - .flatten - - loadedLibraries should contain(("Standard", "Visualization")) - } - - it should "return VisualizationExpressionFailed error when attaching visualization" in { - val idMain = context.Main.metadata.addItem(99, 1) - val contents = context.Main.code - val mainFile = context.writeMain(context.Main.code) - val moduleName = "Enso_Test.Test.Main" - - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 6 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.mainX(contextId), - context.Main.Update.mainY(contextId), - context.Main.Update.mainZ(contextId), - TestMessages.update(contextId, idMain, ConstantsGen.INTEGER), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - idMain, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - "Enso_Test.Test.Main", - "Main.does_not_exist" - ), - "Enso_Test.Test.Main" ) ) ) - ) - context.receiveN(2) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.VisualizationAttached()), - Api.Response( - Api.VisualizationExpressionFailed( - Api.VisualizationContext(visualizationId, contextId, idMain), - "Method `does_not_exist` of type Main could not be found.", - Some( - Api.ExecutionResult.Diagnostic.error( - message = - "Method `does_not_exist` of type Main could not be found.", - stack = Vector( + context.receiveN(2) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.VisualizationAttached()), + Api.Response(Api.ModuleNotFound("Test.Undefined")) + ) + } + + it should "be able to use external libraries if they are needed by the visualization" in withContext() { + context => + val idMain = context.Main.metadata.addItem(99, 1) + val contents = context.Main.code + val mainFile = context.writeMain(context.Main.code) + val moduleName = "Enso_Test.Test.Main" + + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 6 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + TestMessages.update(contextId, idMain, ConstantsGen.INTEGER), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + idMain, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Standard.Visualization.Main", + "x -> x.default_visualization.to_text" + ), + "Standard.Visualization.Main" + ) + ) + ) + ) + + val attachVisualizationResponses = context.receiveN(8) + attachVisualizationResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) + ) + + val Some(data) = attachVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMain` + ), + data + ) + ) => + data + } + + data.sameElements("(Builtin 'JSON')".getBytes) shouldBe true + + val loadedLibraries = attachVisualizationResponses + .collect { + case Api.Response(None, Api.LibraryLoaded(namespace, name, _, _)) => + Some((namespace, name)) + case _ => None + } + .filter(_.isDefined) + .flatten + + loadedLibraries should contain(("Standard", "Visualization")) + } + + it should "return VisualizationExpressionFailed error when attaching visualization" in withContext() { + context => + val idMain = context.Main.metadata.addItem(99, 1) + val contents = context.Main.code + val mainFile = context.writeMain(context.Main.code) + val moduleName = "Enso_Test.Test.Main" + + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 6 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + TestMessages.update(contextId, idMain, ConstantsGen.INTEGER), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + idMain, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Enso_Test.Test.Main", + "Main.does_not_exist" + ), + "Enso_Test.Test.Main" + ) + ) + ) + ) + context.receiveN(2) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.VisualizationAttached()), + Api.Response( + Api.VisualizationExpressionFailed( + Api.VisualizationContext(visualizationId, contextId, idMain), + "Method `does_not_exist` of type Main could not be found.", + Some( + Api.ExecutionResult.Diagnostic.error( + message = + "Method `does_not_exist` of type Main could not be found.", + stack = Vector( // empty stack for now // Api.StackTraceElement("", None, None, None), // Api.StackTraceElement("Debug.eval", None, None, None) - ) - ) - ) - ) - ) - ) - } - - it should "return visualization evaluation errors with diagnostic info" in { - val idMain = context.Main.metadata.addItem(99, 1) - val contents = context.Main.code - val mainFile = context.writeMain(context.Main.code) - val moduleName = "Enso_Test.Test.Main" - - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 6 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.mainX(contextId), - context.Main.Update.mainY(contextId), - context.Main.Update.mainZ(contextId), - TestMessages.update(contextId, idMain, ConstantsGen.INTEGER), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - idMain, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - moduleName, - "x -> x.visualise_me" - ), - moduleName - ) - ) - ) - ) - context.receiveNIgnoreExpressionUpdates( - 3 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.VisualizationAttached()), - Api.Response( - Api.VisualizationEvaluationFailed( - Api.VisualizationContext( - visualizationId, - contextId, - idMain - ), - "Method `visualise_me` of type Integer could not be found.", - Some( - Api.ExecutionResult.Diagnostic.error( - "Method `visualise_me` of type Integer could not be found.", - None, - Some(model.Range(model.Position(0, 5), model.Position(0, 19))), - None, - Vector( - Api.StackTraceElement( - "", - None, - Some( - model.Range(model.Position(0, 5), model.Position(0, 19)) - ), - None ) ) ) ) ) - ), - context.executionComplete(contextId) - ) + ) } - it should "return visualization error with a stack trace" in { - val idMain = context.Main.metadata.addItem(99, 1) - val contents = context.Main.code - val mainFile = context.writeMain(context.Main.code) - val moduleName = "Enso_Test.Test.Main" - val visualizationCode = - """ - |encode x = x.visualise_me - | - |inc_and_encode x = encode x+1 - |""".stripMargin.linesIterator.mkString("\n") + it should "return visualization evaluation errors with diagnostic info" in withContext() { + context => + val idMain = context.Main.metadata.addItem(99, 1) + val contents = context.Main.code + val mainFile = context.writeMain(context.Main.code) + val moduleName = "Enso_Test.Test.Main" - val visualizationFile = - context.writeInSrcDir("Visualization", visualizationCode) + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - - context.send( - Api.Request( - requestId, - Api.OpenFileRequest( - visualizationFile, - visualizationCode - ) + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) ) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 6 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.mainX(contextId), - context.Main.Update.mainY(contextId), - context.Main.Update.mainZ(contextId), - TestMessages.update(contextId, idMain, ConstantsGen.INTEGER), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - idMain, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - "Enso_Test.Test.Visualization", - "inc_and_encode" - ), - "Enso_Test.Test.Visualization" - ) - ) + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) ) - ) - context.receiveNIgnoreExpressionUpdates( - 3 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.VisualizationAttached()), - Api.Response( - Api.VisualizationEvaluationFailed( - Api.VisualizationContext( + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 6 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + TestMessages.update(contextId, idMain, ConstantsGen.INTEGER), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( visualizationId, - contextId, - idMain - ), - "Method `visualise_me` of type Integer could not be found.", - Some( - Api.ExecutionResult.Diagnostic.error( - "Method `visualise_me` of type Integer could not be found.", - Some(visualizationFile), - Some(model.Range(model.Position(1, 11), model.Position(1, 25))), - None, - Vector( - Api.StackTraceElement( - "Visualization.encode", - Some(visualizationFile), - Some( - model.Range(model.Position(1, 11), model.Position(1, 25)) - ), - None - ), - Api.StackTraceElement( - "Visualization.inc_and_encode", - Some(visualizationFile), - Some( - model.Range(model.Position(3, 19), model.Position(3, 29)) - ), - None - ) - ) - ) - ) - ) - ), - context.executionComplete(contextId) - ) - } - - it should "run visualization expression catching error" in { - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - val moduleName = "Enso_Test.Test.Main" - val metadata = new Metadata - - val idMain = metadata.addItem(42, 14) - - val code = - """from Standard.Base import all - | - |main = - | Error.throw 42 - |""".stripMargin.linesIterator.mkString("\n") - val contents = metadata.appendToCode(code) - val mainFile = context.writeMain(contents) - - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnoreStdLib(3) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - TestMessages.error( - contextId, - idMain, - Api.MethodCall( - Api.MethodPointer( - "Standard.Base.Error", - "Standard.Base.Error.Error", - "throw" - ) - ), - Api.ExpressionUpdate.Payload.DataflowError(Seq(idMain)) - ), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - idMain, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - moduleName, - "x -> x.catch_primitive _.to_text" - ), - moduleName - ) - ) - ) - ) - val attachVisualizationResponses = context.receiveN(4, timeoutSeconds = 60) - attachVisualizationResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMain` + idMain, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + moduleName, + "x -> x.visualise_me" ), - data + moduleName ) - ) => - data - } - data.sameElements("42".getBytes) shouldBe true - } - - it should "run visualization expression propagating panic" in { - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - val moduleName = "Enso_Test.Test.Main" - val metadata = new Metadata - - val idMain = metadata.addItem(42, 14) - - val code = - """from Standard.Base import all - | - |main = - | Panic.throw 42 - |""".stripMargin.linesIterator.mkString("\n") - val contents = metadata.appendToCode(code) - val mainFile = context.writeMain(contents) - - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 3 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - TestMessages.panic( - contextId, - idMain, - Api.MethodCall( - Api.MethodPointer( - "Standard.Base.Panic", - "Standard.Base.Panic.Panic", - "throw" - ) - ), - Api.ExpressionUpdate.Payload.Panic("Integer", Seq(idMain)), - Some("Standard.Base.Panic.Panic") - ), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - idMain, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - moduleName, - "x -> Panic.catch_primitive x caught_panic-> caught_panic.payload.to_text" - ), - moduleName ) ) ) - ) - context.receiveNIgnorePendingExpressionUpdates( - 4 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.VisualizationAttached()), - TestMessages.panic( - contextId, - idMain, - Api.MethodCall( - Api.MethodPointer( - "Standard.Base.Panic", - "Standard.Base.Panic.Panic", - "throw" - ) - ), - Api.ExpressionUpdate.Payload.Panic("Integer", Seq(idMain)), - builtin = false - ), - Api.Response( - Api.VisualizationEvaluationFailed( - Api.VisualizationContext( - visualizationId, - contextId, - idMain - ), - "42", - Some( - Api.ExecutionResult.Diagnostic.error( - message = "42", - file = Some(mainFile), - location = - Some(model.Range(model.Position(3, 4), model.Position(3, 18))), - expressionId = Some(idMain), - stack = Vector( - Api.StackTraceElement( - "Main.main", - Some(mainFile), - Some( - model.Range(model.Position(3, 4), model.Position(3, 18)) - ), - Some(idMain) + context.receiveNIgnoreExpressionUpdates( + 3 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.VisualizationAttached()), + Api.Response( + Api.VisualizationEvaluationFailed( + Api.VisualizationContext( + visualizationId, + contextId, + idMain + ), + "Method `visualise_me` of type Integer could not be found.", + Some( + Api.ExecutionResult.Diagnostic.error( + "Method `visualise_me` of type Integer could not be found.", + None, + Some(model.Range(model.Position(0, 5), model.Position(0, 19))), + None, + Vector( + Api.StackTraceElement( + "", + None, + Some( + model.Range(model.Position(0, 5), model.Position(0, 19)) + ), + None + ) ) ) ) ) - ) - ), - context.executionComplete(contextId) - ) + ), + context.executionComplete(contextId) + ) } - it should "run visualization error preprocessor" in { + it should "return visualization error with a stack trace" in withContext() { + context => + val idMain = context.Main.metadata.addItem(99, 1) + val contents = context.Main.code + val mainFile = context.writeMain(context.Main.code) + val moduleName = "Enso_Test.Test.Main" + val visualizationCode = + """ + |encode x = x.visualise_me + | + |inc_and_encode x = encode x+1 + |""".stripMargin.linesIterator.mkString("\n") + + val visualizationFile = + context.writeInSrcDir("Visualization", visualizationCode) + + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + + context.send( + Api.Request( + requestId, + Api.OpenFileRequest( + visualizationFile, + visualizationCode + ) + ) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 6 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + TestMessages.update(contextId, idMain, ConstantsGen.INTEGER), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + idMain, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Enso_Test.Test.Visualization", + "inc_and_encode" + ), + "Enso_Test.Test.Visualization" + ) + ) + ) + ) + context.receiveNIgnoreExpressionUpdates( + 3 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.VisualizationAttached()), + Api.Response( + Api.VisualizationEvaluationFailed( + Api.VisualizationContext( + visualizationId, + contextId, + idMain + ), + "Method `visualise_me` of type Integer could not be found.", + Some( + Api.ExecutionResult.Diagnostic.error( + "Method `visualise_me` of type Integer could not be found.", + Some(visualizationFile), + Some(model.Range(model.Position(1, 11), model.Position(1, 25))), + None, + Vector( + Api.StackTraceElement( + "Visualization.encode", + Some(visualizationFile), + Some( + model.Range(model.Position(1, 11), model.Position(1, 25)) + ), + None + ), + Api.StackTraceElement( + "Visualization.inc_and_encode", + Some(visualizationFile), + Some( + model.Range(model.Position(3, 19), model.Position(3, 29)) + ), + None + ) + ) + ) + ) + ) + ), + context.executionComplete(contextId) + ) + } + + it should "run visualization expression catching error" in withContext() { + context => + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val metadata = new Metadata + + val idMain = metadata.addItem(42, 14) + + val code = + """from Standard.Base import all + | + |main = + | Error.throw 42 + |""".stripMargin.linesIterator.mkString("\n") + val contents = metadata.appendToCode(code) + val mainFile = context.writeMain(contents) + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnoreStdLib(3) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.error( + contextId, + idMain, + Api.MethodCall( + Api.MethodPointer( + "Standard.Base.Error", + "Standard.Base.Error.Error", + "throw" + ) + ), + Api.ExpressionUpdate.Payload.DataflowError(Seq(idMain)) + ), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + idMain, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + moduleName, + "x -> x.catch_primitive _.to_text" + ), + moduleName + ) + ) + ) + ) + val attachVisualizationResponses = + context.receiveN(4, timeoutSeconds = 60) + attachVisualizationResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) + ) + val Some(data) = attachVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMain` + ), + data + ) + ) => + data + } + data.sameElements("42".getBytes) shouldBe true + } + + it should "run visualization expression propagating panic" in withContext() { + context => + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val metadata = new Metadata + + val idMain = metadata.addItem(42, 14) + + val code = + """from Standard.Base import all + | + |main = + | Panic.throw 42 + |""".stripMargin.linesIterator.mkString("\n") + val contents = metadata.appendToCode(code) + val mainFile = context.writeMain(contents) + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 3 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.panic( + contextId, + idMain, + Api.MethodCall( + Api.MethodPointer( + "Standard.Base.Panic", + "Standard.Base.Panic.Panic", + "throw" + ) + ), + Api.ExpressionUpdate.Payload.Panic("Integer", Seq(idMain)), + Some("Standard.Base.Panic.Panic") + ), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + idMain, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + moduleName, + "x -> Panic.catch_primitive x caught_panic-> caught_panic.payload.to_text" + ), + moduleName + ) + ) + ) + ) + context.receiveNIgnorePendingExpressionUpdates( + 4 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.VisualizationAttached()), + TestMessages.panic( + contextId, + idMain, + Api.MethodCall( + Api.MethodPointer( + "Standard.Base.Panic", + "Standard.Base.Panic.Panic", + "throw" + ) + ), + Api.ExpressionUpdate.Payload.Panic("Integer", Seq(idMain)), + builtin = false + ), + Api.Response( + Api.VisualizationEvaluationFailed( + Api.VisualizationContext( + visualizationId, + contextId, + idMain + ), + "42", + Some( + Api.ExecutionResult.Diagnostic.error( + message = "42", + file = Some(mainFile), + location = Some( + model.Range(model.Position(3, 4), model.Position(3, 18)) + ), + expressionId = Some(idMain), + stack = Vector( + Api.StackTraceElement( + "Main.main", + Some(mainFile), + Some( + model.Range(model.Position(3, 4), model.Position(3, 18)) + ), + Some(idMain) + ) + ) + ) + ) + ) + ), + context.executionComplete(contextId) + ) + } + + it should "run visualization error preprocessor" in withContext() { context => val contextId = UUID.randomUUID() val requestId = UUID.randomUUID() val visualizationId = UUID.randomUUID() @@ -2451,400 +2668,197 @@ class RuntimeVisualizationsTest stringified shouldEqual """{"kind":"Dataflow","message":"The List is empty. (at Main.main(Enso_Test.Test.Main:6:5-38)"}""" } - it should "run visualization default preprocessor" in { - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - val moduleName = "Enso_Test.Test.Main" - val metadata = new Metadata + it should "run visualization default preprocessor" in withContext() { + context => + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val metadata = new Metadata - val idMain = metadata.addItem(47, 6) + val idMain = metadata.addItem(47, 6) - val code = - """import Standard.Visualization - | - |main = - | fn = x -> x - | fn - |""".stripMargin.linesIterator.mkString("\n") - val contents = metadata.appendToCode(code) - val mainFile = context.writeMain(contents) + val code = + """import Standard.Visualization + | + |main = + | fn = x -> x + | fn + |""".stripMargin.linesIterator.mkString("\n") + val contents = metadata.appendToCode(code) + val mainFile = context.writeMain(contents) - val visualizationModule = "Standard.Visualization.Preprocessor" - val visualizationFunction = "default_preprocessor" + val visualizationModule = "Standard.Visualization.Preprocessor" + val visualizationFunction = "default_preprocessor" - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, moduleName, "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 3 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - TestMessages.update( - contextId, - idMain, - ConstantsGen.FUNCTION - ), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, moduleName, "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 3 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.update( + contextId, idMain, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.ModuleMethod( - Api.MethodPointer( - visualizationModule, - visualizationModule, - visualizationFunction + ConstantsGen.FUNCTION + ), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + idMain, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.ModuleMethod( + Api.MethodPointer( + visualizationModule, + visualizationModule, + visualizationFunction + ), + Vector() ), - Vector() - ), - visualizationModule + visualizationModule + ) ) ) ) - ) - val attachVisualizationResponses = - context.receiveNIgnoreExpressionUpdates(2) - attachVisualizationResponses should contain( - Api.Response(requestId, Api.VisualizationAttached()) - ) - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMain` - ), - data - ) - ) => - data - } - val stringified = new String(data) - stringified shouldEqual "\"Function\"" + val attachVisualizationResponses = + context.receiveNIgnoreExpressionUpdates(2) + attachVisualizationResponses should contain( + Api.Response(requestId, Api.VisualizationAttached()) + ) + val Some(data) = attachVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMain` + ), + data + ) + ) => + data + } + val stringified = new String(data) + stringified shouldEqual "\"Function\"" } - it should "attach method pointer visualization without arguments" in { - val idMainRes = context.Main.metadata.addItem(99, 1) - val contents = context.Main.code - val mainFile = context.writeMain(context.Main.code) - val visualizationFile = - context.writeInSrcDir("Visualization", context.Visualization.code) + it should "attach method pointer visualization without arguments" in withContext() { + context => + val idMainRes = context.Main.metadata.addItem(99, 1) + val contents = context.Main.code + val mainFile = context.writeMain(context.Main.code) + val visualizationFile = + context.writeInSrcDir("Visualization", context.Visualization.code) - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() - context.send( - Api.Request( - requestId, - Api.OpenFileRequest( - visualizationFile, - context.Visualization.code - ) - ) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer("Enso_Test.Test.Main", "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 6 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.mainX(contextId), - context.Main.Update.mainY(contextId), - context.Main.Update.mainZ(contextId), - TestMessages.update(contextId, idMainRes, ConstantsGen.INTEGER), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - idMainRes, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.ModuleMethod( - Api.MethodPointer( - "Enso_Test.Test.Visualization", - "Enso_Test.Test.Visualization", - "incAndEncode" - ), - Vector() - ), - "Enso_Test.Test.Visualization" + context.send( + Api.Request( + requestId, + Api.OpenFileRequest( + visualizationFile, + context.Visualization.code ) ) ) - ) - val attachVisualizationResponses = - context.receiveNIgnoreExpressionUpdates(3) - attachVisualizationResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMainRes` - ), - data - ) - ) => - data - } - data.sameElements("51".getBytes) shouldBe true - - // recompute - context.send( - Api.Request(requestId, Api.RecomputeContextRequest(contextId, None, None)) - ) - - val recomputeResponses = context.receiveNIgnoreExpressionUpdates(3) - recomputeResponses should contain allOf ( - Api.Response(requestId, Api.RecomputeContextResponse(contextId)), - context.executionComplete(contextId) - ) - val Some(data2) = recomputeResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMainRes` - ), - data - ) - ) => - data - } - data2.sameElements("51".getBytes) shouldBe true - } - - it should "attach method pointer visualization with arguments" in { - val idMainRes = context.Main.metadata.addItem(99, 1) - val contents = context.Main.code - val mainFile = context.writeMain(context.Main.code) - val moduleName = "Enso_Test.Test.Main" - val visualizationFile = - context.writeInSrcDir( - "Visualization", - context.AnnotatedVisualization.code + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) ) - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - - context.send( - Api.Request( - requestId, - Api.OpenFileRequest( - visualizationFile, - context.AnnotatedVisualization.code - ) + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) ) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer("Enso_Test.Test.Main", "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 6 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + TestMessages.update(contextId, idMainRes, ConstantsGen.INTEGER), + context.executionComplete(contextId) + ) - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 6 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.mainX(contextId), - context.Main.Update.mainY(contextId), - context.Main.Update.mainZ(contextId), - TestMessages.update(contextId, idMainRes, ConstantsGen.INTEGER), - context.executionComplete(contextId) - ) - context.consumeOut shouldEqual List() - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - idMainRes, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.ModuleMethod( - Api.MethodPointer( - "Enso_Test.Test.Visualization", - "Enso_Test.Test.Visualization", - "incAndEncode" + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + idMainRes, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.ModuleMethod( + Api.MethodPointer( + "Enso_Test.Test.Visualization", + "Enso_Test.Test.Visualization", + "incAndEncode" + ), + Vector() ), - Vector("2", "3") - ), - "Enso_Test.Test.Visualization" + "Enso_Test.Test.Visualization" + ) ) ) ) - ) - val attachVisualizationResponses = - context.receiveNIgnoreExpressionUpdates(3) - attachVisualizationResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMainRes` - ), - data - ) - ) => - data - } - data.sameElements("103".getBytes) shouldBe true - context.consumeOut shouldEqual List("encoding...") - - // recompute - context.send( - Api.Request(requestId, Api.RecomputeContextRequest(contextId, None, None)) - ) - - val recomputeResponses = context.receiveNIgnoreExpressionUpdates(3) - recomputeResponses should contain allOf ( - Api.Response(requestId, Api.RecomputeContextResponse(contextId)), - context.executionComplete(contextId) - ) - val Some(data2) = recomputeResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMainRes` - ), - data - ) - ) => - data - } - data2.sameElements("103".getBytes) shouldBe true - context.consumeOut shouldEqual List() - - // modify visualization - context.send( - Api.Request( - requestId, - Api.ModifyVisualization( - visualizationId, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.ModuleMethod( - Api.MethodPointer( - "Enso_Test.Test.Visualization", - "Enso_Test.Test.Visualization", - "incAndEncode" - ), - Vector("2", "4") - ), - "Enso_Test.Test.Visualization" - ) - ) + val attachVisualizationResponses = + context.receiveNIgnoreExpressionUpdates(3) + attachVisualizationResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) ) - ) - val modifyVisualizationResponses = - context.receiveNIgnoreExpressionUpdates(2) - modifyVisualizationResponses should contain( - Api.Response(requestId, Api.VisualizationModified()) - ) - val Some(data3) = - modifyVisualizationResponses.collectFirst { + val Some(data) = attachVisualizationResponses.collectFirst { case Api.Response( None, Api.VisualizationUpdate( @@ -2858,1251 +2872,1478 @@ class RuntimeVisualizationsTest ) => data } - data3.sameElements("104".getBytes) shouldBe true - context.consumeOut shouldEqual List("encoding...") - } + data.sameElements("51".getBytes) shouldBe true - it should "cache intermediate visualization expressions" in { - val idMainRes = context.Main.metadata.addItem(99, 1) - val contents = context.Main.code - val mainFile = context.writeMain(context.Main.code) - val moduleName = "Enso_Test.Test.Main" - val visualizationFile = - context.writeInSrcDir( - "Visualization", - context.AnnotatedVisualization.code + // recompute + context.send( + Api.Request( + requestId, + Api.RecomputeContextRequest(contextId, None, None) + ) ) - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() + val recomputeResponses = context.receiveNIgnoreExpressionUpdates(3) + recomputeResponses should contain allOf ( + Api.Response(requestId, Api.RecomputeContextResponse(contextId)), + context.executionComplete(contextId) + ) + val Some(data2) = recomputeResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMainRes` + ), + data + ) + ) => + data + } + data2.sameElements("51".getBytes) shouldBe true + } - context.send( - Api.Request( - requestId, - Api.OpenFileRequest( - visualizationFile, + it should "attach method pointer visualization with arguments" in withContext() { + context => + val idMainRes = context.Main.metadata.addItem(99, 1) + val contents = context.Main.code + val mainFile = context.writeMain(context.Main.code) + val moduleName = "Enso_Test.Test.Main" + val visualizationFile = + context.writeInSrcDir( + "Visualization", context.AnnotatedVisualization.code ) - ) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 6 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - context.Main.Update.mainX(contextId), - context.Main.Update.mainY(contextId), - context.Main.Update.mainZ(contextId), - TestMessages.update(contextId, idMainRes, ConstantsGen.INTEGER), - context.executionComplete(contextId) - ) - context.consumeOut shouldEqual List() - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - idMainRes, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.ModuleMethod( - Api.MethodPointer( - "Enso_Test.Test.Visualization", - "Enso_Test.Test.Visualization", - "incAndEncode" - ), - Vector() - ), - "Enso_Test.Test.Visualization" + context.send( + Api.Request( + requestId, + Api.OpenFileRequest( + visualizationFile, + context.AnnotatedVisualization.code ) ) ) - ) - val attachVisualizationResponses = - context.receiveNIgnoreExpressionUpdates(3) - attachVisualizationResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMainRes` + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 6 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + TestMessages.update(contextId, idMainRes, ConstantsGen.INTEGER), + context.executionComplete(contextId) + ) + context.consumeOut shouldEqual List() + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + idMainRes, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.ModuleMethod( + Api.MethodPointer( + "Enso_Test.Test.Visualization", + "Enso_Test.Test.Visualization", + "incAndEncode" + ), + Vector("2", "3") ), - data + "Enso_Test.Test.Visualization" ) - ) => - data - } - data.sameElements("51".getBytes) shouldBe true - context.consumeOut shouldEqual List("encoding...") + ) + ) + ) + val attachVisualizationResponses = + context.receiveNIgnoreExpressionUpdates(3) + attachVisualizationResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) + ) + val Some(data) = attachVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMainRes` + ), + data + ) + ) => + data + } + data.sameElements("103".getBytes) shouldBe true + context.consumeOut shouldEqual List("encoding...") - // recompute - context.send( - Api.Request(requestId, Api.RecomputeContextRequest(contextId, None, None)) - ) + // recompute + context.send( + Api.Request( + requestId, + Api.RecomputeContextRequest(contextId, None, None) + ) + ) - val recomputeResponses = context.receiveNIgnoreExpressionUpdates(3) - recomputeResponses should contain allOf ( - Api.Response(requestId, Api.RecomputeContextResponse(contextId)), - context.executionComplete(contextId) - ) - val Some(data2) = recomputeResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMainRes` + val recomputeResponses = context.receiveNIgnoreExpressionUpdates(3) + recomputeResponses should contain allOf ( + Api.Response(requestId, Api.RecomputeContextResponse(contextId)), + context.executionComplete(contextId) + ) + val Some(data2) = recomputeResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMainRes` + ), + data + ) + ) => + data + } + data2.sameElements("103".getBytes) shouldBe true + context.consumeOut shouldEqual List() + + // modify visualization + context.send( + Api.Request( + requestId, + Api.ModifyVisualization( + visualizationId, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.ModuleMethod( + Api.MethodPointer( + "Enso_Test.Test.Visualization", + "Enso_Test.Test.Visualization", + "incAndEncode" + ), + Vector("2", "4") ), - data + "Enso_Test.Test.Visualization" ) - ) => - data - } - data2.sameElements("51".getBytes) shouldBe true - context.consumeOut shouldEqual List() + ) + ) + ) + val modifyVisualizationResponses = + context.receiveNIgnoreExpressionUpdates(2) + modifyVisualizationResponses should contain( + Api.Response(requestId, Api.VisualizationModified()) + ) + val Some(data3) = + modifyVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMainRes` + ), + data + ) + ) => + data + } + data3.sameElements("104".getBytes) shouldBe true + context.consumeOut shouldEqual List("encoding...") + } - // Modify the visualization file - context.send( - Api.Request( - Api.EditFileNotification( - visualizationFile, - Seq( - TextEdit( - model.Range(model.Position(6, 21), model.Position(6, 22)), - "2" + it should "cache intermediate visualization expressions" in withContext() { + context => + val idMainRes = context.Main.metadata.addItem(99, 1) + val contents = context.Main.code + val mainFile = context.writeMain(context.Main.code) + val moduleName = "Enso_Test.Test.Main" + val visualizationFile = + context.writeInSrcDir( + "Visualization", + context.AnnotatedVisualization.code + ) + + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + + context.send( + Api.Request( + requestId, + Api.OpenFileRequest( + visualizationFile, + context.AnnotatedVisualization.code + ) + ) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 6 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + TestMessages.update(contextId, idMainRes, ConstantsGen.INTEGER), + context.executionComplete(contextId) + ) + context.consumeOut shouldEqual List() + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + idMainRes, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.ModuleMethod( + Api.MethodPointer( + "Enso_Test.Test.Visualization", + "Enso_Test.Test.Visualization", + "incAndEncode" + ), + Vector() + ), + "Enso_Test.Test.Visualization" + ) + ) + ) + ) + val attachVisualizationResponses = + context.receiveNIgnoreExpressionUpdates(3) + attachVisualizationResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) + ) + val Some(data) = attachVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMainRes` + ), + data + ) + ) => + data + } + data.sameElements("51".getBytes) shouldBe true + context.consumeOut shouldEqual List("encoding...") + + // recompute + context.send( + Api.Request( + requestId, + Api.RecomputeContextRequest(contextId, None, None) + ) + ) + + val recomputeResponses = context.receiveNIgnoreExpressionUpdates(3) + recomputeResponses should contain allOf ( + Api.Response(requestId, Api.RecomputeContextResponse(contextId)), + context.executionComplete(contextId) + ) + val Some(data2) = recomputeResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMainRes` + ), + data + ) + ) => + data + } + data2.sameElements("51".getBytes) shouldBe true + context.consumeOut shouldEqual List() + + // Modify the visualization file + context.send( + Api.Request( + Api.EditFileNotification( + visualizationFile, + Seq( + TextEdit( + model.Range(model.Position(6, 21), model.Position(6, 22)), + "2" + ) + ), + execute = true + ) + ) + ) + + val editFileResponse = context.receiveNIgnoreExpressionUpdates(2) + editFileResponse should contain( + context.executionComplete(contextId) + ) + val Some(data3) = editFileResponse.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMainRes` + ), + data + ) + ) => + data + } + data3.sameElements("52".getBytes) shouldBe true + context.consumeOut shouldEqual List("encoding...") + } + + it should "emit visualization update for values annotated with warnings" in withContext() { + context => + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val metadata = new Metadata + + val idMain = metadata.addItem(37, 26) + + val code = + """from Standard.Base import all + | + |main = + | Warning.attach "y" 42 + |""".stripMargin.linesIterator.mkString("\n") + val contents = metadata.appendToCode(code) + val mainFile = context.writeMain(contents) + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 3 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.update( + contextId, + idMain, + ConstantsGen.INTEGER, + payload = Api.ExpressionUpdate.Payload.Value( + Some( + Api.ExpressionUpdate.Payload.Value.Warnings(1, Some("'y'"), false) + ) + ) + ), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + idMain, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Enso_Test.Test.Main", + "x -> x.to_text" + ), + "Enso_Test.Test.Main" + ) + ) + ) + ) + val attachVisualizationResponses = + context.receiveNIgnoreExpressionUpdates(3) + attachVisualizationResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) + ) + val Some(data) = attachVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMain` + ), + data + ) + ) => + data + } + new String(data, StandardCharsets.UTF_8) shouldEqual "42" + } + + it should "emit visualization update for values in array annotated with warnings" in withContext() { + context => + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val metadata = new Metadata + + val idMain = metadata.addItem(37, 28) + + val code = + """from Standard.Base import all + | + |main = + | [Warning.attach "y" 42] + |""".stripMargin.linesIterator.mkString("\n") + + metadata.assertInCode(idMain, code, "\n [Warning.attach \"y\" 42]") + + val contents = metadata.appendToCode(code) + val mainFile = context.writeMain(contents) + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 3 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.update( + contextId, + idMain, + ConstantsGen.VECTOR, + payload = Api.ExpressionUpdate.Payload.Value( + Some( + Api.ExpressionUpdate.Payload.Value.Warnings(1, Some("'y'"), false) + ) + ) + ), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + idMain, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Enso_Test.Test.Main", + "x -> x.to_text" + ), + "Enso_Test.Test.Main" + ) + ) + ) + ) + val attachVisualizationResponses = + context.receiveNIgnoreExpressionUpdates(3) + attachVisualizationResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) + ) + val Some(data) = attachVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMain` + ), + data + ) + ) => + data + } + new String(data, StandardCharsets.UTF_8) shouldEqual "[42]" + } + + it should "emit visualization update for values in atom annotated with warnings" in withContext() { + context => + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val warningTypeName = QualifiedName.fromString(ConstantsGen.WARNING) + val warningModuleName = warningTypeName.getParent.get + val metadata = new Metadata + + val idX = metadata.addItem(81, 21) + val idRes = metadata.addItem(107, 20) + + val code = + """from Standard.Base import all + | + |type Newtype + | Mk_Newtype value + | + |main = + | x = Warning.attach "x" 42 + | Newtype.Mk_Newtype x + |""".stripMargin.linesIterator.mkString("\n") + val contents = metadata.appendToCode(code) + val mainFile = context.writeMain(contents) + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 4 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.update( + contextId, + idX, + ConstantsGen.INTEGER, + methodCall = Some( + Api.MethodCall( + Api.MethodPointer( + warningModuleName.toString, + warningTypeName.toString, + "attach" + ) ) ), - execute = true - ) - ) - ) - - val editFileResponse = context.receiveNIgnoreExpressionUpdates(2) - editFileResponse should contain( - context.executionComplete(contextId) - ) - val Some(data3) = editFileResponse.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMainRes` - ), - data + payload = Api.ExpressionUpdate.Payload.Value( + Some( + Api.ExpressionUpdate.Payload.Value.Warnings(1, Some("'x'"), false) ) - ) => - data - } - data3.sameElements("52".getBytes) shouldBe true - context.consumeOut shouldEqual List("encoding...") - } + ) + ), + TestMessages.update( + contextId, + idRes, + s"$moduleName.Newtype", + methodCall = Some( + Api.MethodCall( + Api + .MethodPointer(moduleName, s"$moduleName.Newtype", "Mk_Newtype") + ) + ), + payload = Api.ExpressionUpdate.Payload.Value( + Some( + Api.ExpressionUpdate.Payload.Value.Warnings(1, Some("'x'"), false) + ) + ) + ), + context.executionComplete(contextId) + ) - it should "emit visualization update for values annotated with warnings" in { - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - val moduleName = "Enso_Test.Test.Main" - val metadata = new Metadata - - val idMain = metadata.addItem(37, 26) - - val code = - """from Standard.Base import all - | - |main = - | Warning.attach "y" 42 - |""".stripMargin.linesIterator.mkString("\n") - val contents = metadata.appendToCode(code) - val mainFile = context.writeMain(contents) - - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 3 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - TestMessages.update( - contextId, - idMain, - ConstantsGen.INTEGER, - payload = Api.ExpressionUpdate.Payload.Value( - Some( - Api.ExpressionUpdate.Payload.Value.Warnings(1, Some("'y'"), false) + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + idRes, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + "Enso_Test.Test.Main", + "x -> x.to_text" + ), + "Enso_Test.Test.Main" + ) ) ) - ), - context.executionComplete(contextId) - ) + ) + val attachVisualizationResponses = + context.receiveNIgnoreExpressionUpdates(3) + attachVisualizationResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) + ) + val Some(data) = attachVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idRes` + ), + data + ) + ) => + data + } + new String(data, StandardCharsets.UTF_8) shouldEqual "(Mk_Newtype 42)" + } - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - idMain, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - "Enso_Test.Test.Main", - "x -> x.to_text" + it should "emit visualization update for the target of a method call" in withContext() { + context => + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val metadata = new Metadata("import Standard.Base.Data.Numbers\n\n") + + val idX = metadata.addItem(65, 1, "aa") + val idY = metadata.addItem(65, 7, "ab") + val idS = metadata.addItem(81, 1) + val idZ = metadata.addItem(91, 5, "ac") + val idZexprS = metadata.addItem(93, 1) + val idZexpr1 = metadata.addItem(95, 1) + + val code = + """type T + | C + | + | inc self x = x + 1 + | + |main = + | x = T.C + | y = x.inc 7 + | s = 1 + | z = p y s + | z + | + |p x y = x + y + |""".stripMargin.linesIterator.mkString("\n") + val contents = metadata.appendToCode(code) + val mainFile = context.writeMain(contents) + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 8 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.update(contextId, idX, s"$moduleName.T"), + TestMessages.update( + contextId, + idY, + ConstantsGen.INTEGER, + Api.MethodCall(Api.MethodPointer(moduleName, s"$moduleName.T", "inc")) + ), + TestMessages.update(contextId, idS, ConstantsGen.INTEGER), + TestMessages.update( + contextId, + idZ, + ConstantsGen.INTEGER, + Api.MethodCall(Api.MethodPointer(moduleName, moduleName, "p")) + ), + TestMessages.update(contextId, idZexprS, ConstantsGen.INTEGER), + TestMessages.update(contextId, idZexpr1, ConstantsGen.INTEGER), + context.executionComplete(contextId) + ) + + // attach visualization + context.send( + Api.Request( + requestId, + Api.AttachVisualization( + visualizationId, + idX, + Api.VisualizationConfiguration( + contextId, + Api.VisualizationExpression.Text( + moduleName, + "x -> x.to_text" + ), + moduleName + ) + ) + ) + ) + val attachVisualizationResponses = + context.receiveNIgnoreExpressionUpdates(3) + attachVisualizationResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) + ) + val Some(data) = attachVisualizationResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idX` + ), + data + ) + ) => + data + } + new String(data, StandardCharsets.UTF_8) shouldEqual "C" + + // Modify the file + context.send( + Api.Request( + Api.EditFileNotification( + mainFile, + Seq( + TextEdit( + model.Range(model.Position(9, 8), model.Position(9, 9)), + "x" + ) ), - "Enso_Test.Test.Main" + execute = true ) ) ) - ) - val attachVisualizationResponses = - context.receiveNIgnoreExpressionUpdates(3) - attachVisualizationResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMain` - ), - data - ) - ) => - data - } - new String(data, StandardCharsets.UTF_8) shouldEqual "42" - } - it should "emit visualization update for values in array annotated with warnings" in { - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - val moduleName = "Enso_Test.Test.Main" - val metadata = new Metadata - - val idMain = metadata.addItem(37, 28) - - val code = - """from Standard.Base import all - | - |main = - | [Warning.attach "y" 42] - |""".stripMargin.linesIterator.mkString("\n") - - metadata.assertInCode(idMain, code, "\n [Warning.attach \"y\" 42]") - - val contents = metadata.appendToCode(code) - val mainFile = context.writeMain(contents) - - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 3 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - TestMessages.update( - contextId, - idMain, - ConstantsGen.VECTOR, - payload = Api.ExpressionUpdate.Payload.Value( - Some( - Api.ExpressionUpdate.Payload.Value.Warnings(1, Some("'y'"), false) - ) - ) - ), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - idMain, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - "Enso_Test.Test.Main", - "x -> x.to_text" - ), - "Enso_Test.Test.Main" - ) - ) + val editFileResponse = context.receiveNIgnoreExpressionUpdates(2) + editFileResponse should contain( + context.executionComplete(contextId) ) - ) - val attachVisualizationResponses = - context.receiveNIgnoreExpressionUpdates(3) - attachVisualizationResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMain` - ), - data - ) - ) => - data - } - new String(data, StandardCharsets.UTF_8) shouldEqual "[42]" + val Some(data1) = editFileResponse.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idX` + ), + data + ) + ) => + data + } + new String(data1, StandardCharsets.UTF_8) shouldEqual "C" } - it should "emit visualization update for values in atom annotated with warnings" in { - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - val moduleName = "Enso_Test.Test.Main" - val warningTypeName = QualifiedName.fromString(ConstantsGen.WARNING) - val warningModuleName = warningTypeName.getParent.get - val metadata = new Metadata + it should "execute expression in the scope of local expression cached" in withContext() { + context => + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val metadata = new Metadata("import Standard.Base.Data.Numbers\n\n") - val idX = metadata.addItem(81, 21) - val idRes = metadata.addItem(107, 20) + val idOp1 = metadata.addItem(23, 2) + val idOp2 = metadata.addItem(42, 13) - val code = - """from Standard.Base import all - | - |type Newtype - | Mk_Newtype value - | - |main = - | x = Warning.attach "x" 42 - | Newtype.Mk_Newtype x - |""".stripMargin.linesIterator.mkString("\n") - val contents = metadata.appendToCode(code) - val mainFile = context.writeMain(contents) + val code = + """main = + | operator1 = 42 + | operator2 = operator1 + 1 + | operator2 + | + |fun1 x = x.to_text + |""".stripMargin.linesIterator.mkString("\n") + val contents = metadata.appendToCode(code) + val mainFile = context.writeMain(contents) - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 4 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - TestMessages.update( - contextId, - idX, - ConstantsGen.INTEGER, - methodCall = Some( + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, moduleName, "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 4 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.update(contextId, idOp1, ConstantsGen.INTEGER), + TestMessages.update( + contextId, + idOp2, + ConstantsGen.INTEGER, Api.MethodCall( Api.MethodPointer( - warningModuleName.toString, - warningTypeName.toString, - "attach" + "Standard.Base.Data.Numbers", + ConstantsGen.INTEGER, + "+" ) ) ), - payload = Api.ExpressionUpdate.Payload.Value( - Some( - Api.ExpressionUpdate.Payload.Value.Warnings(1, Some("'x'"), false) - ) - ) - ), - TestMessages.update( - contextId, - idRes, - s"$moduleName.Newtype", - methodCall = Some( - Api.MethodCall( - Api.MethodPointer(moduleName, s"$moduleName.Newtype", "Mk_Newtype") - ) - ), - payload = Api.ExpressionUpdate.Payload.Value( - Some( - Api.ExpressionUpdate.Payload.Value.Warnings(1, Some("'x'"), false) - ) - ) - ), - context.executionComplete(contextId) - ) + context.executionComplete(contextId) + ) - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - idRes, - Api.VisualizationConfiguration( + // execute expression + context.send( + Api.Request( + requestId, + Api.ExecuteExpression( contextId, - Api.VisualizationExpression.Text( - "Enso_Test.Test.Main", - "x -> x.to_text" - ), - "Enso_Test.Test.Main" + visualizationId, + idOp2, + "fun1 operator1" ) ) ) - ) - val attachVisualizationResponses = - context.receiveNIgnoreExpressionUpdates(3) - attachVisualizationResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idRes` - ), - data - ) - ) => - data - } - new String(data, StandardCharsets.UTF_8) shouldEqual "(Mk_Newtype 42)" + val executeExpressionResponses = + context.receiveNIgnoreExpressionUpdates(3) + executeExpressionResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) + ) + val Some(data) = executeExpressionResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idOp2` + ), + data + ) + ) => + data + } + new String(data) shouldEqual "42" } - it should "emit visualization update for the target of a method call" in { - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - val moduleName = "Enso_Test.Test.Main" - val metadata = new Metadata("import Standard.Base.Data.Numbers\n\n") + it should "execute expression in the scope of local expression not cached" in withContext() { + context => + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val metadata = new Metadata("import Standard.Base.Data.Numbers\n\n") - val idX = metadata.addItem(65, 1, "aa") - val idY = metadata.addItem(65, 7, "ab") - val idS = metadata.addItem(81, 1) - val idZ = metadata.addItem(91, 5, "ac") - val idZexprS = metadata.addItem(93, 1) - val idZexpr1 = metadata.addItem(95, 1) + val idOp1 = metadata.addItem(23, 2) + val idOp2 = metadata.addItem(42, 13) + val idRes = metadata.addItem(60, 9) - val code = - """type T - | C - | - | inc self x = x + 1 - | - |main = - | x = T.C - | y = x.inc 7 - | s = 1 - | z = p y s - | z - | - |p x y = x + y - |""".stripMargin.linesIterator.mkString("\n") - val contents = metadata.appendToCode(code) - val mainFile = context.writeMain(contents) + val code = + """main = + | operator1 = 42 + | operator2 = operator1 + 1 + | operator2 + | + |fun1 x = x.to_text + |""".stripMargin.linesIterator.mkString("\n") + val contents = metadata.appendToCode(code) + val mainFile = context.writeMain(contents) - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 8 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - TestMessages.update(contextId, idX, s"$moduleName.T"), - TestMessages.update( - contextId, - idY, - ConstantsGen.INTEGER, - Api.MethodCall(Api.MethodPointer(moduleName, s"$moduleName.T", "inc")) - ), - TestMessages.update(contextId, idS, ConstantsGen.INTEGER), - TestMessages.update( - contextId, - idZ, - ConstantsGen.INTEGER, - Api.MethodCall(Api.MethodPointer(moduleName, moduleName, "p")) - ), - TestMessages.update(contextId, idZexprS, ConstantsGen.INTEGER), - TestMessages.update(contextId, idZexpr1, ConstantsGen.INTEGER), - context.executionComplete(contextId) - ) - - // attach visualization - context.send( - Api.Request( - requestId, - Api.AttachVisualization( - visualizationId, - idX, - Api.VisualizationConfiguration( - contextId, - Api.VisualizationExpression.Text( - moduleName, - "x -> x.to_text" - ), - moduleName - ) - ) + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) ) - ) - val attachVisualizationResponses = - context.receiveNIgnoreExpressionUpdates(3) - attachVisualizationResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) - val Some(data) = attachVisualizationResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idX` - ), - data - ) - ) => - data - } - new String(data, StandardCharsets.UTF_8) shouldEqual "C" - // Modify the file - context.send( - Api.Request( - Api.EditFileNotification( - mainFile, - Seq( - TextEdit( - model.Range(model.Position(9, 8), model.Position(9, 9)), - "x" - ) - ), - execute = true - ) + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) ) - ) - val editFileResponse = context.receiveNIgnoreExpressionUpdates(2) - editFileResponse should contain( - context.executionComplete(contextId) - ) - val Some(data1) = editFileResponse.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idX` - ), - data - ) - ) => - data - } - new String(data1, StandardCharsets.UTF_8) shouldEqual "C" - } - - it should "execute expression in the scope of local expression cached" in { - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - val moduleName = "Enso_Test.Test.Main" - val metadata = new Metadata("import Standard.Base.Data.Numbers\n\n") - - val idOp1 = metadata.addItem(23, 2) - val idOp2 = metadata.addItem(42, 13) - - val code = - """main = - | operator1 = 42 - | operator2 = operator1 + 1 - | operator2 - | - |fun1 x = x.to_text - |""".stripMargin.linesIterator.mkString("\n") - val contents = metadata.appendToCode(code) - val mainFile = context.writeMain(contents) - - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, moduleName, "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 4 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - TestMessages.update(contextId, idOp1, ConstantsGen.INTEGER), - TestMessages.update( - contextId, - idOp2, - ConstantsGen.INTEGER, - Api.MethodCall( - Api.MethodPointer( - "Standard.Base.Data.Numbers", - ConstantsGen.INTEGER, - "+" - ) - ) - ), - context.executionComplete(contextId) - ) - - // execute expression - context.send( - Api.Request( - requestId, - Api.ExecuteExpression( + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, moduleName, "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 5 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.update(contextId, idOp1, ConstantsGen.INTEGER), + TestMessages.update( contextId, - visualizationId, idOp2, - "fun1 operator1" - ) - ) - ) - val executeExpressionResponses = - context.receiveNIgnoreExpressionUpdates(3) - executeExpressionResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) - val Some(data) = executeExpressionResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idOp2` - ), - data + ConstantsGen.INTEGER, + Api.MethodCall( + Api.MethodPointer( + "Standard.Base.Data.Numbers", + ConstantsGen.INTEGER, + "+" ) - ) => - data - } - new String(data) shouldEqual "42" - } + ) + ), + TestMessages.update(contextId, idRes, ConstantsGen.INTEGER), + context.executionComplete(contextId) + ) - it should "execute expression in the scope of local expression not cached" in { - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - val moduleName = "Enso_Test.Test.Main" - val metadata = new Metadata("import Standard.Base.Data.Numbers\n\n") - - val idOp1 = metadata.addItem(23, 2) - val idOp2 = metadata.addItem(42, 13) - val idRes = metadata.addItem(60, 9) - - val code = - """main = - | operator1 = 42 - | operator2 = operator1 + 1 - | operator2 - | - |fun1 x = x.to_text - |""".stripMargin.linesIterator.mkString("\n") - val contents = metadata.appendToCode(code) - val mainFile = context.writeMain(contents) - - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) - - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) - - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, moduleName, "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 5 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - TestMessages.update(contextId, idOp1, ConstantsGen.INTEGER), - TestMessages.update( - contextId, - idOp2, - ConstantsGen.INTEGER, - Api.MethodCall( - Api.MethodPointer( - "Standard.Base.Data.Numbers", - ConstantsGen.INTEGER, - "+" + // execute expression + context.send( + Api.Request( + requestId, + Api.ExecuteExpression( + contextId, + visualizationId, + idRes, + "fun1 operator1" ) ) - ), - TestMessages.update(contextId, idRes, ConstantsGen.INTEGER), - context.executionComplete(contextId) - ) - - // execute expression - context.send( - Api.Request( - requestId, - Api.ExecuteExpression( - contextId, - visualizationId, - idRes, - "fun1 operator1" - ) ) - ) - val executeExpressionResponses = - context.receiveNIgnoreExpressionUpdates(3) - executeExpressionResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) - val Some(data) = executeExpressionResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idRes` - ), - data - ) - ) => - data - } - new String(data) shouldEqual "42" + val executeExpressionResponses = + context.receiveNIgnoreExpressionUpdates(3) + executeExpressionResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) + ) + val Some(data) = executeExpressionResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idRes` + ), + data + ) + ) => + data + } + new String(data) shouldEqual "42" } - it should "execute expression in the scope of local binding" in { - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - val moduleName = "Enso_Test.Test.Main" - val metadata = new Metadata("import Standard.Base.Data.Numbers\n\n") + it should "execute expression in the scope of local binding" in withContext() { + context => + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val metadata = new Metadata("import Standard.Base.Data.Numbers\n\n") - val idOp1 = metadata.addItem(23, 2) - val idOp2 = metadata.addItem(42, 13) - val idOp2Binding = metadata.addItem(30, 25) - val idRes = metadata.addItem(60, 9) + val idOp1 = metadata.addItem(23, 2) + val idOp2 = metadata.addItem(42, 13) + val idOp2Binding = metadata.addItem(30, 25) + val idRes = metadata.addItem(60, 9) - val code = - """main = - | operator1 = 42 - | operator2 = operator1 + 1 - | operator2 - | - |fun1 x = x.to_text - |""".stripMargin.linesIterator.mkString("\n") - val contents = metadata.appendToCode(code) - val mainFile = context.writeMain(contents) + val code = + """main = + | operator1 = 42 + | operator2 = operator1 + 1 + | operator2 + | + |fun1 x = x.to_text + |""".stripMargin.linesIterator.mkString("\n") + val contents = metadata.appendToCode(code) + val mainFile = context.writeMain(contents) - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, moduleName, "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 6 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - TestMessages.update(contextId, idOp1, ConstantsGen.INTEGER), - TestMessages.update( - contextId, - idOp2, - ConstantsGen.INTEGER, - Api.MethodCall( - Api.MethodPointer( - "Standard.Base.Data.Numbers", - ConstantsGen.INTEGER, - "+" + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, moduleName, "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 6 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.update(contextId, idOp1, ConstantsGen.INTEGER), + TestMessages.update( + contextId, + idOp2, + ConstantsGen.INTEGER, + Api.MethodCall( + Api.MethodPointer( + "Standard.Base.Data.Numbers", + ConstantsGen.INTEGER, + "+" + ) + ) + ), + TestMessages + .update(contextId, idOp2Binding, ConstantsGen.NOTHING), + TestMessages.update(contextId, idRes, ConstantsGen.INTEGER), + context.executionComplete(contextId) + ) + + // execute expression + context.send( + Api.Request( + requestId, + Api.ExecuteExpression( + contextId, + visualizationId, + idOp2Binding, + "fun1 operator1+operator2" ) ) - ), - TestMessages - .update(contextId, idOp2Binding, ConstantsGen.NOTHING), - TestMessages.update(contextId, idRes, ConstantsGen.INTEGER), - context.executionComplete(contextId) - ) - - // execute expression - context.send( - Api.Request( - requestId, - Api.ExecuteExpression( - contextId, - visualizationId, - idOp2Binding, - "fun1 operator1+operator2" - ) ) - ) - val executeExpressionResponses = - context.receiveNIgnoreExpressionUpdates(3) - executeExpressionResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) - val Some(data) = executeExpressionResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idOp2Binding` - ), - data - ) - ) => - data - } - new String(data) shouldEqual "85" + val executeExpressionResponses = + context.receiveNIgnoreExpressionUpdates(3) + executeExpressionResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) + ) + val Some(data) = executeExpressionResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idOp2Binding` + ), + data + ) + ) => + data + } + new String(data) shouldEqual "85" } - it should "execute expression in the scope of main method" in { - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - val moduleName = "Enso_Test.Test.Main" - val metadata = new Metadata("import Standard.Base.Data.Numbers\n\n") + it should "execute expression in the scope of main method" in withContext() { + context => + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val metadata = new Metadata("import Standard.Base.Data.Numbers\n\n") - val idOp1 = metadata.addItem(23, 2) - val idOp2 = metadata.addItem(42, 13) - val idMain = metadata.addItem(6, 63) + val idOp1 = metadata.addItem(23, 2) + val idOp2 = metadata.addItem(42, 13) + val idMain = metadata.addItem(6, 63) - val code = - """main = - | operator1 = 42 - | operator2 = operator1 + 1 - | operator2 - | - |fun1 x = x.to_text - |""".stripMargin.linesIterator.mkString("\n") - val contents = metadata.appendToCode(code) - val mainFile = context.writeMain(contents) + val code = + """main = + | operator1 = 42 + | operator2 = operator1 + 1 + | operator2 + | + |fun1 x = x.to_text + |""".stripMargin.linesIterator.mkString("\n") + val contents = metadata.appendToCode(code) + val mainFile = context.writeMain(contents) - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, moduleName, "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 5 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - TestMessages.update(contextId, idOp1, ConstantsGen.INTEGER), - TestMessages.update( - contextId, - idOp2, - ConstantsGen.INTEGER, - Api.MethodCall( - Api.MethodPointer( - "Standard.Base.Data.Numbers", - ConstantsGen.INTEGER, - "+" + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, moduleName, "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 5 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.update(contextId, idOp1, ConstantsGen.INTEGER), + TestMessages.update( + contextId, + idOp2, + ConstantsGen.INTEGER, + Api.MethodCall( + Api.MethodPointer( + "Standard.Base.Data.Numbers", + ConstantsGen.INTEGER, + "+" + ) + ) + ), + TestMessages.update(contextId, idMain, ConstantsGen.INTEGER), + context.executionComplete(contextId) + ) + + // execute expression + context.send( + Api.Request( + requestId, + Api.ExecuteExpression( + contextId, + visualizationId, + idMain, + "fun1 operator1+operator2" ) ) - ), - TestMessages.update(contextId, idMain, ConstantsGen.INTEGER), - context.executionComplete(contextId) - ) - - // execute expression - context.send( - Api.Request( - requestId, - Api.ExecuteExpression( - contextId, - visualizationId, - idMain, - "fun1 operator1+operator2" - ) ) - ) - val executeExpressionResponses = - context.receiveNIgnoreExpressionUpdates(3) - executeExpressionResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) - val Some(data) = executeExpressionResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMain` - ), - data - ) - ) => - data - } - new String(data) shouldEqual "85" + val executeExpressionResponses = + context.receiveNIgnoreExpressionUpdates(3) + executeExpressionResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) + ) + val Some(data) = executeExpressionResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMain` + ), + data + ) + ) => + data + } + new String(data) shouldEqual "85" } - it should "execute default visualization preprocessor" in { - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - val moduleName = "Enso_Test.Test.Main" - val metadata = new Metadata + it should "execute default visualization preprocessor" in withContext() { + context => + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val metadata = new Metadata - val idMain = metadata.addItem(60, 6) + val idMain = metadata.addItem(60, 6) - val code = - """import Standard.Visualization.Preprocessor - | - |main = - | fn = x -> x - | fn - |""".stripMargin.linesIterator.mkString("\n") - val contents = metadata.appendToCode(code) - val mainFile = context.writeMain(contents) + val code = + """import Standard.Visualization.Preprocessor + | + |main = + | fn = x -> x + | fn + |""".stripMargin.linesIterator.mkString("\n") + val contents = metadata.appendToCode(code) + val mainFile = context.writeMain(contents) - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, moduleName, "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 3 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - TestMessages.update( - contextId, - idMain, - ConstantsGen.FUNCTION - ), - context.executionComplete(contextId) - ) - - // execute expression - context.send( - Api.Request( - requestId, - Api.ExecuteExpression( + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, moduleName, "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 3 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.update( contextId, - visualizationId, idMain, - "Preprocessor.default_preprocessor 85" + ConstantsGen.FUNCTION + ), + context.executionComplete(contextId) + ) + + // execute expression + context.send( + Api.Request( + requestId, + Api.ExecuteExpression( + contextId, + visualizationId, + idMain, + "Preprocessor.default_preprocessor 85" + ) ) ) - ) - val executeExpressionResponses = - context.receiveNIgnoreExpressionUpdates(3) - executeExpressionResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) - val Some(data) = executeExpressionResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMain` - ), - data - ) - ) => - data - } - new String(data) shouldEqual "85" + val executeExpressionResponses = + context.receiveNIgnoreExpressionUpdates(3) + executeExpressionResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) + ) + val Some(data) = executeExpressionResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMain` + ), + data + ) + ) => + data + } + new String(data) shouldEqual "85" } - it should "execute default visualization preprocessor with a FQN" in { - val contextId = UUID.randomUUID() - val requestId = UUID.randomUUID() - val visualizationId = UUID.randomUUID() - val moduleName = "Enso_Test.Test.Main" - val metadata = new Metadata + it should "execute default visualization preprocessor with a FQN" in withContext() { + context => + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualizationId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val metadata = new Metadata - val idMain = metadata.addItem(90, 6) + val idMain = metadata.addItem(90, 6) - val code = - """import Standard.Visualization - |import Standard.Visualization.Preprocessor - | - |main = - | fn = x -> x - | fn - |""".stripMargin.linesIterator.mkString("\n") - val contents = metadata.appendToCode(code) - val mainFile = context.writeMain(contents) + val code = + """import Standard.Visualization + |import Standard.Visualization.Preprocessor + | + |main = + | fn = x -> x + | fn + |""".stripMargin.linesIterator.mkString("\n") + val contents = metadata.appendToCode(code) + val mainFile = context.writeMain(contents) - // create context - context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) - context.receive shouldEqual Some( - Api.Response(requestId, Api.CreateContextResponse(contextId)) - ) + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) - // Open the new file - context.send( - Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) - ) - context.receive shouldEqual Some( - Api.Response(Some(requestId), Api.OpenFileResponse) - ) + // Open the new file + context.send( + Api.Request(requestId, Api.OpenFileRequest(mainFile, contents)) + ) + context.receive shouldEqual Some( + Api.Response(Some(requestId), Api.OpenFileResponse) + ) - // push main - val item1 = Api.StackItem.ExplicitCall( - Api.MethodPointer(moduleName, moduleName, "main"), - None, - Vector() - ) - context.send( - Api.Request(requestId, Api.PushContextRequest(contextId, item1)) - ) - context.receiveNIgnorePendingExpressionUpdates( - 3 - ) should contain theSameElementsAs Seq( - Api.Response(requestId, Api.PushContextResponse(contextId)), - TestMessages.update( - contextId, - idMain, - ConstantsGen.FUNCTION - ), - context.executionComplete(contextId) - ) - - // execute expression - context.send( - Api.Request( - requestId, - Api.ExecuteExpression( + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, moduleName, "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnorePendingExpressionUpdates( + 3 + ) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.update( contextId, - visualizationId, idMain, - "Standard.Visualization.Preprocessor.default_preprocessor 85" + ConstantsGen.FUNCTION + ), + context.executionComplete(contextId) + ) + + // execute expression + context.send( + Api.Request( + requestId, + Api.ExecuteExpression( + contextId, + visualizationId, + idMain, + "Standard.Visualization.Preprocessor.default_preprocessor 85" + ) ) ) - ) - val executeExpressionResponses = - context.receiveNIgnoreExpressionUpdates(3) - executeExpressionResponses should contain allOf ( - Api.Response(requestId, Api.VisualizationAttached()), - context.executionComplete(contextId) - ) - val Some(data) = executeExpressionResponses.collectFirst { - case Api.Response( - None, - Api.VisualizationUpdate( - Api.VisualizationContext( - `visualizationId`, - `contextId`, - `idMain` - ), - data - ) - ) => - data - } - new String(data) shouldEqual "85" + val executeExpressionResponses = + context.receiveNIgnoreExpressionUpdates(3) + executeExpressionResponses should contain allOf ( + Api.Response(requestId, Api.VisualizationAttached()), + context.executionComplete(contextId) + ) + val Some(data) = executeExpressionResponses.collectFirst { + case Api.Response( + None, + Api.VisualizationUpdate( + Api.VisualizationContext( + `visualizationId`, + `contextId`, + `idMain` + ), + data + ) + ) => + data + } + new String(data) shouldEqual "85" } }