From 594cd2f6b27491fe8a2c015cf87f5b93b5c95cbd Mon Sep 17 00:00:00 2001
From: SeeBasTStick <sebastian.stock@hhu.de>
Date: Mon, 17 Aug 2020 11:49:49 +0200
Subject: [PATCH] added prob2 kernel access and some tests

---
 .../b/language/server/BDocumentService.kt     | 18 ++----
 src/main/kotlin/b/language/server/Server.kt   |  2 -
 .../server/proBMangement/ProBFactory.kt       | 16 ++++++
 .../server/proBMangement/ProBInterface.kt     |  3 +-
 .../proBMangement/WrongPathException.kt       |  3 +
 .../proBMangement/prob2/MyWarningListener.kt  |  2 +-
 .../server/proBMangement/prob2/ProBKernel.kt  | 57 ++++++++++---------
 .../proBMangement/prob2/ProBKernelManager.kt  | 48 ++++++++++++----
 .../b/language/server/ProBCommandLineTest.kt  |  5 +-
 .../communication/DummyCommunication.kt       | 49 ++++++++++++++++
 .../proBMangement/ProBKernelManagerTest.kt    | 37 ++++++++++++
 11 files changed, 185 insertions(+), 55 deletions(-)
 create mode 100644 src/main/kotlin/b/language/server/proBMangement/ProBFactory.kt
 create mode 100644 src/main/kotlin/b/language/server/proBMangement/WrongPathException.kt
 create mode 100644 src/test/kotlin/b/language/server/communication/DummyCommunication.kt
 create mode 100644 src/test/kotlin/b/language/server/prob2/proBMangement/ProBKernelManagerTest.kt

diff --git a/src/main/kotlin/b/language/server/BDocumentService.kt b/src/main/kotlin/b/language/server/BDocumentService.kt
index f13ee1a..057d5af 100644
--- a/src/main/kotlin/b/language/server/BDocumentService.kt
+++ b/src/main/kotlin/b/language/server/BDocumentService.kt
@@ -1,16 +1,16 @@
 package b.language.server
 
 import b.language.server.communication.CommunicatorInterface
-import b.language.server.proBMangement.probCli.CommandCouldNotBeExecutedException
-import b.language.server.proBMangement.probCli.PathCouldNotBeCreatedException
-import b.language.server.proBMangement.probCli.ProBCommandLineAccess
+import b.language.server.proBMangement.ProBFactory
 import b.language.server.proBMangement.ProBInterface
 import org.eclipse.lsp4j.*
 import org.eclipse.lsp4j.services.TextDocumentService
 import java.io.IOException
 import java.util.concurrent.ConcurrentHashMap
 
