Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
23 changes: 22 additions & 1 deletion resharper/src/UnitTesting/GodotTestRunnerHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ public override IPreparedProcess StartProcess(ProcessStartInfo startInfo, ITestR
var patcher = new GodotPatcher(solution, scenePaths.Single().MakeRelativeTo(solutionDirectory));
var request = context.RuntimeEnvironment.ToJetProcessRuntimeRequest();
var patch = new JetProcessStartInfoPatch(patcher, request);
return new PreparedProcess(rawStartInfo, patch);
var preparedProcess = new PreparedProcess(rawStartInfo, patch);
CaptureOutputIfRequired(preparedProcess, context);
return preparedProcess;
}

public override IEnumerable<Assembly> InProcessAssemblies => EmptyArray<Assembly>.Instance;
Expand Down Expand Up @@ -93,6 +95,25 @@ private Task PrepareDebuggerServer(IUnitTestRun run)

return tcs.Task;
}

private void CaptureOutputIfRequired(IPreparedProcess process, ITestRunnerContext context)
{
// in debug redirect output to
if (context is ITestRunnerExecutionContext executionContext &&
executionContext.Run.HostController.HostId == WellKnownHostProvidersIds.DebugProviderId)
{
var solution = context.RuntimeEnvironment.Project.GetSolution();
process.ErrorLineRead += line => { Send(solution, context.Lifetime, line, TestRunnerOutputEventType.Error); };
process.OutputLineRead += line => { Send(solution, context.Lifetime, line, TestRunnerOutputEventType.Message); };
}
}

private static void Send(ISolution solution, Lifetime lifetime, string line, TestRunnerOutputEventType gameOutputEventType)
{
if (line == null) return;
var model = solution.GetProtocolSolution().GetGodotFrontendBackendModel();
solution.Locks.ExecuteOrQueueEx(lifetime, "GameOutputEvent", () => model.OnTestRunnerOutputEvent(new TestRunnerOutputEvent(gameOutputEventType, line)));
}

