Skip to content

Refactor settings in CLion plugin #350

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity
import org.utbot.cpp.clion.plugin.client.Client
import org.utbot.cpp.clion.plugin.settings.UTBotPluginSpecificSettings
import org.utbot.cpp.clion.plugin.settings.pluginSettings
import org.utbot.cpp.clion.plugin.settings.settings
import org.utbot.cpp.clion.plugin.ui.wizard.UTBotWizard
import org.utbot.cpp.clion.plugin.utils.getClient
import org.utbot.cpp.clion.plugin.utils.invokeOnEdt
import org.utbot.cpp.clion.plugin.utils.utbotSettings

class UTBotStartupActivity : StartupActivity {
override fun runActivity(project: Project) {
// start plugin and connect to server on project opening

project.getClient()
guessPathsOnFirstProjectOpen(project)
showWizardOnFirstProjectOpen(project)
}

private fun showWizardOnFirstProjectOpen(project: Project) {
val pluginSettings = service<UTBotPluginSpecificSettings>()
if (pluginSettings.isFirstLaunch && !Client.IS_TEST_MODE) {
pluginSettings.isFirstLaunch = false
invokeOnEdt {
Expand All @@ -31,7 +31,7 @@ class UTBotStartupActivity : StartupActivity {

private fun guessPathsOnFirstProjectOpen(project: Project) {
RunOnceUtil.runOnceForProject(project, "Guess UTBot paths in settings") {
project.utbotSettings.predictPaths()
project.settings.predictPaths()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,28 @@
package org.utbot.cpp.clion.plugin.client

import com.intellij.openapi.Disposable

import testsgen.Testgen

import com.intellij.openapi.project.Project

import io.grpc.Status
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch

import io.grpc.Status
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout

import kotlinx.coroutines.Job
import org.utbot.cpp.clion.plugin.grpc.getProjectConfigGrpcRequest
import org.utbot.cpp.clion.plugin.grpc.getVersionGrpcRequest
import org.utbot.cpp.clion.plugin.client.requests.CheckProjectConfigurationRequest
import org.utbot.cpp.clion.plugin.grpc.getProjectConfigGrpcRequest
import org.utbot.cpp.clion.plugin.listeners.ConnectionStatus
import org.utbot.cpp.clion.plugin.listeners.UTBotEventsListener
import org.utbot.cpp.clion.plugin.settings.projectIndependentSettings
import org.utbot.cpp.clion.plugin.utils.hasChildren
import org.utbot.cpp.clion.plugin.utils.logger
import org.utbot.cpp.clion.plugin.utils.utbotSettings
import testsgen.Testgen

/**
* Sends requests to grpc server via stub
Expand All @@ -38,13 +32,12 @@ class Client(
clientId: String,
private val loggingChannels: List<LoggingChannel>
) : Disposable,
GrpcClient(project.utbotSettings.port, project.utbotSettings.serverName, clientId) {
GrpcClient(projectIndependentSettings.port, projectIndependentSettings.serverName, clientId) {
var connectionStatus = ConnectionStatus.INIT
private set

private val messageBus = project.messageBus
private var newClient = true
private val settings = project.utbotSettings
private val logger = project.logger

/*
Expand All @@ -67,7 +60,7 @@ class Client(
val servicesCS: CoroutineScope = CoroutineScope(dispatcher + excHandler + SupervisorJob())

init {
logger.info { "Connecting to server on host: ${settings.serverName} , port: ${settings.port}" }
logger.info { "Connecting to server on host: ${projectIndependentSettings.serverName} , port: ${projectIndependentSettings.port}" }
startPeriodicHeartBeat()
}

Expand All @@ -92,18 +85,6 @@ class Client(

fun isServerAvailable() = connectionStatus == ConnectionStatus.CONNECTED

fun doHandShake() {
requestsCS.launch {
// Logger.info("sending HandShake request!")
try {
stub.handshake(getVersionGrpcRequest())
logger.info { "Handshake successful!" }
} catch (e: Exception) {
logger.warn { "HandShake failed with the following error: ${e.message}" }
}
}
}

private fun provideLoggingChannels() {
for (channel in loggingChannels) {
servicesCS.launch(CoroutineName(channel.toString())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import kotlinx.coroutines.flow.Flow
import org.utbot.cpp.clion.plugin.UTBot
import org.utbot.cpp.clion.plugin.actions.AskServerToGenerateBuildDir
import org.utbot.cpp.clion.plugin.actions.AskServerToGenerateJsonForProjectConfiguration
import org.utbot.cpp.clion.plugin.settings.settings
import org.utbot.cpp.clion.plugin.utils.getClient
import org.utbot.cpp.clion.plugin.utils.logger
import org.utbot.cpp.clion.plugin.utils.notifyError
import org.utbot.cpp.clion.plugin.utils.notifyInfo
import org.utbot.cpp.clion.plugin.utils.notifyUnknownResponse
import org.utbot.cpp.clion.plugin.utils.notifyWarning
import org.utbot.cpp.clion.plugin.utils.refreshAndFindIOFile
import org.utbot.cpp.clion.plugin.utils.utbotSettings
import testsgen.Testgen

abstract class ProjectConfigResponseHandler(
Expand Down Expand Up @@ -87,7 +87,7 @@ class CreateBuildDirHandler(
}
else -> notifyUnknownResponse(response, project)
}
refreshAndFindIOFile(project.utbotSettings.buildDirPath.toString())
refreshAndFindIOFile(project.settings.buildDirPath.toString())
}
}

Expand All @@ -106,6 +106,6 @@ class GenerateJsonHandler(
)
else -> notifyUnknownResponse(response, project)
}
refreshAndFindIOFile(project.utbotSettings.buildDirPath.toString())
refreshAndFindIOFile(project.settings.buildDirPath.toString())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.utbot.cpp.clion.plugin.grpc
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.project.Project
import org.utbot.cpp.clion.plugin.settings.settings
import org.utbot.cpp.clion.plugin.utils.activeProject
import org.utbot.cpp.clion.plugin.utils.convertToRemotePathIfNeeded
import testsgen.Testgen
Expand Down Expand Up @@ -88,13 +89,12 @@ private fun getPredicateGrpcRequest(predicate: String, returnValue: String, type
.build()

private fun getProjectGrpcRequest(project: Project): Testgen.ProjectRequest {
val settings = project.allSettings()
return Testgen.ProjectRequest.newBuilder()
.setSettingsContext(getSettingsContextMessage(project))
.setProjectContext(getProjectContextMessage(project))
.setTargetPath(settings.convertedTargetPath)
.addAllSourcePaths(settings.convertedSourcePaths)
.setSynchronizeCode(settings.isRemoteScenario)
.setTargetPath(project.settings.convertedTargetPath)
.addAllSourcePaths(project.settings.convertedSourcePaths)
.setSynchronizeCode(project.settings.isRemoteScenario)
.build()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
package org.utbot.cpp.clion.plugin.grpc

import com.intellij.openapi.components.service
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.project.Project
import org.utbot.cpp.clion.plugin.settings.UTBotAllSettings
import org.utbot.cpp.clion.plugin.settings.settings
import testsgen.Testgen

fun getSettingsContextMessage(project: Project): Testgen.SettingsContext {
val settings = project.allSettings()
val storedSettings = project.settings.storedSettings
return Testgen.SettingsContext.newBuilder()
.setVerbose(settings.verbose)
.setUseStubs(settings.useStubs)
.setTimeoutPerTest(settings.timeoutPerTest)
.setTimeoutPerFunction(settings.timeoutPerFunction)
.setGenerateForStaticFunctions(settings.generateForStaticFunctions)
.setUseDeterministicSearcher(settings.useDeterministicSearcher)
.setVerbose(storedSettings.verbose)
.setUseStubs(storedSettings.useStubs)
.setTimeoutPerTest(storedSettings.timeoutPerTest)
.setTimeoutPerFunction(storedSettings.timeoutPerFunction)
.setGenerateForStaticFunctions(storedSettings.generateForStaticFunctions)
.setUseDeterministicSearcher(storedSettings.useDeterministicSearcher)
.build()
}

fun getProjectContextMessage(project: Project): Testgen.ProjectContext {
val settings = project.allSettings()
return Testgen.ProjectContext.newBuilder()
.setProjectName(project.name)
.setProjectPath(settings.convertedProjectPath)
.setBuildDirRelativePath(settings.buildDirRelativePath)
.setResultsDirRelativePath("") // this path is used only by command line interface, server doesn't require it.
.setTestDirPath(settings.convertedTestDirPath)
.build()
}
fun getProjectContextMessage(project: Project): Testgen.ProjectContext = Testgen.ProjectContext.newBuilder()
.setProjectName(project.name)
.setProjectPath(project.settings.convertedProjectPath)
.setBuildDirRelativePath(project.settings.storedSettings.buildDirRelativePath)
.setResultsDirRelativePath("") // this path is used only by command line interface, server doesn't require it.
.setTestDirPath(project.settings.convertedTestDirPath)
.build()

fun Project.allSettings() = this.service<UTBotAllSettings>()
fun AnActionEvent.activeProject() = this.project
?: error("A project related to action event $this not found")
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.utbot.cpp.clion.plugin.listeners

import com.intellij.util.messages.Topic
import org.utbot.cpp.clion.plugin.settings.UTBotAllSettings
import org.utbot.cpp.clion.plugin.settings.UTBotAllProjectSettings

fun interface UTBotSettingsChangedListener {
companion object {
Expand All @@ -11,5 +11,5 @@ fun interface UTBotSettingsChangedListener {
)
}

fun settingsChanged(settings: UTBotAllSettings)
fun settingsChanged(settings: UTBotAllProjectSettings)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.utbot.cpp.clion.plugin.settings

import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project

val Project.settings: UTBotAllProjectSettings
get() = this.service()

val projectIndependentSettings: UTBotProjectIndependentSettings.State
get() = service<UTBotProjectIndependentSettings>().state

val pluginSettings: UTBotPluginSpecificSettings
get() = service()
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package org.utbot.cpp.clion.plugin.settings

import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import com.jetbrains.cidr.cpp.execution.CMakeAppRunConfiguration
import org.utbot.cpp.clion.plugin.listeners.UTBotSettingsChangedListener
import org.utbot.cpp.clion.plugin.ui.targetsToolWindow.UTBotTarget
import org.utbot.cpp.clion.plugin.utils.convertToRemotePathIfNeeded
import org.utbot.cpp.clion.plugin.utils.isWindows
import org.utbot.cpp.clion.plugin.utils.notifyWarning
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths

@Service
class UTBotAllProjectSettings(val project: Project) {
val storedSettings: UTBotProjectStoredSettings.State
get() = project.service<UTBotProjectStoredSettings>().state

var projectPath: String
get() = storedSettings.projectPath ?: project.guessProjectDir()?.path
?: error("Could not guess project path! Should be specified in settings by user")
set(value) {
storedSettings.projectPath = value
}

val buildDirPath: Path
get() = Paths.get(projectPath).resolve(storedSettings.buildDirRelativePath)

val convertedSourcePaths: List<String>
get() = storedSettings.sourceDirs.map { it.convertToRemotePathIfNeeded(project) }

val convertedTestDirPath: String
get() = storedSettings.testDirPath.convertToRemotePathIfNeeded(project)

val convertedTargetPath: String
get() = if (storedSettings.targetPath == UTBotTarget.autoTarget.path) storedSettings.targetPath
else storedSettings.targetPath.convertToRemotePathIfNeeded(project)

val convertedProjectPath: String get() = projectPath.convertToRemotePathIfNeeded(project)

/**
* If this property returns true, plugin must convert path sent and returned from server.
* @see [String.convertToRemotePathIfNeeded], [String.convertFromRemotePathIfNeeded]
*
* If we are on Windows, this is not a server, so it is always a remote scenario.
*/
val isRemoteScenario: Boolean
get() {
val isLocalHost = projectIndependentSettings.serverName == "localhost" || projectIndependentSettings.serverName == "127.0.0.01"
return !(storedSettings.remotePath == projectPath && isLocalHost) || isWindows
}

fun fireUTBotSettingsChanged() {
project.messageBus.syncPublisher(UTBotSettingsChangedListener.TOPIC).settingsChanged(this)
}

fun predictPaths() {
fun getSourceFoldersFromSources(sources: Collection<File>) = sources.map { it.parent }.toMutableSet()

storedSettings.remotePath = projectPath
storedSettings.buildDirRelativePath = "build-utbot"
storedSettings.targetPath = UTBotTarget.autoTarget.path

try {
storedSettings.testDirPath = Paths.get(projectPath, "tests").toString()
} catch (e: IllegalStateException) {
notifyWarning("Guessing settings failed: could not guess project path! Please specify project path in settings!")
}

val cmakeRunConfiguration = CMakeAppRunConfiguration.getSelectedConfigurationAndTarget(project)?.first
val buildConfigurationSources = cmakeRunConfiguration?.cMakeTarget?.buildConfigurations?.map { it.sources }
//TODO: why do we use firstOrNull here?
val cmakeConfiguration = buildConfigurationSources?.firstOrNull() ?: emptySet()

storedSettings.sourceDirs = getSourceFoldersFromSources(cmakeConfiguration)
}

companion object {
const val clientVersion = "2022.7"
const val DEFAULT_HOST = "localhost"
const val DEFAULT_PORT = 2121
}
}

data class UTBotSettingsModel(
var projectSettings: UTBotProjectStoredSettings.State,
var globalSettings: UTBotProjectIndependentSettings.State,
)
Loading