[Gradle, K/N] Improve message when a simulator for test is not booted

If a simulator test task is configured to disable standalone mode and the simulator isn't booted, the default error message might sound cryptic, so we can give a more user-friendly error message
#KT-38317 Fixed
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeXcodeSimulatorTestsIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeXcodeSimulatorTestsIT.kt
index ec255e83a..ad57d3a 100644
--- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeXcodeSimulatorTestsIT.kt
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/native/NativeXcodeSimulatorTestsIT.kt
@@ -21,6 +21,32 @@
 @NativeGradlePluginTests
 @EnabledOnOs(OS.MAC)
 class NativeXcodeSimulatorTestsIT : KGPBaseTest() {
+    private val defaultIosSimulator by lazy {
+        val xcode = Xcode ?: error("XCode is expected to be defined")
+        xcode.getDefaultTestDeviceId(KonanTarget.IOS_SIMULATOR_ARM64) ?: error("No simulator found for iOS ARM64")
+    }
+
+    @DisplayName("A user-friendly error message is produced when the standalone mode is disabled and no simulator has booted")
+    @GradleTest
+    fun checkNoSimulatorErrorMessage(gradleVersion: GradleVersion) {
+        shutDownSimulators() // based on the tests order there no simulator should be booted, but anyway to be sure
+        // Note the test still may fail if you have booted the required simulator manually before running the test
+        // We don't shut down such simulators in the tests
+        project("native-test-ios-https-request", gradleVersion) {
+            buildGradleKts.append(
+                """
+                tasks.withType<org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeSimulatorTest> {
+                    device.set("$defaultIosSimulator")
+                    standalone.set(false)
+                }
+                """.trimIndent()
+            )
+            buildAndFail("check") {
+                assertOutputContains("The problem can be that you have not booted the required device or have configured the task to a different simulator. Please check the task output and its device configuration.")
+            }
+        }
+    }
+
     @DisplayName("iOS simulator test with an https request fails with default settings")
     @GradleTest
     fun checkSimulatorTestFailsInStandaloneMode(gradleVersion: GradleVersion) {
@@ -36,13 +62,11 @@
     @GradleTest
     fun checkSimulatorTestFailsInNonStandaloneMode(gradleVersion: GradleVersion) {
         project("native-test-ios-https-request", gradleVersion) {
-            val xcode = Xcode ?: error("XCode is expected to be defined")
-            val device = xcode.getDefaultTestDeviceId(KonanTarget.IOS_SIMULATOR_ARM64) ?: error("No simulator found for iOS ARM64")
-            bootXcodeSimulator(device)
+            bootXcodeSimulator(defaultIosSimulator)
             buildGradleKts.append(
                 """
                 tasks.withType<org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeSimulatorTest> {
-                    device.set("$device")
+                    device.set("$defaultIosSimulator")
                     standalone.set(false)
                 }
                 """.trimIndent()
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeAppleSimulatorTCServiceMessagesClient.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeAppleSimulatorTCServiceMessagesClient.kt
new file mode 100644
index 0000000..6f0bad0
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/NativeAppleSimulatorTCServiceMessagesClient.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.gradle.targets.native.internal
+
+import org.gradle.api.internal.tasks.testing.TestResultProcessor
+import org.gradle.api.provider.Provider
+import org.gradle.process.ProcessForkOptions
+import org.gradle.process.internal.ExecHandle
+import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClient
+import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSettings
+import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec
+import org.jetbrains.kotlin.gradle.plugin.internal.MppTestReportHelper
+import org.slf4j.Logger
+
+internal class NativeAppleSimulatorTCServiceMessagesTestExecutionSpec(
+    forkOptions: ProcessForkOptions,
+    args: List<String>,
+    checkExitCode: Boolean,
+    clientSettings: TCServiceMessagesClientSettings,
+    dryRunArgs: List<String>?,
+    private val standaloneMode: Provider<Boolean>,
+) : TCServiceMessagesTestExecutionSpec(forkOptions, args, checkExitCode, clientSettings, dryRunArgs) {
+    override fun createClient(
+        testResultProcessor: TestResultProcessor,
+        log: Logger,
+        testReporter: MppTestReportHelper
+    ): TCServiceMessagesClient {
+        return NativeAppleSimulatorTCServiceMessagesClient(testResultProcessor, clientSettings, log, testReporter, standaloneMode)
+    }
+}
+
+internal class NativeAppleSimulatorTCServiceMessagesClient(
+    results: TestResultProcessor,
+    settings: TCServiceMessagesClientSettings,
+    log: Logger,
+    testReporter: MppTestReportHelper,
+    private val standaloneMode: Provider<Boolean>
+) : TCServiceMessagesClient(results, settings, log, testReporter) {
+    override fun testFailedMessage(execHandle: ExecHandle, exitValue: Int) = when {
+        !standaloneMode.get() && exitValue == 149 -> """
+                You have standalone simulator tests run mode disabled and tests have failed to run.
+                The problem can be that you have not booted the required device or have configured the task to a different simulator. Please check the task output and its device configuration.
+                If you are sure that your setup is correct, please file an issue: https://kotl.in/issue
+            """.trimIndent()
+        else -> super.testFailedMessage(execHandle, exitValue)
+    }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTest.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTest.kt
index 5862a9f..b929f7d 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTest.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTest.kt
@@ -16,17 +16,17 @@
 import org.gradle.process.ProcessForkOptions
 import org.gradle.process.internal.DefaultProcessForkOptions
 import org.gradle.work.NormalizeLineEndings
-import org.jetbrains.kotlin.compilerRunner.konanVersion
 import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSettings
 import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec
 import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutor.Companion.TC_PROJECT_PROPERTY
+import org.jetbrains.kotlin.gradle.targets.native.internal.NativeAppleSimulatorTCServiceMessagesTestExecutionSpec
 import org.jetbrains.kotlin.gradle.targets.native.internal.parseKotlinNativeStackTraceAsJvm
 import org.jetbrains.kotlin.gradle.tasks.KotlinTest
 import java.io.File
 import java.util.concurrent.Callable
 import javax.inject.Inject
 
-abstract class KotlinNativeTest: KotlinTest() {
+abstract class KotlinNativeTest : KotlinTest() {
     @get:Inject
     abstract val providerFactory: ProviderFactory
 
@@ -80,8 +80,6 @@
     private val trackedEnvironmentVariablesKeys = mutableSetOf<String>()
 
 
-    private val konanVersion = project.konanVersion
-
     @Suppress("unused")
     @get:Input
     val trackedEnvironment
@@ -261,4 +259,16 @@
             ) +
                     testArgs(testLogger, checkExitCode, testGradleFilter, testNegativeGradleFilter, userArgs)
     }
+
+    override fun createTestExecutionSpec(): TCServiceMessagesTestExecutionSpec {
+        val origin = super.createTestExecutionSpec()
+        return NativeAppleSimulatorTCServiceMessagesTestExecutionSpec(
+            origin.forkOptions,
+            origin.args,
+            origin.checkExitCode,
+            origin.clientSettings,
+            origin.dryRunArgs,
+            standalone,
+        )
+    }
 }
\ No newline at end of file