-
Notifications
You must be signed in to change notification settings - Fork 153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TECH: added bluetooth's switcher #684
Open
ersanin
wants to merge
2
commits into
master
Choose a base branch
from
TECH-device-bluetooth-switch
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+255
−0
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
kaspresso/src/main/kotlin/com/kaspersky/kaspresso/device/bluetooth/Bluetooth.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package com.kaspersky.kaspresso.device.bluetooth | ||
|
||
/** | ||
* The interface to work with bluetooth settings. | ||
* | ||
* Required: Started AdbServer | ||
* 1. Download a file "kaspresso/artifacts/adbserver-desktop.jar" | ||
* 2. Start AdbServer => input in cmd "java jar path_to_file/adbserver-desktop.jar" | ||
* Methods demanding to use AdbServer in the default implementation of this interface are marked. | ||
* But nobody can't deprecate you to write implementation that doesn't require AdbServer. | ||
*/ | ||
interface Bluetooth { | ||
|
||
/** | ||
* Enables Bluetooth on the device using adb. | ||
*/ | ||
fun enable() | ||
|
||
/** | ||
* Disables Bluetooth on the device using adb. | ||
*/ | ||
fun disable() | ||
} |
137 changes: 137 additions & 0 deletions
137
kaspresso/src/main/kotlin/com/kaspersky/kaspresso/device/bluetooth/BluetoothImpl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package com.kaspersky.kaspresso.device.bluetooth | ||
|
||
import android.bluetooth.BluetoothAdapter | ||
import android.bluetooth.BluetoothManager | ||
import android.content.Context | ||
import com.kaspersky.components.kautomator.system.UiSystem | ||
import com.kaspersky.kaspresso.device.server.AdbServer | ||
import com.kaspersky.kaspresso.flakysafety.algorithm.FlakySafetyAlgorithm | ||
import com.kaspersky.kaspresso.internal.exceptions.AdbServerException | ||
import com.kaspersky.kaspresso.internal.systemscreen.NotificationsFullScreen | ||
|
||
import com.kaspersky.kaspresso.logger.UiTestLogger | ||
import com.kaspersky.kaspresso.params.FlakySafetyParams | ||
|
||
/** | ||
* The implementation of the [Bluetooth] interface. | ||
*/ | ||
class BluetoothImpl( | ||
private val logger: UiTestLogger, | ||
private val targetContext: Context, | ||
private val adbServer: AdbServer | ||
) : Bluetooth { | ||
|
||
companion object { | ||
private const val CMD_STATE_ENABLE = "enable" | ||
private const val CMD_STATE_DISABLE = "disable" | ||
private const val BLUETOOTH_STATE_CHANGE_CMD = "svc bluetooth" | ||
private const val BLUETOOTH_STATE_CHANGE_ROOT_CMD = "su 0 svc bluetooth" | ||
private const val BLUETOOTH_STATE_CHECK_CMD = "settings get global bluetooth_on" | ||
private const val BLUETOOTH_STATE_CHECK_RESULT_ENABLED = "1" | ||
private const val BLUETOOTH_STATE_CHECK_RESULT_DISABLED = "0" | ||
private val ADB_RESULT_REGEX = Regex("exitCode=(\\d+), message=(.+)") | ||
} | ||
|
||
private val flakySafetyAlgorithm = FlakySafetyAlgorithm(logger) | ||
private val flakySafetyParams: FlakySafetyParams | ||
get() = FlakySafetyParams( | ||
timeoutMs = 1000, | ||
intervalMs = 100, | ||
allowedExceptions = setOf(AdbServerException::class.java) | ||
) | ||
|
||
override fun enable() { | ||
logger.i("Enable bluetooth") | ||
toggleBluetooth(enable = true) | ||
} | ||
|
||
override fun disable() { | ||
logger.i("Disable bluetooth") | ||
toggleBluetooth(enable = false) | ||
} | ||
|
||
/** | ||
* Toggles Bluetooth state | ||
* Tries, first and foremost, to send ADB command. If this attempt fails, | ||
* opens Android Settings screen and tries to switch Bluetooth setting thumb. | ||
*/ | ||
private fun toggleBluetooth(enable: Boolean) { | ||
if (isBluetoothNotSupported()) { | ||
logger.i("Bluetooth is not supported") | ||
return | ||
} | ||
if (!changeBluetoothStateUsingAdbServer(enable, BLUETOOTH_STATE_CHANGE_ROOT_CMD) && | ||
!changeBluetoothStateUsingAdbServer(enable, BLUETOOTH_STATE_CHANGE_CMD) | ||
) { | ||
toggleBluetoothUsingAndroidSettings(enable) | ||
} | ||
if (isBluetoothEnabled()) { | ||
logger.i("Bluetooth enabled") | ||
} else { | ||
logger.i("Bluetooth disabled") | ||
} | ||
} | ||
|
||
/** | ||
* Tries to change Bluetooth state using AdbServer if it is available | ||
* @return true if Bluetooth state changed or false otherwise | ||
*/ | ||
private fun changeBluetoothStateUsingAdbServer(isEnabled: Boolean, changeCommand: String): Boolean = | ||
try { | ||
val (state, expectedResult) = when (isEnabled) { | ||
true -> CMD_STATE_ENABLE to BLUETOOTH_STATE_CHECK_RESULT_ENABLED | ||
false -> CMD_STATE_DISABLE to BLUETOOTH_STATE_CHECK_RESULT_DISABLED | ||
} | ||
adbServer.performShell("$changeCommand $state") | ||
flakySafetyAlgorithm.invokeFlakySafely(flakySafetyParams) { | ||
val result = adbServer.performShell(BLUETOOTH_STATE_CHECK_CMD) | ||
if (parseAdbResponse(result)?.trim() == expectedResult) true else | ||
throw AdbServerException("Failed to change Bluetooth state using ABD") | ||
} | ||
} catch (e: AdbServerException) { | ||
false | ||
} | ||
|
||
@Suppress("MagicNumber") | ||
private fun toggleBluetoothUsingAndroidSettings(enable: Boolean) { | ||
val height = targetContext.resources.displayMetrics.heightPixels | ||
val width = targetContext.resources.displayMetrics.widthPixels | ||
|
||
// Swipe down on the screen to open the Quick Access Menu | ||
UiSystem { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Plesae, leave a comment on what's going on here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
drag(width / 2, 0, width / 2, (height * 0.67).toInt(), 50) | ||
} | ||
// Swipe down again for more options. This is necessary because sometimes the bluetooth switch is located in additional settings | ||
UiSystem { | ||
drag(width / 2, 0, width / 2, (height * 0.67).toInt(), 50) | ||
} | ||
// Turn Bluetooth off or on via Quick Access Menu | ||
NotificationsFullScreen { | ||
bluetoothSwitch.setChecked(enable) | ||
} | ||
// Swipe up to close additional settings | ||
UiSystem { | ||
drag(width / 2, height, width / 2, 0, 50) | ||
} | ||
// Swipe up again to close Quick Access Menu | ||
UiSystem { | ||
drag(width / 2, height, width / 2, 0, 50) | ||
} | ||
} | ||
|
||
private fun isBluetoothNotSupported(): Boolean = | ||
getBluetoothAdapter() == null | ||
|
||
private fun isBluetoothEnabled(): Boolean = | ||
getBluetoothAdapter()?.isEnabled ?: false | ||
|
||
private fun getBluetoothAdapter(): BluetoothAdapter? = | ||
(this.targetContext.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager)?.adapter | ||
|
||
private fun parseAdbResponse(response: List<String>): String? { | ||
val result = response.firstOrNull()?.lineSequence()?.first() ?: return null | ||
val match = ADB_RESULT_REGEX.find(result) ?: return null | ||
val (_, message) = match.destructured | ||
return message | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
.../androidTest/kotlin/com/kaspersky/kaspressample/device_tests/DeviceBluetoothSampleTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package com.kaspersky.kaspressample.device_tests | ||
|
||
import android.bluetooth.BluetoothAdapter | ||
import android.bluetooth.BluetoothManager | ||
import android.content.Context | ||
import androidx.test.ext.junit.rules.activityScenarioRule | ||
import com.kaspersky.kaspressample.device.DeviceSampleActivity | ||
import com.kaspersky.kaspressample.utils.SafeAssert.assertFalseSafely | ||
import com.kaspersky.kaspressample.utils.SafeAssert.assertTrueSafely | ||
import com.kaspersky.kaspresso.device.Device | ||
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase | ||
import com.kaspersky.kaspresso.testcases.core.testcontext.BaseTestContext | ||
import org.junit.Rule | ||
import org.junit.Test | ||
|
||
class DeviceBluetoothSampleTest : TestCase() { | ||
|
||
@get:Rule | ||
val activityRule = activityScenarioRule<DeviceSampleActivity>() | ||
|
||
@Test | ||
fun bluetoothSampleTest() { | ||
before { | ||
tryToggleBluetooth(shouldEnable = true) | ||
}.after { | ||
tryToggleBluetooth(shouldEnable = true) | ||
}.run { | ||
|
||
step("Disable bluetooth") { | ||
tryToggleBluetooth(shouldEnable = false) | ||
checkBluetooth(shouldBeEnabled = false) | ||
} | ||
|
||
step("Enable bluetooth") { | ||
tryToggleBluetooth(shouldEnable = true) | ||
checkBluetooth(shouldBeEnabled = true) | ||
} | ||
} | ||
} | ||
|
||
private fun tryToggleBluetooth(shouldEnable: Boolean) { | ||
if (shouldEnable) { | ||
device.bluetooth.enable() | ||
} else { | ||
device.bluetooth.disable() | ||
} | ||
} | ||
|
||
private fun BaseTestContext.checkBluetooth(shouldBeEnabled: Boolean) { | ||
try { | ||
if (shouldBeEnabled) assertTrueSafely { isBluetoothEnabled() } else assertFalseSafely { isBluetoothEnabled() } | ||
} catch (assertionError: AssertionError) { | ||
if (isBluetoothNotSupported()) return | ||
else throw assertionError | ||
} | ||
} | ||
|
||
private fun isBluetoothNotSupported(): Boolean = | ||
device.getBluetoothAdapter() == null | ||
|
||
private fun isBluetoothEnabled(): Boolean = | ||
device.getBluetoothAdapter()?.isEnabled ?: false | ||
|
||
private fun Device.getBluetoothAdapter(): BluetoothAdapter? = | ||
(this.targetContext.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager)?.adapter | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Catching AdbServerException here may lead to the missing the fact that adb server is down or broken. Please, check that the test crashes when the adb server is disabled. I would throw an exception in the toggleBluetooth method if bluetooth state didn't change since we failed to set the test to the desired state
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why should the test fail if the adb server is disabled? In this case we can still try to switch bluetooth via quick access settings.