From 15d01853cfa49b69d66097c1da2f9a8d33b4ee77 Mon Sep 17 00:00:00 2001 From: Nikita Evdokimov Date: Thu, 29 Aug 2024 17:14:11 +0300 Subject: [PATCH 1/4] ISSUE-646: Clean semantics flaky safety interceptor --- .../composesupport/config/ComposeConfig.kt | 4 +++ .../flakysafety/ComposeFlakySafetyScalper.kt | 25 ++++++++++++++++ .../scalpel/FlakySafeInterceptorScalpel.kt | 17 ++++++----- .../external/ExternalFlakySafetyScalper.kt | 7 +++++ .../ExternalFlakySafetyScalperNotifier.kt | 29 +++++++++++++++++++ .../kaspresso/kaspresso/Kaspresso.kt | 10 ++++++- 6 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 compose-support/src/main/java/com/kaspersky/components/composesupport/flakysafety/ComposeFlakySafetyScalper.kt create mode 100644 kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/external/ExternalFlakySafetyScalper.kt create mode 100644 kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/external/ExternalFlakySafetyScalperNotifier.kt diff --git a/compose-support/src/main/java/com/kaspersky/components/composesupport/config/ComposeConfig.kt b/compose-support/src/main/java/com/kaspersky/components/composesupport/config/ComposeConfig.kt index 5134f7fd3..48fbdcf9e 100644 --- a/compose-support/src/main/java/com/kaspersky/components/composesupport/config/ComposeConfig.kt +++ b/compose-support/src/main/java/com/kaspersky/components/composesupport/config/ComposeConfig.kt @@ -1,5 +1,6 @@ package com.kaspersky.components.composesupport.config +import com.kaspersky.components.composesupport.flakysafety.ComposeFlakySafetyScalper import com.kaspersky.components.composesupport.interceptors.behavior.SemanticsBehaviorInterceptor import com.kaspersky.components.composesupport.interceptors.behavior.impl.autoscroll.AutoScrollSemanticsBehaviorInterceptor import com.kaspersky.components.composesupport.interceptors.behavior.impl.elementloader.ElementLoaderSemanticsBehaviorInterceptor @@ -51,6 +52,9 @@ class ComposeConfig { FlakySafeSemanticsBehaviorInterceptor(flakySafetyParams, libLogger) ) } + + val composeFlakySafetyScalper = ComposeFlakySafetyScalper(semanticsBehaviorInterceptors, semanticsWatcherInterceptors) + externalFlakySafetyScalperNotifier.addScalper(composeFlakySafetyScalper) } fun build() { diff --git a/compose-support/src/main/java/com/kaspersky/components/composesupport/flakysafety/ComposeFlakySafetyScalper.kt b/compose-support/src/main/java/com/kaspersky/components/composesupport/flakysafety/ComposeFlakySafetyScalper.kt new file mode 100644 index 000000000..12c15f291 --- /dev/null +++ b/compose-support/src/main/java/com/kaspersky/components/composesupport/flakysafety/ComposeFlakySafetyScalper.kt @@ -0,0 +1,25 @@ +package com.kaspersky.components.composesupport.flakysafety + +import com.kaspersky.components.composesupport.config.ComposeInterceptorsInjector +import com.kaspersky.components.composesupport.interceptors.behavior.SemanticsBehaviorInterceptor +import com.kaspersky.components.composesupport.interceptors.behavior.impl.flakysafety.FlakySafeSemanticsBehaviorInterceptor +import com.kaspersky.components.composesupport.interceptors.watcher.SemanticsWatcherInterceptor +import com.kaspersky.kaspresso.flakysafety.scalpel.external.ExternalFlakySafetyScalper + +class ComposeFlakySafetyScalper( + private val semanticsBehaviorInterceptors: MutableList, + private val semanticsWatcherInterceptors: MutableList, +) : ExternalFlakySafetyScalper { + override fun isFlakySafetyInterceptorPresent(): Boolean { + return semanticsBehaviorInterceptors.any { it is FlakySafeSemanticsBehaviorInterceptor } + } + + override fun scalpFlakySafety() { + val interceptors = semanticsBehaviorInterceptors.filter { it !is FlakySafeSemanticsBehaviorInterceptor } + ComposeInterceptorsInjector.injectKaspressoInKakaoCompose(interceptors, semanticsWatcherInterceptors) + } + + override fun restoreFlakySafety() { + ComposeInterceptorsInjector.injectKaspressoInKakaoCompose(semanticsBehaviorInterceptors, semanticsWatcherInterceptors) + } +} diff --git a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/FlakySafeInterceptorScalpel.kt b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/FlakySafeInterceptorScalpel.kt index 5648f188f..f10e97394 100644 --- a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/FlakySafeInterceptorScalpel.kt +++ b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/FlakySafeInterceptorScalpel.kt @@ -10,8 +10,7 @@ import com.kaspersky.kaspresso.interceptors.behaviorkautomator.DeviceBehaviorInt import com.kaspersky.kaspresso.interceptors.behaviorkautomator.ObjectBehaviorInterceptor import com.kaspersky.kaspresso.interceptors.behaviorkautomator.impl.flakysafety.FlakySafeDeviceBehaviorInterceptor import com.kaspersky.kaspresso.interceptors.behaviorkautomator.impl.flakysafety.FlakySafeObjectBehaviorInterceptor -import com.kaspersky.kaspresso.interceptors.tolibrary.KakaoLibraryInjector.injectKaspressoInKakao -import com.kaspersky.kaspresso.interceptors.tolibrary.KakaoLibraryInjector.injectKaspressoInKautomator +import com.kaspersky.kaspresso.interceptors.tolibrary.KakaoLibraryInjector import com.kaspersky.kaspresso.kaspresso.Kaspresso /** @@ -30,6 +29,7 @@ internal class FlakySafeInterceptorScalpel( actionToTakeScalp = { scalpKakaoInterceptors() scalpKautomatorInterceptors() + kaspresso.externalFlakySafetyScalperNotifier.scalpFlakySafety() } ) } @@ -39,7 +39,8 @@ internal class FlakySafeInterceptorScalpel( kaspresso.dataBehaviorInterceptors.filterIsInstance().isNotEmpty() || kaspresso.webBehaviorInterceptors.filterIsInstance().isNotEmpty() || kaspresso.objectBehaviorInterceptors.filterIsInstance().isNotEmpty() || - kaspresso.deviceBehaviorInterceptors.filterIsInstance().isNotEmpty() + kaspresso.deviceBehaviorInterceptors.filterIsInstance().isNotEmpty() || + kaspresso.externalFlakySafetyScalperNotifier.isAnyExternalFlakySafetyInterceptorPresent() private fun scalpKakaoInterceptors() { val scalpedViewBehaviorInterceptors: List = @@ -55,7 +56,7 @@ internal class FlakySafeInterceptorScalpel( it !is FlakySafeWebBehaviorInterceptor } - injectKaspressoInKakao( + KakaoLibraryInjector.injectKaspressoInKakao( scalpedViewBehaviorInterceptors, scalpedDataBehaviorInterceptors, scalpedWebBehaviorInterceptors, @@ -77,7 +78,7 @@ internal class FlakySafeInterceptorScalpel( it !is FlakySafeDeviceBehaviorInterceptor } - injectKaspressoInKautomator( + KakaoLibraryInjector.injectKaspressoInKautomator( scalpedObjectBehaviorInterceptors, scalpedDeviceBehaviorInterceptors, kaspresso.objectWatcherInterceptors, @@ -87,7 +88,7 @@ internal class FlakySafeInterceptorScalpel( fun restoreScalpToLibs() { scalpelSwitcher.attemptRestoreScalp { - injectKaspressoInKakao( + KakaoLibraryInjector.injectKaspressoInKakao( kaspresso.viewBehaviorInterceptors, kaspresso.dataBehaviorInterceptors, kaspresso.webBehaviorInterceptors, @@ -98,12 +99,14 @@ internal class FlakySafeInterceptorScalpel( kaspresso.params.clickParams ) - injectKaspressoInKautomator( + KakaoLibraryInjector.injectKaspressoInKautomator( kaspresso.objectBehaviorInterceptors, kaspresso.deviceBehaviorInterceptors, kaspresso.objectWatcherInterceptors, kaspresso.deviceWatcherInterceptors ) + + kaspresso.externalFlakySafetyScalperNotifier.restoreFlakySafety() } } } diff --git a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/external/ExternalFlakySafetyScalper.kt b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/external/ExternalFlakySafetyScalper.kt new file mode 100644 index 000000000..b612c26c5 --- /dev/null +++ b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/external/ExternalFlakySafetyScalper.kt @@ -0,0 +1,7 @@ +package com.kaspersky.kaspresso.flakysafety.scalpel.external + +interface ExternalFlakySafetyScalper { + fun isFlakySafetyInterceptorPresent(): Boolean + fun scalpFlakySafety() + fun restoreFlakySafety() +} diff --git a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/external/ExternalFlakySafetyScalperNotifier.kt b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/external/ExternalFlakySafetyScalperNotifier.kt new file mode 100644 index 000000000..2f977d59f --- /dev/null +++ b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/external/ExternalFlakySafetyScalperNotifier.kt @@ -0,0 +1,29 @@ +package com.kaspersky.kaspresso.flakysafety.scalpel.external + +interface ExternalFlakySafetyScalperNotifier { + fun addScalper(scalper: ExternalFlakySafetyScalper) + + fun isAnyExternalFlakySafetyInterceptorPresent(): Boolean + fun scalpFlakySafety() + fun restoreFlakySafety() +} + +internal class ExternalFlakySafetyScalperNotifierImpl : ExternalFlakySafetyScalperNotifier { + private val scalpers = mutableListOf() + + override fun addScalper(scalper: ExternalFlakySafetyScalper) { + synchronized(this) { + scalpers.add(scalper) + } + } + + override fun isAnyExternalFlakySafetyInterceptorPresent(): Boolean = scalpers.any { it.isFlakySafetyInterceptorPresent() } + + override fun scalpFlakySafety() = scalpers.forEach { + it.scalpFlakySafety() + } + + override fun restoreFlakySafety() = scalpers.forEach { + it.restoreFlakySafety() + } +} diff --git a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/kaspresso/Kaspresso.kt b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/kaspresso/Kaspresso.kt index 9a17041db..dfa7baf48 100644 --- a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/kaspresso/Kaspresso.kt +++ b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/kaspresso/Kaspresso.kt @@ -64,6 +64,8 @@ import com.kaspersky.kaspresso.files.resources.impl.DefaultResourceFilesProvider import com.kaspersky.kaspresso.files.resources.impl.DefaultResourcesDirNameProvider import com.kaspersky.kaspresso.files.resources.impl.DefaultResourcesDirsProvider import com.kaspersky.kaspresso.files.resources.impl.DefaultResourcesRootDirsProvider +import com.kaspersky.kaspresso.flakysafety.scalpel.external.ExternalFlakySafetyScalperNotifier +import com.kaspersky.kaspresso.flakysafety.scalpel.external.ExternalFlakySafetyScalperNotifierImpl import com.kaspersky.kaspresso.idlewaiting.KautomatorWaitForIdleSettings import com.kaspersky.kaspresso.instrumental.InstrumentalDependencyProvider import com.kaspersky.kaspresso.instrumental.InstrumentalDependencyProviderFactory @@ -153,7 +155,8 @@ data class Kaspresso( internal val deviceBehaviorInterceptors: List, internal val stepWatcherInterceptors: List, internal val testRunWatcherInterceptors: List, - internal val resourceFilesProvider: ResourceFilesProvider + internal val resourceFilesProvider: ResourceFilesProvider, + internal val externalFlakySafetyScalperNotifier: ExternalFlakySafetyScalperNotifier ) { companion object { @@ -650,6 +653,8 @@ data class Kaspresso( */ lateinit var testRunWatcherInterceptors: MutableList + lateinit var externalFlakySafetyScalperNotifier: ExternalFlakySafetyScalperNotifier + /** * Holds the implementation of the [androidx.test.espresso.FailureHandler] interface, that is called on every * failure. @@ -926,6 +931,8 @@ data class Kaspresso( defaultsTestRunWatcherInterceptor ) + if (!::externalFlakySafetyScalperNotifier.isInitialized) externalFlakySafetyScalperNotifier = ExternalFlakySafetyScalperNotifierImpl() + if (artifactsPullParams.enabled) { instrumentalDependencyProviderFactory.getComponentProvider(instrumentation).runNotifier.addUniqueListener { ArtifactsPullRunListener(params = artifactsPullParams, files = files, logger = libLogger) @@ -1001,6 +1008,7 @@ data class Kaspresso( stepWatcherInterceptors = stepWatcherInterceptors, testRunWatcherInterceptors = testRunWatcherInterceptors, + externalFlakySafetyScalperNotifier = externalFlakySafetyScalperNotifier ) configurator.waitForIdleTimeout = kautomatorWaitForIdleSettings.waitForIdleTimeout From 02c9c814929fe13c3f82f4d51b5bc78e1245ca92 Mon Sep 17 00:00:00 2001 From: Nikita Evdokimov Date: Thu, 29 Aug 2024 17:20:39 +0300 Subject: [PATCH 2/4] ISSUE-646: Docs --- .../composesupport/flakysafety/ComposeFlakySafetyScalper.kt | 4 ++++ .../flakysafety/scalpel/FlakySafeInterceptorScalpel.kt | 2 +- .../kotlin/com/kaspersky/kaspresso/kaspresso/Kaspresso.kt | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/compose-support/src/main/java/com/kaspersky/components/composesupport/flakysafety/ComposeFlakySafetyScalper.kt b/compose-support/src/main/java/com/kaspersky/components/composesupport/flakysafety/ComposeFlakySafetyScalper.kt index 12c15f291..014a7efc4 100644 --- a/compose-support/src/main/java/com/kaspersky/components/composesupport/flakysafety/ComposeFlakySafetyScalper.kt +++ b/compose-support/src/main/java/com/kaspersky/components/composesupport/flakysafety/ComposeFlakySafetyScalper.kt @@ -6,6 +6,10 @@ import com.kaspersky.components.composesupport.interceptors.behavior.impl.flakys import com.kaspersky.components.composesupport.interceptors.watcher.SemanticsWatcherInterceptor import com.kaspersky.kaspresso.flakysafety.scalpel.external.ExternalFlakySafetyScalper +/** + * Removes and restores compose flaky safety interceptor so the `flakySafely` expression works correctly + * @see com.kaspersky.kaspresso.flakysafety.scalpel.FlakySafeInterceptorScalpel + */ class ComposeFlakySafetyScalper( private val semanticsBehaviorInterceptors: MutableList, private val semanticsWatcherInterceptors: MutableList, diff --git a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/FlakySafeInterceptorScalpel.kt b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/FlakySafeInterceptorScalpel.kt index f10e97394..bbfe65deb 100644 --- a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/FlakySafeInterceptorScalpel.kt +++ b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/FlakySafeInterceptorScalpel.kt @@ -14,7 +14,7 @@ import com.kaspersky.kaspresso.interceptors.tolibrary.KakaoLibraryInjector import com.kaspersky.kaspresso.kaspresso.Kaspresso /** - * The special class that removes all interceptors related to FlakySafety from Kautomator settings + * The special class that removes all interceptors related to FlakySafety from kakao settings * and restore them by demand */ internal class FlakySafeInterceptorScalpel( diff --git a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/kaspresso/Kaspresso.kt b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/kaspresso/Kaspresso.kt index dfa7baf48..9f3f0b11a 100644 --- a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/kaspresso/Kaspresso.kt +++ b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/kaspresso/Kaspresso.kt @@ -653,6 +653,11 @@ data class Kaspresso( */ lateinit var testRunWatcherInterceptors: MutableList + /** + * Holds a reference to the custom "external" flaky safety scalpers that are not set in the kaspresso by default + * @see com.kaspersky.kaspresso.flakysafety.scalpel.FlakySafeInterceptorScalpel + * @see com.kaspersky.kaspresso.flakysafety.scalpel.external.ExternalFlakySafetyScalper + */ lateinit var externalFlakySafetyScalperNotifier: ExternalFlakySafetyScalperNotifier /** From 360920b9fe59b99820fd61f45b47ea4b79efd355 Mon Sep 17 00:00:00 2001 From: Nikita Evdokimov Date: Fri, 22 Nov 2024 18:47:46 +0300 Subject: [PATCH 3/4] ISSUE-646: Add synchronized block --- .../scalpel/external/ExternalFlakySafetyScalperNotifier.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/external/ExternalFlakySafetyScalperNotifier.kt b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/external/ExternalFlakySafetyScalperNotifier.kt index 2f977d59f..967990a37 100644 --- a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/external/ExternalFlakySafetyScalperNotifier.kt +++ b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/flakysafety/scalpel/external/ExternalFlakySafetyScalperNotifier.kt @@ -17,7 +17,11 @@ internal class ExternalFlakySafetyScalperNotifierImpl : ExternalFlakySafetyScalp } } - override fun isAnyExternalFlakySafetyInterceptorPresent(): Boolean = scalpers.any { it.isFlakySafetyInterceptorPresent() } + override fun isAnyExternalFlakySafetyInterceptorPresent(): Boolean { + synchronized(this) { + return scalpers.any { it.isFlakySafetyInterceptorPresent() } + } + } override fun scalpFlakySafety() = scalpers.forEach { it.scalpFlakySafety() From eba181f9033993dfff9357e6d79df38f0d52b02f Mon Sep 17 00:00:00 2001 From: Nikita Evdokimov Date: Thu, 12 Dec 2024 14:23:27 +0300 Subject: [PATCH 4/4] ISSUE-646: Make ComposeFlakySafetyScalper internal --- .../composesupport/flakysafety/ComposeFlakySafetyScalper.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose-support/src/main/java/com/kaspersky/components/composesupport/flakysafety/ComposeFlakySafetyScalper.kt b/compose-support/src/main/java/com/kaspersky/components/composesupport/flakysafety/ComposeFlakySafetyScalper.kt index 014a7efc4..4ab7f4c49 100644 --- a/compose-support/src/main/java/com/kaspersky/components/composesupport/flakysafety/ComposeFlakySafetyScalper.kt +++ b/compose-support/src/main/java/com/kaspersky/components/composesupport/flakysafety/ComposeFlakySafetyScalper.kt @@ -10,7 +10,7 @@ import com.kaspersky.kaspresso.flakysafety.scalpel.external.ExternalFlakySafetyS * Removes and restores compose flaky safety interceptor so the `flakySafely` expression works correctly * @see com.kaspersky.kaspresso.flakysafety.scalpel.FlakySafeInterceptorScalpel */ -class ComposeFlakySafetyScalper( +internal class ComposeFlakySafetyScalper( private val semanticsBehaviorInterceptors: MutableList, private val semanticsWatcherInterceptors: MutableList, ) : ExternalFlakySafetyScalper {