diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 45e4fc994..08e00c3ab 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -53,6 +53,7 @@ kakaoExtClicks = { module = "io.github.kakaocup:kakao-ext-clicks", version.ref = junit = "junit:junit:4.13.2" junitJupiter = "org.junit.jupiter:junit-jupiter:5.9.0" truth = "com.google.truth:truth:1.3.0" +mockk = "io.mockk:mockk:1.13.12" androidXTestCore = { module = "androidx.test:core", version.ref = "androidXTest" } androidXTestRules = { module = "androidx.test:rules", version.ref = "androidXTest" } diff --git a/kaspresso/build.gradle.kts b/kaspresso/build.gradle.kts index a52cfc7ec..36bd66020 100644 --- a/kaspresso/build.gradle.kts +++ b/kaspresso/build.gradle.kts @@ -30,4 +30,5 @@ dependencies { testImplementation(libs.junit) testImplementation(libs.truth) + testImplementation(libs.mockk) } 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..ecf94d6a9 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 @@ -13,25 +13,28 @@ import com.kaspersky.kaspresso.interceptors.behaviorkautomator.impl.flakysafety. import com.kaspersky.kaspresso.interceptors.tolibrary.KakaoLibraryInjector.injectKaspressoInKakao import com.kaspersky.kaspresso.interceptors.tolibrary.KakaoLibraryInjector.injectKaspressoInKautomator import com.kaspersky.kaspresso.kaspresso.Kaspresso +import java.util.concurrent.atomic.AtomicInteger /** * The special class that removes all interceptors related to FlakySafety from Kautomator settings * and restore them by demand */ internal class FlakySafeInterceptorScalpel( - private val kaspresso: Kaspresso -) { - + private val kaspresso: Kaspresso, private val scalpelSwitcher: ScalpelSwitcher = ScalpelSwitcher() +) { + private val entriesCount = AtomicInteger() fun scalpFromLibs() { - scalpelSwitcher.attemptTakeScalp( - actionToDetermineScalp = { determineScalpExistingInKaspresso() }, - actionToTakeScalp = { - scalpKakaoInterceptors() - scalpKautomatorInterceptors() - } - ) + if (entriesCount.getAndIncrement() == 0) { + scalpelSwitcher.attemptTakeScalp( + actionToDetermineScalp = { determineScalpExistingInKaspresso() }, + actionToTakeScalp = { + scalpKakaoInterceptors() + scalpKautomatorInterceptors() + } + ) + } } private fun determineScalpExistingInKaspresso() = @@ -86,24 +89,27 @@ internal class FlakySafeInterceptorScalpel( } fun restoreScalpToLibs() { - scalpelSwitcher.attemptRestoreScalp { - injectKaspressoInKakao( - kaspresso.viewBehaviorInterceptors, - kaspresso.dataBehaviorInterceptors, - kaspresso.webBehaviorInterceptors, - kaspresso.viewActionWatcherInterceptors, - kaspresso.viewAssertionWatcherInterceptors, - kaspresso.atomWatcherInterceptors, - kaspresso.webAssertionWatcherInterceptors, - kaspresso.params.clickParams - ) + val nestingDepth = entriesCount.decrementAndGet() + if (nestingDepth <= 0) { // prevent restoring the interceptors in case if a "flakySafely" block is nested in an another "flakySafely" + scalpelSwitcher.attemptRestoreScalp { + injectKaspressoInKakao( + kaspresso.viewBehaviorInterceptors, + kaspresso.dataBehaviorInterceptors, + kaspresso.webBehaviorInterceptors, + kaspresso.viewActionWatcherInterceptors, + kaspresso.viewAssertionWatcherInterceptors, + kaspresso.atomWatcherInterceptors, + kaspresso.webAssertionWatcherInterceptors, + kaspresso.params.clickParams + ) - injectKaspressoInKautomator( - kaspresso.objectBehaviorInterceptors, - kaspresso.deviceBehaviorInterceptors, - kaspresso.objectWatcherInterceptors, - kaspresso.deviceWatcherInterceptors - ) + injectKaspressoInKautomator( + kaspresso.objectBehaviorInterceptors, + kaspresso.deviceBehaviorInterceptors, + kaspresso.objectWatcherInterceptors, + kaspresso.deviceWatcherInterceptors + ) + } } } } diff --git a/kaspresso/src/test/java/com/kaspersky/kaspresso/flakysafety/scalpel/FlakySafeInterceptorScalpelTest.kt b/kaspresso/src/test/java/com/kaspersky/kaspresso/flakysafety/scalpel/FlakySafeInterceptorScalpelTest.kt new file mode 100644 index 000000000..a50a23ca0 --- /dev/null +++ b/kaspresso/src/test/java/com/kaspersky/kaspresso/flakysafety/scalpel/FlakySafeInterceptorScalpelTest.kt @@ -0,0 +1,36 @@ +package com.kaspersky.kaspresso.flakysafety.scalpel + +import com.kaspersky.kaspresso.kaspresso.Kaspresso +import io.mockk.mockk +import io.mockk.verify +import org.junit.Test + +class FlakySafeInterceptorScalpelTest { + + private val kaspresso = mockk(relaxed = true) + private val scalpelSwitcher = mockk(relaxed = true) + private val scalpel = FlakySafeInterceptorScalpel(kaspresso, scalpelSwitcher) + @Test + fun `GIVEN nested flaky safety WHEN trying to scalp flaky safety interceptors THEN should not scalp interceptors`() { + scalpel.scalpFromLibs() + scalpel.scalpFromLibs() + scalpel.scalpFromLibs() + + verify(exactly = 1) { scalpelSwitcher.attemptTakeScalp(any(), any()) } + } + + @Test + fun `GIVEN nested flaky safety WHEN trying to restore flaky safety interceptors THEN should not restore interceptors`() { + scalpel.scalpFromLibs() + scalpel.scalpFromLibs() + scalpel.scalpFromLibs() + + scalpel.restoreScalpToLibs() + scalpel.restoreScalpToLibs() + + verify(exactly = 0) { scalpelSwitcher.attemptRestoreScalp(any()) } + + scalpel.restoreScalpToLibs() + verify { scalpelSwitcher.attemptRestoreScalp(any()) } + } +}