mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[ide starter extended] run system metrics collection on each IDE launch
GitOrigin-RevId: c21263f35c0f563cb8d6fedff55de79e9e1b4464
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c9f4edb128
commit
c43aa0311a
@@ -1,3 +1,6 @@
|
||||
package com.intellij.ide.starter.bus
|
||||
|
||||
class Event<T>
|
||||
open class Event<T>(
|
||||
state: EventState = EventState.UNDEFINED,
|
||||
val data: T
|
||||
) : Signal(state)
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.intellij.ide.starter.bus
|
||||
|
||||
enum class EventState {
|
||||
UNDEFINED,
|
||||
|
||||
/** Right before the action */
|
||||
BEFORE,
|
||||
|
||||
/** After the action was completed */
|
||||
AFTER
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.intellij.ide.starter.bus
|
||||
|
||||
enum class EventTimelineState {
|
||||
/** Right before the action */
|
||||
READY,
|
||||
|
||||
/** After action was completed */
|
||||
FINISHED
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.intellij.ide.starter.bus
|
||||
|
||||
import com.intellij.ide.starter.utils.catchAll
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.drop
|
||||
@@ -10,89 +11,93 @@ import kotlinx.coroutines.flow.filterNotNull
|
||||
* @license Apache 2.0 https://github.com/Kosert/FlowBus/blob/master/LICENSE
|
||||
* Class for receiving events posted to [FlowBus]
|
||||
*
|
||||
* @param bus [FlowBus] instance to subscribe to. If not set, [EventBus] will be used
|
||||
* @param bus [FlowBus] instance to subscribe to. If not set, [StarterBus] will be used
|
||||
*/
|
||||
open class EventsReceiver @JvmOverloads constructor(
|
||||
private val bus: FlowBus = EventBus
|
||||
private val bus: FlowBus = StarterBus
|
||||
) {
|
||||
|
||||
private val jobs = mutableMapOf<Class<*>, Job>()
|
||||
private val jobs = mutableMapOf<Class<*>, Job>()
|
||||
|
||||
private var returnDispatcher: CoroutineDispatcher = Dispatchers.Default
|
||||
private var returnDispatcher: CoroutineDispatcher = Dispatchers.Default
|
||||
|
||||
/**
|
||||
* Set the `CoroutineDispatcher` which will be used to launch your callbacks.
|
||||
*
|
||||
* If this [EventsReceiver] was created on the main thread the default dispatcher will be [Dispatchers.Main].
|
||||
* In any other case [Dispatchers.Default] will be used.
|
||||
*/
|
||||
fun returnOn(dispatcher: CoroutineDispatcher): EventsReceiver {
|
||||
returnDispatcher = dispatcher
|
||||
return this
|
||||
/**
|
||||
* Set the `CoroutineDispatcher` which will be used to launch your callbacks.
|
||||
*
|
||||
* If this [EventsReceiver] was created on the main thread the default dispatcher will be [Dispatchers.Main].
|
||||
* In any other case [Dispatchers.Default] will be used.
|
||||
*/
|
||||
fun returnOn(dispatcher: CoroutineDispatcher): EventsReceiver {
|
||||
returnDispatcher = dispatcher
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to events that are type of [clazz] with the given [callback] function.
|
||||
* The [callback] can be called immediately if event of type [clazz] is present in the flow.
|
||||
*
|
||||
* @param clazz Type of event to subscribe to
|
||||
* @param skipRetained Skips event already present in the flow. This is `false` by default
|
||||
* @param callback The callback function
|
||||
* @return This instance of [EventsReceiver] for chaining
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun <T : Any> subscribeTo(
|
||||
clazz: Class<T>,
|
||||
skipRetained: Boolean = false,
|
||||
callback: suspend (event: T) -> Unit
|
||||
): EventsReceiver {
|
||||
|
||||
if (jobs.containsKey(clazz))
|
||||
throw IllegalArgumentException("Already subscribed for event type: $clazz")
|
||||
|
||||
val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
|
||||
throw throwable
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to events that are type of [clazz] with the given [callback] function.
|
||||
* The [callback] can be called immediately if event of type [clazz] is present in the flow.
|
||||
*
|
||||
* @param clazz Type of event to subscribe to
|
||||
* @param skipRetained Skips event already present in the flow. This is `false` by default
|
||||
* @param callback The callback function
|
||||
* @return This instance of [EventsReceiver] for chaining
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun <T : Any> subscribeTo(
|
||||
clazz: Class<T>,
|
||||
skipRetained: Boolean = false,
|
||||
callback: suspend (event: T) -> Unit
|
||||
): EventsReceiver {
|
||||
|
||||
if (jobs.containsKey(clazz))
|
||||
throw IllegalArgumentException("Already subscribed for event type: $clazz")
|
||||
|
||||
val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
|
||||
throw throwable
|
||||
val job = CoroutineScope(Job() + Dispatchers.Default + exceptionHandler).launch {
|
||||
bus.forEvent(clazz)
|
||||
.drop(if (skipRetained) 1 else 0)
|
||||
.filterNotNull()
|
||||
.collect {
|
||||
catchAll {
|
||||
withContext(returnDispatcher) { callback(it) }
|
||||
}
|
||||
}
|
||||
|
||||
val job = CoroutineScope(Job() + Dispatchers.Default + exceptionHandler).launch {
|
||||
bus.forEvent(clazz)
|
||||
.drop(if (skipRetained) 1 else 0)
|
||||
.filterNotNull()
|
||||
.collect { withContext(returnDispatcher) { callback(it) } }
|
||||
}
|
||||
|
||||
jobs[clazz] = job
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* A variant of [subscribeTo] that uses an instance of [EventCallback] as callback.
|
||||
*
|
||||
* @param clazz Type of event to subscribe to
|
||||
* @param skipRetained Skips event already present in the flow. This is `false` by default
|
||||
* @param callback Interface with implemented callback function
|
||||
* @return This instance of [EventsReceiver] for chaining
|
||||
* @see [subscribeTo]
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun <T : Any> subscribeTo(
|
||||
clazz: Class<T>,
|
||||
callback: EventCallback<T>,
|
||||
skipRetained: Boolean = false
|
||||
): EventsReceiver = subscribeTo(clazz, skipRetained) { callback.onEvent(it) }
|
||||
jobs[clazz] = job
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from events type of [clazz]
|
||||
*/
|
||||
fun <T : Any> unsubscribe(clazz: Class<T>) {
|
||||
jobs.remove(clazz)?.cancel()
|
||||
}
|
||||
/**
|
||||
* A variant of [subscribeTo] that uses an instance of [EventCallback] as callback.
|
||||
*
|
||||
* @param clazz Type of event to subscribe to
|
||||
* @param skipRetained Skips event already present in the flow. This is `false` by default
|
||||
* @param callback Interface with implemented callback function
|
||||
* @return This instance of [EventsReceiver] for chaining
|
||||
* @see [subscribeTo]
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun <T : Any> subscribeTo(
|
||||
clazz: Class<T>,
|
||||
callback: EventCallback<T>,
|
||||
skipRetained: Boolean = false
|
||||
): EventsReceiver = subscribeTo(clazz, skipRetained) { callback.onEvent(it) }
|
||||
|
||||
/**
|
||||
* Unsubscribe from all events
|
||||
*/
|
||||
fun unsubscribe() {
|
||||
jobs.values.forEach { it.cancel() }
|
||||
jobs.clear()
|
||||
}
|
||||
/**
|
||||
* Unsubscribe from events type of [clazz]
|
||||
*/
|
||||
fun <T : Any> unsubscribe(clazz: Class<T>) {
|
||||
jobs.remove(clazz)?.cancel()
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from all events
|
||||
*/
|
||||
fun unsubscribe() {
|
||||
jobs.values.forEach { it.cancel() }
|
||||
jobs.clear()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,6 @@ package com.intellij.ide.starter.bus
|
||||
*/
|
||||
inline fun <reified T : Any> FlowBus.dropEvent() = dropEvent(T::class.java)
|
||||
|
||||
/**
|
||||
* @see FlowBus.getLastEvent
|
||||
*/
|
||||
inline fun <reified T : Any> FlowBus.getLastEvent(): T? = getLastEvent(T::class.java)
|
||||
|
||||
/**
|
||||
* @see FlowBus.getFlow
|
||||
*/
|
||||
|
||||
@@ -1,25 +1,30 @@
|
||||
package com.intellij.ide.starter.bus
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* @author https://github.com/Kosert/FlowBus
|
||||
* @license Apache 2.0 https://github.com/Kosert/FlowBus/blob/master/LICENSE
|
||||
*
|
||||
* This class holds all state flows and handles event posting.
|
||||
* You can use [GlobalBus] that is just plain instance of this class or create your own implementation.
|
||||
* This class holds all shared flows and handles event posting.
|
||||
* You can use [StarterBus] that is just plain instance of this class or create your own implementation.
|
||||
*/
|
||||
open class FlowBus {
|
||||
|
||||
private val flows = mutableMapOf<Class<*>, MutableStateFlow<*>>()
|
||||
private val flows = mutableMapOf<Class<*>, MutableSharedFlow<*>>()
|
||||
|
||||
/**
|
||||
* Gets a MutableStateFlow for events of the given type. Creates new if one doesn't exist.
|
||||
* @return MutableStateFlow for events that are instances of clazz
|
||||
* Gets a MutableSharedFlow for events of the given type. Creates new if one doesn't exist.
|
||||
* @return MutableSharedFlow for events that are instances of clazz
|
||||
*/
|
||||
internal fun <T : Any> forEvent(clazz: Class<T>): MutableStateFlow<T?> {
|
||||
return flows.getOrPut(clazz) { MutableStateFlow<T?>(null) } as MutableStateFlow<T?>
|
||||
internal fun <T : Any> forEvent(clazz: Class<T>): MutableSharedFlow<T?> {
|
||||
return flows.getOrPut(clazz) {
|
||||
MutableSharedFlow<T?>(extraBufferCapacity = 5000)
|
||||
} as MutableSharedFlow<T?>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,43 +38,36 @@ open class FlowBus {
|
||||
* @see [SharedFlow]
|
||||
*/
|
||||
fun <T : Any> getFlow(clazz: Class<T>): Flow<T> {
|
||||
return forEvent(clazz).asStateFlow().filterNotNull()
|
||||
return forEvent(clazz).filterNotNull()
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts new event to StateFlow of the [event] type.
|
||||
* Posts new event to SharedFlow of the [event] type.
|
||||
* @param retain If the [event] should be retained in the flow for future subscribers. This is true by default.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun <T : Any> post(event: T, retain: Boolean = true) {
|
||||
val flow = forEvent(event.javaClass)
|
||||
|
||||
flow.tryEmit(event).also {
|
||||
if (!it)
|
||||
throw IllegalStateException("StateFlow cannot take element, this should never happen")
|
||||
throw IllegalStateException("SharedFlow cannot take element, this should never happen")
|
||||
}
|
||||
if (!retain) {
|
||||
// without starting a coroutine here, the event is dropped immediately
|
||||
// and not delivered to subscribers
|
||||
CoroutineScope(Job() + Dispatchers.Unconfined).launch {
|
||||
CoroutineScope(Job() + Dispatchers.Default).launch {
|
||||
dropEvent(event.javaClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns last posted event that was instance of [clazz] or `null` if no event of the given type is retained.
|
||||
* @return Retained event that is instance of [clazz]
|
||||
*/
|
||||
fun <T : Any> getLastEvent(clazz: Class<T>): T? {
|
||||
return flows.getOrElse(clazz) { null }?.value as T?
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes retained event of type [clazz]
|
||||
*/
|
||||
fun <T> dropEvent(clazz: Class<T>) {
|
||||
if (!flows.contains(clazz)) return
|
||||
val channel = flows[clazz] as MutableStateFlow<T?>
|
||||
val channel = flows[clazz] as MutableSharedFlow<T?>
|
||||
channel.tryEmit(null)
|
||||
}
|
||||
|
||||
@@ -78,7 +76,7 @@ open class FlowBus {
|
||||
*/
|
||||
fun dropAll() {
|
||||
flows.values.forEach {
|
||||
(it as MutableStateFlow<Any?>).tryEmit(null)
|
||||
(it as MutableSharedFlow<Any?>).tryEmit(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
package com.intellij.ide.starter.bus
|
||||
|
||||
/** Event, that works as a marker, that some action happened */
|
||||
class Signal<T>(val state: EventTimelineState)
|
||||
open class Signal(val state: EventState = EventState.UNDEFINED)
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
package com.intellij.ide.starter.bus
|
||||
|
||||
object EventBus : FlowBus()
|
||||
object StarterBus : FlowBus()
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.intellij.ide.starter.bus
|
||||
|
||||
object StarterListener : EventsReceiver()
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.intellij.ide.starter.runner
|
||||
|
||||
import com.intellij.ide.starter.bus.EventState
|
||||
import com.intellij.ide.starter.bus.StarterBus
|
||||
import com.intellij.ide.starter.di.di
|
||||
import com.intellij.ide.starter.exec.ExecOutputRedirect
|
||||
import com.intellij.ide.starter.exec.ExecTimeoutException
|
||||
@@ -90,6 +92,8 @@ data class IDERunContext(
|
||||
|
||||
// TODO: refactor this
|
||||
private fun prepareToRunIDE(): IDEStartResult {
|
||||
StarterBus.post(IdeLaunchEvent(EventState.BEFORE, this))
|
||||
|
||||
deleteSavedAppStateOnMac()
|
||||
val paths = testContext.paths
|
||||
val logsDir = paths.logsDir.createDirectories()
|
||||
@@ -266,37 +270,42 @@ data class IDERunContext(
|
||||
}
|
||||
finally {
|
||||
|
||||
if (SystemInfo.isWindows) {
|
||||
destroyGradleDaemonProcessIfExists()
|
||||
}
|
||||
|
||||
listOf(heapDumpOnOomDirectory, jvmCrashLogDirectory).filter { dir ->
|
||||
dir.listDirectoryEntries().isEmpty()
|
||||
}.forEach { it.toFile().deleteRecursively() }
|
||||
|
||||
ErrorReporter.reportErrorsAsFailedTests(logsDir / "script-errors", contextName)
|
||||
val (artifactPath, artifactName) = if (successfulRun) contextName to "logs" else "run/$contextName" to "crash"
|
||||
testContext.publishArtifact(logsDir, artifactPath, formatArtifactName(artifactName, testContext.testName))
|
||||
val snapshotFiles = Files.list(testContext.paths.snapshotsDir).use { it.filter { it.isRegularFile() }.toList() }
|
||||
if (snapshotFiles.isNotEmpty()) {
|
||||
testContext.publishArtifact(testContext.paths.snapshotsDir, contextName, formatArtifactName("snapshots", testContext.testName))
|
||||
}
|
||||
if (codeBuilder != null) {
|
||||
host.tearDown(testContext)
|
||||
}
|
||||
|
||||
val closeContext = object : IDERunCloseContext {
|
||||
override val wasRunSuccessful: Boolean = successfulRun
|
||||
}
|
||||
|
||||
closeHandlers.forEach {
|
||||
try {
|
||||
it.invoke(closeContext)
|
||||
try {
|
||||
if (SystemInfo.isWindows) {
|
||||
destroyGradleDaemonProcessIfExists()
|
||||
}
|
||||
catch (t: Throwable) {
|
||||
logOutput("Failed to complete close step. ${t.message}.\n" + t)
|
||||
t.printStackTrace(System.err)
|
||||
|
||||
listOf(heapDumpOnOomDirectory, jvmCrashLogDirectory).filter { dir ->
|
||||
dir.listDirectoryEntries().isEmpty()
|
||||
}.forEach { it.toFile().deleteRecursively() }
|
||||
|
||||
ErrorReporter.reportErrorsAsFailedTests(logsDir / "script-errors", contextName)
|
||||
val (artifactPath, artifactName) = if (successfulRun) contextName to "logs" else "run/$contextName" to "crash"
|
||||
testContext.publishArtifact(logsDir, artifactPath, formatArtifactName(artifactName, testContext.testName))
|
||||
val snapshotFiles = Files.list(testContext.paths.snapshotsDir).use { it.filter { it.isRegularFile() }.toList() }
|
||||
if (snapshotFiles.isNotEmpty()) {
|
||||
testContext.publishArtifact(testContext.paths.snapshotsDir, contextName, formatArtifactName("snapshots", testContext.testName))
|
||||
}
|
||||
if (codeBuilder != null) {
|
||||
host.tearDown(testContext)
|
||||
}
|
||||
|
||||
val closeContext = object : IDERunCloseContext {
|
||||
override val wasRunSuccessful: Boolean = successfulRun
|
||||
}
|
||||
|
||||
closeHandlers.forEach {
|
||||
try {
|
||||
it.invoke(closeContext)
|
||||
}
|
||||
catch (t: Throwable) {
|
||||
logOutput("Failed to complete close step. ${t.message}.\n" + t)
|
||||
t.printStackTrace(System.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
StarterBus.post(IdeLaunchEvent(EventState.AFTER, this))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.intellij.ide.starter.runner
|
||||
|
||||
import com.intellij.ide.starter.bus.Event
|
||||
import com.intellij.ide.starter.bus.EventState
|
||||
|
||||
class IdeLaunchEvent(state: EventState, runContext: IDERunContext) : Event<IDERunContext>(state, runContext)
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.intellij.ide.starter.utils
|
||||
|
||||
import java.util.*
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.name
|
||||
|
||||
|
||||
/**
|
||||
* Format: testMethodName => test-method-name
|
||||
*/
|
||||
fun String.hyphenateTestName(): String {
|
||||
|
||||
fun hyphenateString(input: String) = input
|
||||
.replace(" ", "-").trim()
|
||||
.replaceFirstChar { it.lowercase(Locale.getDefault()) }.toCharArray()
|
||||
.map {
|
||||
if (it.isUpperCase()) "-${it.lowercaseChar()}"
|
||||
else it
|
||||
}
|
||||
.joinToString(separator = "")
|
||||
|
||||
val hyphenatedPath = try {
|
||||
val originalPath = Path(this)
|
||||
|
||||
var convertedPath = Path("")
|
||||
(0 until originalPath.nameCount).map { pathNameIndex ->
|
||||
convertedPath = convertedPath.resolve(hyphenateString(originalPath.getName(pathNameIndex).name))
|
||||
}
|
||||
|
||||
convertedPath.toString()
|
||||
}
|
||||
catch (_: Exception) {
|
||||
return hyphenateString(this)
|
||||
}
|
||||
|
||||
return hyphenatedPath
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import java.time.format.DateTimeFormatter
|
||||
import kotlin.io.path.*
|
||||
import kotlin.time.Duration
|
||||
|
||||
fun formatArtifactName(artifactType: String, testName: String): String{
|
||||
fun formatArtifactName(artifactType: String, testName: String): String {
|
||||
val testNameFormatted = testName.replace("/", "-").replace(" ", "")
|
||||
val time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
|
||||
return "$artifactType-$testNameFormatted-$time"
|
||||
@@ -35,7 +35,7 @@ inline fun catchAll(action: () -> Unit) {
|
||||
action()
|
||||
}
|
||||
catch (t: Throwable) {
|
||||
logOutput("CatchAll swallowed error: ${t.message}")
|
||||
logError("CatchAll swallowed error: ${t.message}")
|
||||
logError(getThrowableText(t))
|
||||
}
|
||||
}
|
||||
@@ -135,7 +135,7 @@ fun String.withIndent(indent: String = " "): String = lineSequence().map { "$in
|
||||
private fun quoteArg(arg: String): String {
|
||||
|
||||
val specials = " #'\"\n\r\t\u000c"
|
||||
if(!specials.any { arg.contains(it) }){
|
||||
if (!specials.any { arg.contains(it) }) {
|
||||
return arg
|
||||
}
|
||||
|
||||
|
||||
+9
-7
@@ -1,6 +1,9 @@
|
||||
package com.intellij.ide.starter.tests.unit
|
||||
|
||||
import com.intellij.ide.starter.bus.*
|
||||
import com.intellij.ide.starter.bus.Signal
|
||||
import com.intellij.ide.starter.bus.StarterBus
|
||||
import com.intellij.ide.starter.bus.StarterListener
|
||||
import com.intellij.ide.starter.bus.subscribe
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.matchers.shouldBe
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -13,7 +16,6 @@ import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.time.Duration
|
||||
|
||||
class EventBusTest {
|
||||
private val receiver = EventsReceiver()
|
||||
private var isEventHappened: AtomicBoolean = AtomicBoolean(false)
|
||||
|
||||
private fun checkIsEventFired(shouldEventBeFired: Boolean, isEventFiredGetter: () -> Boolean) {
|
||||
@@ -22,7 +24,7 @@ class EventBusTest {
|
||||
withClue("Event should $shouldNotMessage be fired") {
|
||||
runBlocking {
|
||||
try {
|
||||
withTimeout(timeout = Duration.seconds(5)) {
|
||||
withTimeout(timeout = Duration.seconds(10)) {
|
||||
while (shouldEventBeFired != isEventFiredGetter()) {
|
||||
delay(Duration.milliseconds(500))
|
||||
}
|
||||
@@ -43,19 +45,19 @@ class EventBusTest {
|
||||
|
||||
@After
|
||||
fun afterEach() {
|
||||
receiver.unsubscribe()
|
||||
StarterListener.unsubscribe()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun filteringEventsByTypeIsWorking() {
|
||||
receiver.subscribe { event: Int ->
|
||||
StarterListener.subscribe { event: Signal ->
|
||||
isEventHappened.set(true)
|
||||
}
|
||||
|
||||
EventBus.post(Signal<EventBusTest>(EventTimelineState.READY))
|
||||
StarterBus.post(2)
|
||||
checkIsEventFired(false) { isEventHappened.get() }
|
||||
|
||||
EventBus.post(2)
|
||||
StarterBus.post(Signal())
|
||||
checkIsEventFired(true) { isEventHappened.get() }
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -5,6 +5,7 @@ import com.intellij.ide.starter.ide.IDETestContext
|
||||
import com.intellij.ide.starter.ide.InstalledIDE
|
||||
import com.intellij.ide.starter.models.TestCase
|
||||
import com.intellij.ide.starter.path.IDEDataPaths
|
||||
import com.intellij.ide.starter.utils.hyphenateTestName
|
||||
import io.kotest.matchers.shouldBe
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
@@ -30,7 +31,7 @@ class PluginsInjectionTest {
|
||||
|
||||
@Test
|
||||
fun theSameIDETestContextShouldBeReferencedInPluginConfigurator() {
|
||||
val testName = "example test"
|
||||
val testName = object {}.javaClass.enclosingMethod.name.hyphenateTestName()
|
||||
val paths = IDEDataPaths.createPaths(testName, testDirectory, useInMemoryFs = false)
|
||||
|
||||
val projectHome = testCase.projectInfo?.resolveProjectHome()
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package com.intellij.ide.starter.tests.unit
|
||||
|
||||
import com.intellij.ide.starter.bus.EventState
|
||||
import com.intellij.ide.starter.bus.StarterListener
|
||||
import com.intellij.ide.starter.bus.subscribe
|
||||
import com.intellij.ide.starter.di.di
|
||||
import com.intellij.ide.starter.ide.IDETestContext
|
||||
import com.intellij.ide.starter.ide.InstalledIDE
|
||||
import com.intellij.ide.starter.ide.command.CommandChain
|
||||
import com.intellij.ide.starter.models.TestCase
|
||||
import com.intellij.ide.starter.path.IDEDataPaths
|
||||
import com.intellij.ide.starter.runner.IdeLaunchEvent
|
||||
import com.intellij.ide.starter.utils.catchAll
|
||||
import com.intellij.ide.starter.utils.hyphenateTestName
|
||||
import io.kotest.assertions.assertSoftly
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.matchers.shouldBe
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import org.kodein.di.direct
|
||||
import org.kodein.di.instance
|
||||
import org.mockito.Mock
|
||||
import org.mockito.junit.jupiter.MockitoExtension
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
@ExtendWith(MockitoExtension::class)
|
||||
class RunIdeEventsTest {
|
||||
|
||||
@TempDir
|
||||
lateinit var testDirectory: Path
|
||||
|
||||
@Mock
|
||||
private lateinit var testCase: TestCase
|
||||
|
||||
@Mock
|
||||
private lateinit var ide: InstalledIDE
|
||||
|
||||
@Test
|
||||
fun eventsForIdeLaunchShouldBeFired() {
|
||||
val testName = object {}.javaClass.enclosingMethod.name.hyphenateTestName()
|
||||
val paths = IDEDataPaths.createPaths(testName, testDirectory, useInMemoryFs = false)
|
||||
|
||||
val projectHome = testCase.projectInfo?.resolveProjectHome()
|
||||
val context = IDETestContext(paths = paths,
|
||||
ide = ide,
|
||||
testCase = testCase,
|
||||
testName = testName,
|
||||
_resolvedProjectHome = projectHome,
|
||||
patchVMOptions = { this },
|
||||
ciServer = di.direct.instance())
|
||||
|
||||
val firedEvents = mutableListOf<IdeLaunchEvent>()
|
||||
|
||||
StarterListener.subscribe { event: IdeLaunchEvent -> firedEvents.add(event) }
|
||||
|
||||
catchAll {
|
||||
context.runIDE(commands = CommandChain())
|
||||
}
|
||||
|
||||
assertSoftly {
|
||||
withClue("During IDE run should be fired 2 events: before ide start and after ide finished") {
|
||||
firedEvents.first().state.shouldBe(EventState.BEFORE)
|
||||
firedEvents.last().state.shouldBe(EventState.AFTER)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user