[rdct-tests] fix the situation when the focus is globally gone, and we are waiting for it: use robot

GitOrigin-RevId: 667cf7fe735b315eba200772cba149ce50c040cc
This commit is contained in:
Anastasia Katsman
2024-09-16 22:33:50 +02:00
committed by intellij-monorepo-bot
parent 09bdd80767
commit 302ca65ed7
3 changed files with 63 additions and 51 deletions

View File

@@ -57,7 +57,7 @@ class DistributedTestModel private constructor(
private val __RdTestSessionNullableSerializer = RdTestSession.nullable()
const val serializationHash = 5182389909649567871L
const val serializationHash = -2934857283786697801L
}
override val serializersOwner: ISerializersOwner get() = DistributedTestModel
@@ -377,7 +377,8 @@ class RdTestSession private constructor(
private val _closeProjectIfOpened: RdCall<Unit, Boolean>,
private val _runNextAction: RdCall<RdTestActionParameters, String?>,
private val _runNextActionGetComponentData: RdCall<RdTestActionParameters, RdTestComponentData>,
private val _requestFocus: RdCall<String, Boolean>,
private val _requestFocus: RdCall<Boolean, Boolean>,
private val _isFocused: RdCall<Unit, Boolean>,
private val _visibleFrameNames: RdCall<Unit, List<String>>,
private val _projectsNames: RdCall<Unit, List<String>>,
private val _makeScreenshot: RdCall<String, Boolean>,
@@ -407,13 +408,14 @@ class RdTestSession private constructor(
val _closeProjectIfOpened = RdCall.read(ctx, buffer, FrameworkMarshallers.Void, FrameworkMarshallers.Bool)
val _runNextAction = RdCall.read(ctx, buffer, RdTestActionParameters, __StringNullableSerializer)
val _runNextActionGetComponentData = RdCall.read(ctx, buffer, RdTestActionParameters, RdTestComponentData)
val _requestFocus = RdCall.read(ctx, buffer, FrameworkMarshallers.String, FrameworkMarshallers.Bool)
val _requestFocus = RdCall.read(ctx, buffer, FrameworkMarshallers.Bool, FrameworkMarshallers.Bool)
val _isFocused = RdCall.read(ctx, buffer, FrameworkMarshallers.Void, FrameworkMarshallers.Bool)
val _visibleFrameNames = RdCall.read(ctx, buffer, FrameworkMarshallers.Void, __StringListSerializer)
val _projectsNames = RdCall.read(ctx, buffer, FrameworkMarshallers.Void, __StringListSerializer)
val _makeScreenshot = RdCall.read(ctx, buffer, FrameworkMarshallers.String, FrameworkMarshallers.Bool)
val _isResponding = RdCall.read(ctx, buffer, FrameworkMarshallers.Void, FrameworkMarshallers.Bool)
val _projectsAreInitialised = RdCall.read(ctx, buffer, FrameworkMarshallers.Void, FrameworkMarshallers.Bool)
return RdTestSession(agentInfo, testClassName, testMethodName, traceCategories, debugCategories, _ready, _sendException, _exitApp, _showNotification, _closeProject, _forceLeaveAllModals, _closeProjectIfOpened, _runNextAction, _runNextActionGetComponentData, _requestFocus, _visibleFrameNames, _projectsNames, _makeScreenshot, _isResponding, _projectsAreInitialised).withId(_id)
return RdTestSession(agentInfo, testClassName, testMethodName, traceCategories, debugCategories, _ready, _sendException, _exitApp, _showNotification, _closeProject, _forceLeaveAllModals, _closeProjectIfOpened, _runNextAction, _runNextActionGetComponentData, _requestFocus, _isFocused, _visibleFrameNames, _projectsNames, _makeScreenshot, _isResponding, _projectsAreInitialised).withId(_id)
}
override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: RdTestSession) {
@@ -433,6 +435,7 @@ class RdTestSession private constructor(
RdCall.write(ctx, buffer, value._runNextAction)
RdCall.write(ctx, buffer, value._runNextActionGetComponentData)
RdCall.write(ctx, buffer, value._requestFocus)
RdCall.write(ctx, buffer, value._isFocused)
RdCall.write(ctx, buffer, value._visibleFrameNames)
RdCall.write(ctx, buffer, value._projectsNames)
RdCall.write(ctx, buffer, value._makeScreenshot)
@@ -455,7 +458,8 @@ class RdTestSession private constructor(
val closeProjectIfOpened: RdCall<Unit, Boolean> get() = _closeProjectIfOpened
val runNextAction: RdCall<RdTestActionParameters, String?> get() = _runNextAction
val runNextActionGetComponentData: RdCall<RdTestActionParameters, RdTestComponentData> get() = _runNextActionGetComponentData
val requestFocus: RdCall<String, Boolean> get() = _requestFocus
val requestFocus: RdCall<Boolean, Boolean> get() = _requestFocus
val isFocused: RdCall<Unit, Boolean> get() = _isFocused
val visibleFrameNames: RdCall<Unit, List<String>> get() = _visibleFrameNames
val projectsNames: RdCall<Unit, List<String>> get() = _projectsNames
val makeScreenshot: RdCall<String, Boolean> get() = _makeScreenshot
@@ -476,6 +480,7 @@ class RdTestSession private constructor(
_runNextAction.async = true
_runNextActionGetComponentData.async = true
_requestFocus.async = true
_isFocused.async = true
_visibleFrameNames.async = true
_projectsNames.async = true
_makeScreenshot.async = true
@@ -494,6 +499,7 @@ class RdTestSession private constructor(
bindableChildren.add("runNextAction" to _runNextAction)
bindableChildren.add("runNextActionGetComponentData" to _runNextActionGetComponentData)
bindableChildren.add("requestFocus" to _requestFocus)
bindableChildren.add("isFocused" to _isFocused)
bindableChildren.add("visibleFrameNames" to _visibleFrameNames)
bindableChildren.add("projectsNames" to _projectsNames)
bindableChildren.add("makeScreenshot" to _makeScreenshot)
@@ -523,7 +529,8 @@ class RdTestSession private constructor(
RdCall<Unit, Boolean>(FrameworkMarshallers.Void, FrameworkMarshallers.Bool),
RdCall<RdTestActionParameters, String?>(RdTestActionParameters, __StringNullableSerializer),
RdCall<RdTestActionParameters, RdTestComponentData>(RdTestActionParameters, RdTestComponentData),
RdCall<String, Boolean>(FrameworkMarshallers.String, FrameworkMarshallers.Bool),
RdCall<Boolean, Boolean>(FrameworkMarshallers.Bool, FrameworkMarshallers.Bool),
RdCall<Unit, Boolean>(FrameworkMarshallers.Void, FrameworkMarshallers.Bool),
RdCall<Unit, List<String>>(FrameworkMarshallers.Void, __StringListSerializer),
RdCall<Unit, List<String>>(FrameworkMarshallers.Void, __StringListSerializer),
RdCall<String, Boolean>(FrameworkMarshallers.String, FrameworkMarshallers.Bool),
@@ -552,6 +559,7 @@ class RdTestSession private constructor(
print("runNextAction = "); _runNextAction.print(printer); println()
print("runNextActionGetComponentData = "); _runNextActionGetComponentData.print(printer); println()
print("requestFocus = "); _requestFocus.print(printer); println()
print("isFocused = "); _isFocused.print(printer); println()
print("visibleFrameNames = "); _visibleFrameNames.print(printer); println()
print("projectsNames = "); _projectsNames.print(printer); println()
print("makeScreenshot = "); _makeScreenshot.print(printer); println()
@@ -578,6 +586,7 @@ class RdTestSession private constructor(
_runNextAction.deepClonePolymorphic(),
_runNextActionGetComponentData.deepClonePolymorphic(),
_requestFocus.deepClonePolymorphic(),
_isFocused.deepClonePolymorphic(),
_visibleFrameNames.deepClonePolymorphic(),
_projectsNames.deepClonePolymorphic(),
_makeScreenshot.deepClonePolymorphic(),

View File

@@ -76,7 +76,8 @@ object DistributedTestModel : Ext(TestRoot) {
call("closeProjectIfOpened", void, bool).async
call("runNextAction", RdTestActionParameters, string.nullable).async
call("runNextActionGetComponentData", RdTestActionParameters, RdTestComponentData).async
call("requestFocus", string, bool).async
call("requestFocus", bool, bool).async
call("isFocused", void, bool).async
call("visibleFrameNames", void, immutableList(string)).async
call("projectsNames", void, immutableList(string)).async
call("makeScreenshot", string, bool).async

View File

@@ -22,6 +22,7 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ex.ProjectManagerEx
import com.intellij.openapi.rd.util.adviseSuspendPreserveClientId
import com.intellij.openapi.rd.util.setSuspendPreserveClientId
import com.intellij.openapi.ui.isFocusAncestor
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.SystemInfoRt
import com.intellij.openapi.wm.WindowManager
@@ -49,6 +50,7 @@ import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.TestOnly
import java.awt.Component
import java.awt.Frame
import java.awt.KeyboardFocusManager
import java.awt.Window
import java.awt.image.BufferedImage
import java.io.File
@@ -197,7 +199,7 @@ open class DistributedTestHost(coroutineScope: CoroutineScope) {
return withContext(providedContext + clientId.asContextElement()) {
assert(ClientId.current == clientId) { "ClientId '${ClientId.current}' should equal $clientId one when test method starts" }
if (!app.isHeadlessEnvironment && isNotRdHost && (requestFocusBeforeStart ?: isCurrentThreadEdt())) {
requestFocus(actionTitle)
requestFocus(silent = false)
}
assert(ClientId.current == clientId) { "ClientId '${ClientId.current}' should equal $clientId one when after request focus" }
@@ -323,9 +325,9 @@ open class DistributedTestHost(coroutineScope: CoroutineScope) {
// actually doesn't really preserve clientId, not really important here
// https://youtrack.jetbrains.com/issue/RDCT-653/setSuspendPreserveClientId-with-custom-dispatcher-doesnt-preserve-ClientId
session.requestFocus.setSuspendPreserveClientId(handlerScheduler = Dispatchers.Default.asRdScheduler) { _, actionTitle ->
session.requestFocus.setSuspendPreserveClientId(handlerScheduler = Dispatchers.Default.asRdScheduler) { _, silent ->
withContext(Dispatchers.EDT + ModalityState.any().asContextElement()) {
requestFocus(actionTitle)
requestFocus(silent)
}
}
@@ -361,81 +363,81 @@ open class DistributedTestHost(coroutineScope: CoroutineScope) {
}
private suspend fun requestFocus(actionTitle: String): Boolean {
LOG.info("$actionTitle: Requesting focus")
private suspend fun requestFocus(silent: Boolean): Boolean {
LOG.info("Requesting focus")
val projects = ProjectManagerEx.getOpenProjects()
if (projects.size > 1) {
LOG.info("'$actionTitle': Can't choose a project to focus. All projects: ${projects.joinToString(", ")}")
LOG.info("Can't choose a project to focus. All projects: ${projects.joinToString(", ")}")
return false
}
val currentProject = projects.singleOrNull()
return if (currentProject == null) {
requestFocusNoProject(actionTitle)
requestFocusNoProject(silent)
}
else {
requestFocusWithProject(currentProject, actionTitle)
requestFocusWithProjectIfNeeded(currentProject, silent)
}
}
private suspend fun requestFocusWithProject(project: Project, actionTitle: String): Boolean {
private suspend fun requestFocusWithProjectIfNeeded(project: Project, silent: Boolean): Boolean {
val projectIdeFrame = WindowManager.getInstance().getFrame(project)
if (projectIdeFrame == null) {
LOG.info("$actionTitle: No frame yet, nothing to focus")
LOG.info("No frame yet, nothing to focus")
return false
}
else {
val windowString = "window '${projectIdeFrame.name}'"
if (SystemInfo.isWindows) {
return requestFocusWithProjectOnWindows(projectIdeFrame, project, windowString)
}
val frameName = "frame '${projectIdeFrame.name}'"
if (projectIdeFrame.isFocused) {
LOG.info("$actionTitle: Window '$windowString' is already focused")
return true
return if ((projectIdeFrame.isFocusAncestor() || projectIdeFrame.isFocused) && !SystemInfo.isWindows) {
LOG.info("Frame '$frameName' is already focused")
true
}
else {
LOG.info("$actionTitle: Requesting project focus for '$windowString'")
ProjectUtil.focusProjectWindow(project, true)
val waitResult = waitFor(timeout = 5.seconds.toJavaDuration()) {
projectIdeFrame.isFocused
}
if (!waitResult) {
LOG.error("Couldn't wait for focus in project '$windowString'")
}
return waitResult
requestFocusWithProject(projectIdeFrame, project, frameName, silent)
}
}
}
private suspend fun requestFocusWithProjectOnWindows(projectIdeFrame: JFrame, project: Project, windowString: String): Boolean {
private suspend fun requestFocusWithProject(projectIdeFrame: JFrame, project: Project, frameName: String, silent: Boolean): Boolean {
LOG.info("Requesting project focus for '$frameName'")
AppIcon.getInstance().requestFocus(projectIdeFrame)
ProjectUtil.focusProjectWindow(project)
val waitResult = waitFor(timeout = 5.seconds.toJavaDuration()) { projectIdeFrame.isFocused }
if (!waitResult) {
LOG.error("Couldn't wait for focus in project '$windowString'")
ProjectUtil.focusProjectWindow(project, stealFocusIfAppInactive = true)
return waitFor(timeout = 5.seconds.toJavaDuration()) {
projectIdeFrame.isFocusAncestor() || projectIdeFrame.isFocused
}.also {
if (!it && !silent) {
val keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager()
LOG.error("Couldn't wait for focus in project '$frameName'," +
"component isFocused=" + projectIdeFrame.isFocused + " isFocusAncestor=" + projectIdeFrame.isFocusAncestor() +
"\nActual focused component: " +
"\nfocusedWindow is " + keyboardFocusManager.focusedWindow +
"\nfocusOwner is " + keyboardFocusManager.focusOwner +
"\nactiveWindow is " + keyboardFocusManager.activeWindow +
"\npermanentFocusOwner is " + keyboardFocusManager.permanentFocusOwner)
}
}
return waitResult
}
private suspend fun requestFocusNoProject(actionTitle: String): Boolean {
private suspend fun requestFocusNoProject(silent: Boolean): Boolean {
val visibleWindows = Window.getWindows().filter { it.isShowing }
if (visibleWindows.size != 1) {
LOG.info("$actionTitle: There are multiple windows, will focus them all. All windows: ${visibleWindows.joinToString(", ")}")
if (visibleWindows.size > 1) {
LOG.info("There are multiple windows, will focus them all. All windows: ${visibleWindows.joinToString(", ")}")
}
return visibleWindows.map {
LOG.info("$actionTitle: Focusing window '$it'")
visibleWindows.forEach {
AppIcon.getInstance().requestFocus(it)
val waitResult = waitFor(timeout = 5.seconds.toJavaDuration()) {
it.isFocused
}
return waitFor(timeout = 5.seconds.toJavaDuration()) {
KeyboardFocusManager.getCurrentKeyboardFocusManager().focusOwner != null
}.also {
if (!it && !silent) {
LOG.error("Couldn't wait for focus in case there is no project")
}
if (!waitResult) {
LOG.error("Couldn't wait for focus in project '$it'")
}
waitResult
}.all { it }
}
}
private fun screenshotFile(actionName: String, suffix: String, timeStamp: LocalTime): File {