private class GodotPatcher : IProcessStartInfoPatcher
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,22 @@ import com.jetbrains.rd.generator.nova.kotlin.Kotlin11Generator
@Suppress("unused")
object GodotFrontendBackendModel : Ext(SolutionModel.Solution) {

val TestRunnerOutputEvent = structdef("TestRunnerOutputEvent"){
field("type", enum("TestRunnerOutputEventType") {
+"Error"
+"Message"
})
field("message", string)
}

init {
setting(Kotlin11Generator.Namespace, "com.jetbrains.rider.model.godot.frontendBackend")
setting(CSharp50Generator.Namespace, "JetBrains.Rider.Model.Godot.FrontendBackend")

// Actions called from the backend to the frontend
sink("activateRider", void).documentation = "Tell Rider to bring itself to the foreground. Called when opening a file from Godot"
callback("startDebuggerServer", void, int).documentation = "Tell the frontend to start listening for debugger. Returns port. Used for debugging unit tests"
sink("onTestRunnerOutputEvent", TestRunnerOutputEvent).documentation = "Pass output of the game under tests to frontend"

// Misc backend/fronted context
property("godotPath", string).documentation = "Path to GodotEditor"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,28 @@ import com.intellij.openapi.wm.WindowManager
import com.intellij.util.BitUtil
import com.jetbrains.rd.framework.impl.RdTask
import com.jetbrains.rd.platform.util.idea.ProtocolSubscribedProjectComponent
import com.jetbrains.rd.util.reactive.AddRemove
import com.jetbrains.rd.util.reactive.adviseNotNull
import com.jetbrains.rd.util.lifetime.onTermination
import com.jetbrains.rd.util.reactive.*
import com.jetbrains.rider.debugger.DebuggerInitializingState
import com.jetbrains.rider.debugger.DotNetDebugProcess
import com.jetbrains.rider.debugger.RiderDebugActiveDotNetSessionsTracker
import com.jetbrains.rider.debugger.tryWriteMessageToConsoleView
import com.jetbrains.rider.model.debuggerWorker.OutputMessageWithSubject
import com.jetbrains.rider.model.debuggerWorker.OutputSubject
import com.jetbrains.rider.model.debuggerWorker.OutputType
import com.jetbrains.rider.model.godot.frontendBackend.TestRunnerOutputEventType
import com.jetbrains.rider.model.godot.frontendBackend.godotFrontendBackendModel
import com.jetbrains.rider.plugins.godot.run.GodotRunConfigurationGenerator
import com.jetbrains.rider.plugins.godot.run.configurations.GodotDotNetRemoteConfiguration
import com.jetbrains.rider.plugins.godot.run.configurations.GodotDotNetRemoteConfigurationFactory
import com.jetbrains.rider.projectView.solution
import com.jetbrains.rider.run.configurations.remote.DotNetRemoteConfiguration
import com.jetbrains.rider.run.configurations.remote.MonoRemoteConfigType
import com.jetbrains.rider.util.NetUtils
import java.awt.Frame

class FrontendBackendHost(project: Project) : ProtocolSubscribedProjectComponent(project) {
val model = project.solution.godotFrontendBackendModel
private val model = project.solution.godotFrontendBackendModel
private val myDebugProcessProperty: IProperty<DotNetDebugProcess?> = Property(null)

init {
model.activateRider.advise(projectComponentLifetime) {
Expand All @@ -35,26 +43,42 @@ class FrontendBackendHost(project: Project) : ProtocolSubscribedProjectComponent
}
}

model.onTestRunnerOutputEvent.advise(projectComponentLifetime) {
myDebugProcessProperty.value?.console?.tryWriteMessageToConsoleView(
OutputMessageWithSubject(
output = "${it.message}\r\n",
type = when (it.type) {
TestRunnerOutputEventType.Message -> OutputType.Info
TestRunnerOutputEventType.Error -> OutputType.Error
},
subject = OutputSubject.Default
)
)
}

model.startDebuggerServer.set { lt, _ ->
val task = RdTask<Int>()
val runManager = RunManager.getInstance(project)
val configurationType = ConfigurationTypeUtil.findConfigurationType(MonoRemoteConfigType::class.java)
val runConfiguration = runManager.createConfiguration(
GodotRunConfigurationGenerator.ATTACH_CONFIGURATION_NAME,
configurationType.factory
GodotDotNetRemoteConfigurationFactory(configurationType)
)
val remoteConfiguration = runConfiguration.configuration as DotNetRemoteConfiguration
val remoteConfiguration = runConfiguration.configuration as GodotDotNetRemoteConfiguration
remoteConfiguration.listenPortForConnections = true
remoteConfiguration.port = NetUtils.findFreePort(500013)
remoteConfiguration.address = "127.0.0.1"

val processTracker: RiderDebugActiveDotNetSessionsTracker =
RiderDebugActiveDotNetSessionsTracker.getInstance(project)
processTracker.dotNetDebugProcesses.change.advise(projectComponentLifetime) { (event, debugProcess) ->
processTracker.dotNetDebugProcesses.change.advise(lt) { (event, debugProcess) ->
if (event == AddRemove.Add) {
debugProcess.initializeDebuggerTask.debuggerInitializingState.advise(lt) {
if (it == DebuggerInitializingState.Initialized)
if (it == DebuggerInitializingState.Initialized) {
myDebugProcessProperty.set(debugProcess)
debugProcess.sessionLifetime.onTermination { myDebugProcessProperty.set(null) }
task.set(remoteConfiguration.port)
}
if (it == DebuggerInitializingState.Canceled)
task.set(0)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,20 @@ import com.jetbrains.rider.model.debuggerWorker.DebuggerWorkerModel
import com.jetbrains.rider.model.debuggerWorker.OutputMessageWithSubject
import com.jetbrains.rider.model.debuggerWorker.OutputSubject
import com.jetbrains.rider.model.debuggerWorker.OutputType

import com.jetbrains.rider.model.godot.frontendBackend.godotFrontendBackendModel
import com.jetbrains.rider.plugins.godot.model.debuggerWorker.godotDebuggerWorkerModel
import com.jetbrains.rider.projectView.solution
import com.jetbrains.rider.run.*
import com.jetbrains.rider.run.configurations.remote.MonoConnectRemoteProfileState
import com.jetbrains.rider.run.ExternalConsoleMediator
import com.jetbrains.rider.run.WorkerRunInfo
import com.jetbrains.rider.run.configurations.remote.RemoteConfiguration
import com.jetbrains.rider.run.createEmptyConsoleCommandLine
import com.jetbrains.rider.run.withRawParameters
import com.jetbrains.rider.util.NetUtils

class GodotDebugProfileState(private val exeConfiguration: GodotDebugRunConfiguration, private val remoteConfiguration: RemoteConfiguration, executionEnvironment: ExecutionEnvironment)
: MonoConnectRemoteProfileState(remoteConfiguration, executionEnvironment) {
: GodotMonoConnectRemoteProfileState(remoteConfiguration, executionEnvironment) {
private val ansiEscapeDecoder = AnsiEscapeDecoder()

override val consoleKind: ConsoleKind = ConsoleKind.Normal

override suspend fun createDebuggerWorker(
workerCmd: GeneralCommandLine,
protocolModel: DebuggerWorkerModel,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.jetbrains.rider.plugins.godot.run.configurations

import com.intellij.execution.Executor
import com.intellij.execution.configurations.ConfigurationFactory
import com.intellij.execution.configurations.RunProfileState
import com.intellij.execution.executors.DefaultDebugExecutor
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.openapi.project.Project
import com.jetbrains.rider.run.configurations.remote.DotNetRemoteConfiguration

class GodotDotNetRemoteConfiguration(project: Project, factory: ConfigurationFactory, name:String): DotNetRemoteConfiguration(project, factory, name) {
override fun getState(executor: Executor, environment: ExecutionEnvironment): RunProfileState? {
if (executor.id != DefaultDebugExecutor.EXECUTOR_ID)
return null
return GodotMonoConnectRemoteProfileState(this, environment)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.jetbrains.rider.plugins.godot.run.configurations

import com.intellij.execution.configurations.RunConfiguration
import com.intellij.openapi.project.Project
import com.jetbrains.rider.run.configurations.DotNetConfigurationFactoryBase
import com.jetbrains.rider.run.configurations.remote.DotNetRemoteConfiguration
import com.jetbrains.rider.run.configurations.remote.MonoRemoteConfigType

class GodotDotNetRemoteConfigurationFactory(monoRemoteConfigType: MonoRemoteConfigType) : DotNetConfigurationFactoryBase<DotNetRemoteConfiguration>(monoRemoteConfigType){
override fun getId(): String {
// super.getId() does the same, but prints a deprecation message
return name
}

override fun createTemplateConfiguration(project: Project): RunConfiguration = GodotDotNetRemoteConfiguration(project, this, "")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.jetbrains.rider.plugins.godot.run.configurations

import com.intellij.execution.runners.ExecutionEnvironment
import com.jetbrains.rider.run.ConsoleKind
import com.jetbrains.rider.run.configurations.remote.MonoConnectRemoteProfileState
import com.jetbrains.rider.run.configurations.remote.RemoteConfiguration

open class GodotMonoConnectRemoteProfileState(remoteConfiguration: RemoteConfiguration, executionEnvironment: ExecutionEnvironment): MonoConnectRemoteProfileState(remoteConfiguration, executionEnvironment) {
override val consoleKind: ConsoleKind = ConsoleKind.Normal
}