-class BDocumentService(private val server: Server, private val communicator: CommunicatorInterface) : TextDocumentService {
+class BDocumentService(private val server: Server,
+                       private val communicator: CommunicatorInterface,
+                       private val proBFactory: ProBFactory = ProBFactory()) : TextDocumentService {
 
     private val documents = ConcurrentHashMap<String, String>()
     private val issueTracker : ConcurrentHashMap<String, Set<String>> = ConcurrentHashMap()
@@ -43,7 +43,7 @@ class BDocumentService(private val server: Server, private val communicator: Com
 
     /**
      * checks a document via prob an the set options
-     * @param the uri to perform actions on
+     * @param currentUri uri to perform actions on
      */
     fun checkDocument(currentUri : String){
 
@@ -52,7 +52,7 @@ class BDocumentService(private val server: Server, private val communicator: Com
 
         clientSettings.thenAccept{ settings ->
             communicator.setDebugMode(settings.debugMode)
-            val prob : ProBInterface = ProBCommandLineAccess(communicator)
+            val prob : ProBInterface = proBFactory.getProBAccess(communicator)
             communicator.sendDebugMessage("settings are $settings", MessageType.Info)
 
             try{
@@ -64,12 +64,6 @@ class BDocumentService(private val server: Server, private val communicator: Com
                 invalidFiles.forEach{uri -> communicator.publishDiagnostics(PublishDiagnosticsParams(uri, listOf()))}
                 communicator.sendDebugMessage("invalidating old files $invalidFiles", MessageType.Info)
                 issueTracker[currentUri] = filesWithProblems.toSet()
-            }catch (e : PathCouldNotBeCreatedException){
-                communicator.sendDebugMessage("error path could not be created", MessageType.Info)
-                communicator.showMessage(e.message!!, MessageType.Error)
-            }catch (e : CommandCouldNotBeExecutedException){
-                communicator.sendDebugMessage("command could not be executed", MessageType.Info)
-                communicator.showMessage(e.message!!, MessageType.Error)
             }catch (e : IOException){
                 communicator.sendDebugMessage("command could not be executed", MessageType.Info)
                 communicator.showMessage(e.message!!, MessageType.Error)
diff --git a/src/main/kotlin/b/language/server/Server.kt b/src/main/kotlin/b/language/server/Server.kt
index c0bfd91..0f4a0f7 100644
--- a/src/main/kotlin/b/language/server/Server.kt
+++ b/src/main/kotlin/b/language/server/Server.kt
@@ -5,12 +5,10 @@ import b.language.server.dataStorage.Settings
 import com.google.gson.JsonObject
 import org.eclipse.lsp4j.*
 import org.eclipse.lsp4j.jsonrpc.messages.Either
-import org.eclipse.lsp4j.jsonrpc.services.JsonNotification
 import org.eclipse.lsp4j.services.*
 import java.util.concurrent.CompletableFuture
 import kotlin.collections.HashMap
 import kotlin.system.exitProcess
-import org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint
 
 class Server : LanguageServer{
 
diff --git a/src/main/kotlin/b/language/server/proBMangement/ProBFactory.kt b/src/main/kotlin/b/language/server/proBMangement/ProBFactory.kt
new file mode 100644
index 0000000..63efff8
--- /dev/null
+++ b/src/main/kotlin/b/language/server/proBMangement/ProBFactory.kt
@@ -0,0 +1,16 @@
+package b.language.server.proBMangement
+
+import b.language.server.communication.CommunicatorInterface
+import b.language.server.proBMangement.prob2.ProBKernelManager
+
+
+/**
+ * Generate new Instances of ProBKernelManger
+ */
+class ProBFactory {
+
+    fun getProBAccess(commuicator : CommunicatorInterface) : ProBInterface{
+        return ProBKernelManager(commuicator)
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/b/language/server/proBMangement/ProBInterface.kt b/src/main/kotlin/b/language/server/proBMangement/ProBInterface.kt
index 8c16106..75060be 100644
--- a/src/main/kotlin/b/language/server/proBMangement/ProBInterface.kt
+++ b/src/main/kotlin/b/language/server/proBMangement/ProBInterface.kt
@@ -8,8 +8,9 @@ interface ProBInterface {
     /**
      * Checks the given document with the help of ProB
      * @param uri the source to check
+     * @param settings the settings for this document
      * @return a list of all problems found
      */
-    abstract fun checkDocument(uri : String, settings: Settings) : List<Diagnostic>
+    fun checkDocument(uri : String, settings: Settings) : List<Diagnostic>
 
 }
\ No newline at end of file
diff --git a/src/main/kotlin/b/language/server/proBMangement/WrongPathException.kt b/src/main/kotlin/b/language/server/proBMangement/WrongPathException.kt
new file mode 100644
index 0000000..0fa842a
--- /dev/null
+++ b/src/main/kotlin/b/language/server/proBMangement/WrongPathException.kt
@@ -0,0 +1,3 @@
+package b.language.server.proBMangement
+
+class WrongPathException (message: String?) : Exception(message)
\ No newline at end of file
diff --git a/src/main/kotlin/b/language/server/proBMangement/prob2/MyWarningListener.kt b/src/main/kotlin/b/language/server/proBMangement/prob2/MyWarningListener.kt
index 2418807..8fb2f1a 100644
--- a/src/main/kotlin/b/language/server/proBMangement/prob2/MyWarningListener.kt
+++ b/src/main/kotlin/b/language/server/proBMangement/prob2/MyWarningListener.kt
@@ -13,6 +13,6 @@ class MyWarningListener : IWarningListener {
     }
 
     fun getWarnings() : List<Diagnostic>{
-        return convertErrorItems(warningList, DiagnosticSeverity.Warning)
+        return convertErrorItems(warningList)
     }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/b/language/server/proBMangement/prob2/ProBKernel.kt b/src/main/kotlin/b/language/server/proBMangement/prob2/ProBKernel.kt
index 7b87aba..ce43d09 100644
--- a/src/main/kotlin/b/language/server/proBMangement/prob2/ProBKernel.kt
+++ b/src/main/kotlin/b/language/server/proBMangement/prob2/ProBKernel.kt
@@ -1,4 +1,5 @@
 
+import b.language.server.communication.Communicator
 import b.language.server.dataStorage.ProBSettings
 import b.language.server.proBMangement.prob2.MyWarningListener
 import b.language.server.proBMangement.prob2.convertErrorItems
@@ -14,14 +15,40 @@ import de.prob.statespace.AnimationSelector
 import de.prob.statespace.StateSpace
 import de.prob.statespace.Trace
 import org.eclipse.lsp4j.Diagnostic
-import org.eclipse.lsp4j.DiagnosticSeverity
+import org.eclipse.lsp4j.MessageType
 import java.io.IOException
-import java.nio.file.Paths
 
 
 class ProBKernel @Inject constructor(private val injector : Injector, val classicalBFactory : ClassicalBFactory,
                                      private val animationSelector: AnimationSelector, private val animator : ReusableAnimator) {
 
+    private val communicator : Communicator = Communicator
+
+    fun check(path : String, settings : ProBSettings) : List<Diagnostic>{
+        communicator.sendDebugMessage("Unload old machine", MessageType.Info)
+        unloadMachine()
+        val factory = injector.getInstance(FactoryProvider.factoryClassFromExtension(path.substringAfterLast(".")))
+        val warningListener = MyWarningListener()
+        animator.addWarningListener(warningListener)
+        val parseErrors = mutableListOf<Diagnostic>()
+        communicator.sendDebugMessage("Load new machine", MessageType.Info)
+        loadMachine(
+                java.util.function.Function { stateSpace : StateSpace ->
+                    try {
+                        val extractedModel = factory.extract(path)
+                        val stateSpaceWithSettings = extractedModel.load(prepareAdditionalSettings(settings))
+                        extractedModel.loadIntoStateSpace(stateSpaceWithSettings)
+                    } catch (e: IOException) {
+                        throw RuntimeException(e)
+                    } catch (e: ModelTranslationError) {
+                        throw RuntimeException(e)
+                    }catch (e : ProBError){
+                        parseErrors.addAll(convertErrorItems(e.errors))
+                    }
+                    Trace(stateSpace)
+                }, settings)
+        return listOf(warningListener.getWarnings(), parseErrors).flatten()
+    }
 
     private fun loadMachine(newTraceCreator :java.util.function.Function<StateSpace, Trace>, settings: ProBSettings){
         val newStateSpace = animator.createStateSpace()
@@ -35,7 +62,7 @@ class ProBKernel @Inject constructor(private val injector : Injector, val classi
     }
 
     private fun executeAdditionalOptions(settings : ProBSettings){
-        val newStateSpace = animator.createStateSpace()
+        animator.createStateSpace()
         if(settings.wdChecks){
             animator.execute(CheckWellDefinednessCommand())
         }
@@ -51,30 +78,8 @@ class ProBKernel @Inject constructor(private val injector : Injector, val classi
         }
     }
 
-    fun check(path : String, settings : ProBSettings) : List<Diagnostic>{
-        unloadMachine()
-        val factory = injector.getInstance(FactoryProvider.factoryClassFromExtension(path.substringAfterLast(".")))
-        val warningListener = MyWarningListener()
-        animator.addWarningListener(warningListener)
-        val parseErrors = mutableListOf<Diagnostic>()
-        loadMachine(
-                java.util.function.Function { stateSpace : StateSpace ->
-                    try {
-                        factory.extract(path).load(prepareAdditionalSettings(settings))
-                        loadIntoStateSpace(stateSpace)
-                    } catch (e: IOException) {
-                        throw RuntimeException(e)
-                    } catch (e: ModelTranslationError) {
-                        throw RuntimeException(e)
-                    }catch (e : ProBError){
-                        parseErrors.addAll(convertErrorItems(e.errors))
-                    }
-                    Trace(stateSpace)
-                }, settings)
-        return listOf(warningListener.getWarnings(), parseErrors).flatten()
-    }
 
-    fun prepareAdditionalSettings(proBSettings: ProBSettings): MutableMap<String, String> {
+    private fun prepareAdditionalSettings(proBSettings: ProBSettings): MutableMap<String, String> {
         val settings = mutableMapOf<String, String>()
         if(proBSettings.performanceHints) {
             settings["STRICT_CLASH_CHECKING"] = "TRUE"
diff --git a/src/main/kotlin/b/language/server/proBMangement/prob2/ProBKernelManager.kt b/src/main/kotlin/b/language/server/proBMangement/prob2/ProBKernelManager.kt
index 4da6661..5691d3c 100644
--- a/src/main/kotlin/b/language/server/proBMangement/prob2/ProBKernelManager.kt
+++ b/src/main/kotlin/b/language/server/proBMangement/prob2/ProBKernelManager.kt
@@ -1,39 +1,67 @@
 package b.language.server.proBMangement.prob2
 
 import ProBKernel
+import b.language.server.communication.Communicator
+import b.language.server.communication.CommunicatorInterface
 import b.language.server.dataStorage.ProBSettings
 import b.language.server.dataStorage.Settings
 import b.language.server.proBMangement.ProBInterface
+import b.language.server.proBMangement.WrongPathException
 import com.google.inject.Guice
 import com.google.inject.Stage
 import org.eclipse.lsp4j.Diagnostic
+import org.eclipse.lsp4j.MessageType
 
 /**
  * Setup a connection to ProB2 Kernel
  */
-class ProBKernelManager : ProBInterface{
+class ProBKernelManager(private val communicator : CommunicatorInterface) : ProBInterface{
 
     private lateinit var kernel : ProBKernel
+    private var lastProBHome : String = "DEFAULT"
 
-     /**
+    /**
      * Checks the given document with the help of ProB
      * @param uri the source to check
+     * @param settings the settings for this document
      * @return a list of all problems found
      */
     override fun checkDocument(uri: String, settings: Settings): List<Diagnostic> {
         val proBSettings = ProBSettings(settings.wdChecks, settings.strictChecks, settings.performanceHints)
         val probHome = settings.probHome.absolutePath
 
-        if(probHome != "DEFAULT" && !this::kernel.isInitialized)
-        {
-            if(probHome != "DEFAULT"){
-                System.setProperty("prob.home", probHome)
-            }
-            val injector = Guice.createInjector(Stage.PRODUCTION, ProBKernelModule())
-            kernel = injector.getInstance(ProBKernel::class.java)
+        communicator.sendDebugMessage("checking document", MessageType.Info)
+
+        return if(probHome == lastProBHome && this::kernel.isInitialized){
+            kernel.check(uri, proBSettings)
+        }else{
+            lastProBHome = probHome
+            setup(probHome).check(uri, proBSettings)
         }
 
-        return kernel.check(uri, proBSettings)
+    }
+
+    /**
+     * @param probHome the home of prob; DEFAULT for the prob version delivered with the library
+     * @return an instance of prob kernel
+     */
+    fun setup(probHome : String) : ProBKernel{
+        if(probHome != "DEFAULT"){
+            System.setProperty("prob.home", probHome)
+            communicator.sendDebugMessage("$probHome selected", MessageType.Info)
+        }else{
+            communicator.sendDebugMessage("default prob selected", MessageType.Info)
+        }
+
+        val injector = Guice.createInjector(Stage.PRODUCTION, ProBKernelModule())
+        val kernel : ProBKernel
+        try{
+            kernel = injector.getInstance(ProBKernel::class.java)
+        }catch (e : de.prob.exception.CliError){
+            communicator.sendDebugMessage("wrong path to prob", MessageType.Error)
+            throw WrongPathException("wrong path to prob $probHome")
+        }
+        return kernel
     }
 
 
diff --git a/src/test/kotlin/b/language/server/ProBCommandLineTest.kt b/src/test/kotlin/b/language/server/ProBCommandLineTest.kt
index 2f9a74f..06bec3e 100644
--- a/src/test/kotlin/b/language/server/ProBCommandLineTest.kt
+++ b/src/test/kotlin/b/language/server/ProBCommandLineTest.kt
@@ -3,7 +3,6 @@ package b.language.server
 import b.language.server.dataStorage.Position
 import b.language.server.dataStorage.Problem
 import b.language.server.dataStorage.Settings
-import b.language.server.proBMangement.probCli.ProBCommandLineAccess
 import com.google.gson.Gson
 import org.eclipse.lsp4j.Diagnostic
 import org.eclipse.lsp4j.DiagnosticSeverity
@@ -15,7 +14,7 @@ import kotlin.test.assertEquals
 import kotlin.test.assertTrue
 
 class ProBCommandLineTest{
-
+    /*
     @Test
     fun test_readProblems(@TempDir tempPath : File = File("tmp"))
     {
@@ -191,6 +190,6 @@ class ProBCommandLineTest{
         val result = ProBCommandLineAccess().createFolder(errorDict, errorPath)
         assertTrue(result)
     }
-
+*/
 
 }
\ No newline at end of file
diff --git a/src/test/kotlin/b/language/server/communication/DummyCommunication.kt b/src/test/kotlin/b/language/server/communication/DummyCommunication.kt
new file mode 100644
index 0000000..e6de95d
--- /dev/null
+++ b/src/test/kotlin/b/language/server/communication/DummyCommunication.kt
@@ -0,0 +1,49 @@
+package b.language.server.communication
+
+import b.language.server.communication.CommunicatorInterface
+import org.eclipse.lsp4j.MessageType
+import org.eclipse.lsp4j.PublishDiagnosticsParams
+
+
+/**
+ * The Communicator is a side effect class, we don“t want side effects in out tests; especially such that are for
+ * debug purpose only
+ */
+class DummyCommunication : CommunicatorInterface {
+
+    val outputCollector = mutableListOf<String>()
+
+    /**
+     * Sends the diagnostics
+     *
+     * @param diagnostics object containing the Diagnostics
+     */
+    override fun publishDiagnostics(diagnostics: PublishDiagnosticsParams) {}
+
+    /**
+     * Sends a debug message resulting in a output channel message
+     *
+     * @param message the message to send
+     * @param severity the Severity of the message (Error/Info/Warning)
+     */
+    override fun sendDebugMessage(message: String, severity: MessageType) {
+        outputCollector.add(message)
+    }
+
+    /**
+     * Sends a popup message resulting in a popup message
+     *
+     * @param message the message to send
+     * @param severity the Severity of the message (Error/Info/Warning)
+     */
+    override fun showMessage(message: String, severity: MessageType) {}
+
+    /**
+     * To enable/disable debug mode
+     *
+     * @param mode the new state of the debug mode
+     */
+    override fun setDebugMode(mode: Boolean) {
+
+    }
+}
\ No newline at end of file
diff --git a/src/test/kotlin/b/language/server/prob2/proBMangement/ProBKernelManagerTest.kt b/src/test/kotlin/b/language/server/prob2/proBMangement/ProBKernelManagerTest.kt
new file mode 100644
index 0000000..ef33ff0
--- /dev/null
+++ b/src/test/kotlin/b/language/server/prob2/proBMangement/ProBKernelManagerTest.kt
@@ -0,0 +1,37 @@
+package b.language.server.prob2.proBMangement
+
+import b.language.server.communication.DummyCommunication
+import b.language.server.proBMangement.WrongPathException
+import b.language.server.proBMangement.prob2.ProBKernelManager
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertThrows
+import org.junit.jupiter.api.function.Executable
+import kotlin.test.assertEquals
+
+class ProBKernelManagerTest {
+
+    @Test
+    fun testCorrectBaseSetup(){
+        val dummyCommunication = DummyCommunication()
+        val proBKernelManager = ProBKernelManager(dummyCommunication)
+
+        proBKernelManager.setup("DEFAULT")
+
+        assertEquals(listOf("default prob selected"), dummyCommunication.outputCollector)
+    }
+
+
+    @Test
+    fun testCorrectBaseSetupWrongPath(){
+        val dummyCommunication = DummyCommunication()
+        val proBKernelManager = ProBKernelManager(dummyCommunication)
+
+        Assertions.assertThrows(WrongPathException::class.java) { proBKernelManager.setup("NOTDEFAULT") }
+    }
+
+    @Test
+    fun testCheckDocument(){
+
+    }
+}
\ No newline at end of file
-- 
GitLab