diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeErrorsTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeErrorsTest.scala index 515d72f3503..0d0c3f69ad1 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeErrorsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeErrorsTest.scala @@ -130,53 +130,6 @@ class RuntimeErrorsTest Api.Response(Api.ExecutionComplete(contextId)) } - object Update { - - def panic( - contextId: UUID, - expressionId: UUID, - payload: Api.ExpressionUpdate.Payload - ): Api.Response = - Api.Response( - Api.ExpressionUpdates( - contextId, - Set( - Api.ExpressionUpdate( - expressionId, - Some(Constants.PANIC), - None, - Vector(Api.ProfilingInfo.ExecutionTime(0)), - false, - payload - ) - ) - ) - ) - - def panic( - contextId: UUID, - expressionId: UUID, - methodPointer: Api.MethodPointer, - payload: Api.ExpressionUpdate.Payload - ): Api.Response = - Api.Response( - Api.ExpressionUpdates( - contextId, - Set( - Api.ExpressionUpdate( - expressionId, - Some(Constants.PANIC), - Some(methodPointer), - Vector(Api.ProfilingInfo.ExecutionTime(0)), - false, - payload - ) - ) - ) - ) - - } - def contentsVersion(content: String): ContentVersion = Sha3_224VersionCalculator.evalVersion(content) @@ -247,7 +200,7 @@ class RuntimeErrorsTest ) ) ), - Update.panic( + TestMessages.panic( contextId, xId, Api.ExpressionUpdate.Payload.Panic( @@ -255,7 +208,7 @@ class RuntimeErrorsTest Seq(xId) ) ), - Update.panic( + TestMessages.panic( contextId, yId, Api.ExpressionUpdate.Payload.Panic( @@ -263,7 +216,7 @@ class RuntimeErrorsTest Seq(xId) ) ), - Update.panic( + TestMessages.panic( contextId, mainResId, Api.ExpressionUpdate.Payload.Panic( @@ -331,7 +284,7 @@ class RuntimeErrorsTest ) ) ), - Update.panic( + TestMessages.panic( contextId, mainBodyId, Api.MethodPointer("Enso_Test.Test.Main", "Enso_Test.Test.Main", "foo"), @@ -482,7 +435,7 @@ class RuntimeErrorsTest ) ) ), - Update.panic( + TestMessages.panic( contextId, xId, Api.ExpressionUpdate.Payload.Panic( @@ -710,7 +663,7 @@ class RuntimeErrorsTest context.consumeOut shouldEqual List("499999999999") } - it should "not send updates when dataflow error changes" in { + it should "not send updates when dataflow error changes in expression" in { val contextId = UUID.randomUUID() val requestId = UUID.randomUUID() val moduleName = "Enso_Test.Test.Main" @@ -796,6 +749,96 @@ class RuntimeErrorsTest context.consumeOut shouldEqual List("(Error: MyError2)") } + it should "not send updates when dataflow error changes in method" in { + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val metadata = new Metadata + val xId = metadata.addItem(111, 8) + val yId = metadata.addItem(128, 5) + val mainResId = metadata.addItem(138, 12) + + val code = + """from Standard.Builtins import all + | + |type MyError1 + |type MyError2 + | + |foo = + | Error.throw MyError1 + | + |main = + | x = this.foo + | y = x - 1 + | IO.println 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(Api.OpenFileNotification(mainFile, contents, true)) + ) + context.receiveNone shouldEqual None + + // push main + context.send( + Api.Request( + requestId, + Api.PushContextRequest( + contextId, + Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + ) + ) + ) + context.receive(5) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.error( + contextId, + xId, + Api.MethodPointer(moduleName, moduleName, "foo"), + Api.ExpressionUpdate.Payload.DataflowError(Seq()) + ), + TestMessages.error( + contextId, + yId, + Api.ExpressionUpdate.Payload.DataflowError(Seq()) + ), + TestMessages.update(contextId, mainResId, Constants.NOTHING), + context.executionComplete(contextId) + ) + context.consumeOut shouldEqual Seq("(Error: MyError1)") + + // Modify the file + context.send( + Api.Request( + Api.EditFileNotification( + mainFile, + Seq( + TextEdit( + model.Range(model.Position(6, 16), model.Position(6, 24)), + "MyError2" + ) + ) + ) + ) + ) + context.receive(1) should contain theSameElementsAs Seq( + context.executionComplete(contextId) + ) + context.consumeOut shouldEqual List("(Error: MyError2)") + } + it should "continue execution after thrown panics" in { val contextId = UUID.randomUUID() val requestId = UUID.randomUUID() @@ -846,7 +889,7 @@ class RuntimeErrorsTest ) context.receive(5) should contain theSameElementsAs Seq( Api.Response(requestId, Api.PushContextResponse(contextId)), - Update.panic( + TestMessages.panic( contextId, xId, Api.ExpressionUpdate.Payload.Panic( @@ -854,7 +897,7 @@ class RuntimeErrorsTest Seq(xId) ) ), - Update.panic( + TestMessages.panic( contextId, yId, Api.ExpressionUpdate.Payload.Panic( @@ -862,7 +905,7 @@ class RuntimeErrorsTest Seq(xId) ) ), - Update.panic( + TestMessages.panic( contextId, mainResId, Api.ExpressionUpdate.Payload.Panic( @@ -958,7 +1001,7 @@ class RuntimeErrorsTest ) ) ), - Update.panic( + TestMessages.panic( contextId, xId, Api.ExpressionUpdate.Payload.Panic( @@ -966,7 +1009,7 @@ class RuntimeErrorsTest Seq(xId) ) ), - Update.panic( + TestMessages.panic( contextId, yId, Api.ExpressionUpdate.Payload.Panic( @@ -974,7 +1017,7 @@ class RuntimeErrorsTest Seq(xId) ) ), - Update.panic( + TestMessages.panic( contextId, mainResId, Api.ExpressionUpdate.Payload.Panic( @@ -1010,7 +1053,7 @@ class RuntimeErrorsTest } - it should "send updates when panic changes" in { + it should "send updates when panic changes in expression" in { val contextId = UUID.randomUUID() val requestId = UUID.randomUUID() val moduleName = "Enso_Test.Test.Main" @@ -1061,7 +1104,7 @@ class RuntimeErrorsTest ) context.receive(5) should contain theSameElementsAs Seq( Api.Response(requestId, Api.PushContextResponse(contextId)), - Update.panic( + TestMessages.panic( contextId, xId, Api.ExpressionUpdate.Payload.Panic( @@ -1069,7 +1112,7 @@ class RuntimeErrorsTest Seq(xId) ) ), - Update.panic( + TestMessages.panic( contextId, yId, Api.ExpressionUpdate.Payload.Panic( @@ -1077,7 +1120,7 @@ class RuntimeErrorsTest Seq(xId) ) ), - Update.panic( + TestMessages.panic( contextId, mainResId, Api.ExpressionUpdate.Payload.Panic( @@ -1104,7 +1147,7 @@ class RuntimeErrorsTest ) ) context.receive(4) should contain theSameElementsAs Seq( - Update.panic( + TestMessages.panic( contextId, xId, Api.ExpressionUpdate.Payload.Panic( @@ -1112,7 +1155,7 @@ class RuntimeErrorsTest Seq(xId) ) ), - Update.panic( + TestMessages.panic( contextId, yId, Api.ExpressionUpdate.Payload.Panic( @@ -1120,7 +1163,7 @@ class RuntimeErrorsTest Seq(xId) ) ), - Update.panic( + TestMessages.panic( contextId, mainResId, Api.ExpressionUpdate.Payload.Panic( @@ -1133,6 +1176,212 @@ class RuntimeErrorsTest context.consumeOut shouldEqual List() } + it should "send updates when panic is resolved in method" in { + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val metadata = new Metadata + val xId = metadata.addItem(75, 8) + val yId = metadata.addItem(92, 5) + val mainResId = metadata.addItem(102, 12) + + val code = + """from Standard.Builtins import all + | + |foo = + | Panic.throw 9 + | + |main = + | x = this.foo + | y = x + 1 + | IO.println 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(Api.OpenFileNotification(mainFile, contents, true)) + ) + context.receiveNone shouldEqual None + + // push main + context.send( + Api.Request( + requestId, + Api.PushContextRequest( + contextId, + Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + ) + ) + ) + context.receive(5) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.panic( + contextId, + xId, + Api.MethodPointer(moduleName, moduleName, "foo"), + Api.ExpressionUpdate.Payload.Panic( + "9 (Integer)", + Seq(xId) + ) + ), + TestMessages.panic( + contextId, + yId, + Api.ExpressionUpdate.Payload.Panic( + "9 (Integer)", + Seq(xId) + ) + ), + TestMessages.panic( + contextId, + mainResId, + Api.ExpressionUpdate.Payload.Panic( + "9 (Integer)", + Seq(xId) + ) + ), + context.executionComplete(contextId) + ) + context.consumeOut shouldEqual Seq() + + // Modify the file + context.send( + Api.Request( + Api.EditFileNotification( + mainFile, + Seq( + TextEdit( + model.Range(model.Position(3, 4), model.Position(3, 17)), + "10002 - 10000" + ) + ) + ) + ) + ) + context.receive(4) should contain theSameElementsAs Seq( + TestMessages.update( + contextId, + xId, + Constants.INTEGER, + Api.MethodPointer(moduleName, moduleName, "foo") + ), + TestMessages.update(contextId, yId, Constants.INTEGER), + TestMessages.update(contextId, mainResId, Constants.NOTHING), + context.executionComplete(contextId) + ) + context.consumeOut shouldEqual List("3") + } + + it should "send updates when dataflow error is resolved in method" in { + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val moduleName = "Enso_Test.Test.Main" + val metadata = new Metadata + val xId = metadata.addItem(75, 8) + val yId = metadata.addItem(92, 5) + val mainResId = metadata.addItem(102, 12) + + val code = + """from Standard.Builtins import all + | + |foo = + | Error.throw 9 + | + |main = + | x = this.foo + | y = x + 1 + | IO.println 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(Api.OpenFileNotification(mainFile, contents, true)) + ) + context.receiveNone shouldEqual None + + // push main + context.send( + Api.Request( + requestId, + Api.PushContextRequest( + contextId, + Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + ) + ) + ) + context.receive(5) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + TestMessages.error( + contextId, + xId, + Api.MethodPointer(moduleName, moduleName, "foo"), + Api.ExpressionUpdate.Payload.DataflowError(Seq()) + ), + TestMessages.error( + contextId, + yId, + Api.ExpressionUpdate.Payload.DataflowError(Seq()) + ), + TestMessages.update( + contextId, + mainResId, + Constants.NOTHING + ), + context.executionComplete(contextId) + ) + context.consumeOut shouldEqual Seq("(Error: 9)") + + // Modify the file + context.send( + Api.Request( + Api.EditFileNotification( + mainFile, + Seq( + TextEdit( + model.Range(model.Position(3, 4), model.Position(3, 17)), + "10002 - 10000" + ) + ) + ) + ) + ) + context.receive(3) should contain theSameElementsAs Seq( + TestMessages.update( + contextId, + xId, + Constants.INTEGER, + Api.MethodPointer(moduleName, moduleName, "foo") + ), + TestMessages.update(contextId, yId, Constants.INTEGER), + context.executionComplete(contextId) + ) + context.consumeOut shouldEqual List("3") + } + it should "not cache panics" in { val contextId = UUID.randomUUID() val requestId = UUID.randomUUID() @@ -1194,7 +1443,7 @@ class RuntimeErrorsTest ) ) ), - Update.panic( + TestMessages.panic( contextId, xId, Api.ExpressionUpdate.Payload.Panic( @@ -1202,7 +1451,7 @@ class RuntimeErrorsTest Seq(xId) ) ), - Update.panic( + TestMessages.panic( contextId, mainResId, Api.ExpressionUpdate.Payload.Panic( diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeStdlibTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeStdlibTest.scala index 45d7b94dac1..6718a37e4ad 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeStdlibTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/RuntimeStdlibTest.scala @@ -188,11 +188,13 @@ class RuntimeStdlibTest context.executionComplete(contextId), timeout = 60 ) + // sanity check responses should contain allOf ( Api.Response(requestId, Api.PushContextResponse(contextId)), context.executionComplete(contextId) ) + // check that the suggestion notifications are received val suggestions = responses.collect { case Api.Response( None, @@ -202,6 +204,18 @@ class RuntimeStdlibTest } suggestions.isEmpty shouldBe false + // check that the Standard.Base library is indexed + val stdlibSuggestions = responses.collect { + case Api.Response( + None, + Api.SuggestionsDatabaseModuleUpdateNotification(file, _, as, xs) + ) if file.getPath.contains("Vector") => + (xs.nonEmpty || as.nonEmpty) shouldBe true + xs.toVector.head.suggestion.module shouldEqual "Standard.Base.Data.Vector" + } + stdlibSuggestions.nonEmpty shouldBe true + + // check that builtins are indexed val builtinsSuggestions = responses.collect { case Api.Response( None, @@ -211,6 +225,7 @@ class RuntimeStdlibTest } builtinsSuggestions.length shouldBe 1 + // check LibraryLoaded notifications val contentRootNotifications = responses.collect { case Api.Response( None, diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/TestMessages.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/TestMessages.scala index 7d6d3d05f7b..8c81c374793 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/TestMessages.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/instrument/TestMessages.scala @@ -122,27 +122,10 @@ object TestMessages { contextId: UUID, expressionId: UUID, payload: Api.ExpressionUpdate.Payload - ): Api.Response = - error(contextId, expressionId, Constants.ERROR, payload) - - /** Create an error update response. - * - * @param contextId an identifier of the context - * @param expressionId an identifier of the expression - * @param expressionType a type of the expression - * @param payload the error payload - * @return the expression update response - */ - def error( - contextId: UUID, - expressionId: UUID, - expressionType: String, - payload: Api.ExpressionUpdate.Payload ): Api.Response = errorBuilder( contextId, expressionId, - Some(expressionType), None, false, payload @@ -152,7 +135,28 @@ object TestMessages { * * @param contextId an identifier of the context * @param expressionId an identifier of the expression - * @param expressionType a type of the expression + * @param methodPointer a pointer to the method definition + * @param payload the error payload + * @return the expression update response + */ + def error( + contextId: UUID, + expressionId: UUID, + methodPointer: Api.MethodPointer, + payload: Api.ExpressionUpdate.Payload + ): Api.Response = + error( + contextId, + expressionId, + methodPointer, + false, + payload + ) + + /** Create an error update response. + * + * @param contextId an identifier of the context + * @param expressionId an identifier of the expression * @param methodPointer a pointer to the method definition * @param fromCache whether or not the value for this expression came * from the cache @@ -162,7 +166,6 @@ object TestMessages { def error( contextId: UUID, expressionId: UUID, - expressionType: String, methodPointer: Api.MethodPointer, fromCache: Boolean, payload: Api.ExpressionUpdate.Payload @@ -170,7 +173,6 @@ object TestMessages { errorBuilder( contextId, expressionId, - Some(expressionType), Some(methodPointer), fromCache, payload @@ -180,7 +182,6 @@ object TestMessages { * * @param contextId an identifier of the context * @param expressionId an identifier of the expression - * @param expressionTypeOpt a type of the expression * @param methodPointerOpt a pointer to the method definition * @param fromCache whether or not the value for this expression came * from the cache @@ -190,7 +191,6 @@ object TestMessages { private def errorBuilder( contextId: UUID, expressionId: UUID, - expressionTypeOpt: Option[String], methodPointerOpt: Option[Api.MethodPointer], fromCache: Boolean, payload: Api.ExpressionUpdate.Payload @@ -201,7 +201,7 @@ object TestMessages { Set( Api.ExpressionUpdate( expressionId, - expressionTypeOpt, + Some(Constants.ERROR), methodPointerOpt, Vector(Api.ProfilingInfo.ExecutionTime(0)), fromCache, @@ -211,4 +211,64 @@ object TestMessages { ) ) + /** Create a panic update response. + * + * @param contextId an identifier of the context + * @param expressionId an identifier of the expression + * @param payload the error payload + * @return the expression update response + */ + def panic( + contextId: UUID, + expressionId: UUID, + payload: Api.ExpressionUpdate.Payload + ): Api.Response = + panicBuilder(contextId, expressionId, None, payload) + + /** Create a panic update response. + * + * @param contextId an identifier of the context + * @param expressionId an identifier of the expression + * @param methodPointer a pointer to the method definition + * @param payload the error payload + * @return the expression update response + */ + def panic( + contextId: UUID, + expressionId: UUID, + methodPointer: Api.MethodPointer, + payload: Api.ExpressionUpdate.Payload + ): Api.Response = + panicBuilder(contextId, expressionId, Some(methodPointer), payload) + + /** Create a panic update response. + * + * @param contextId an identifier of the context + * @param expressionId an identifier of the expression + * @param methodPointer a pointer to the method definition + * @param payload the error payload + * @return the expression update response + */ + private def panicBuilder( + contextId: UUID, + expressionId: UUID, + methodPointer: Option[Api.MethodPointer], + payload: Api.ExpressionUpdate.Payload + ): Api.Response = + Api.Response( + Api.ExpressionUpdates( + contextId, + Set( + Api.ExpressionUpdate( + expressionId, + Some(Constants.PANIC), + methodPointer, + Vector(Api.ProfilingInfo.ExecutionTime(0)), + false, + payload + ) + ) + ) + ) + }