mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
Support for more pods + tests
This commit is contained in:
+4
-59
@@ -1,70 +1,15 @@
|
||||
package eu.darken.capod.pods.core.airpods
|
||||
package eu.darken.capod.pods.core.apple
|
||||
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.bluetooth.le.ScanRecord
|
||||
import android.bluetooth.le.ScanResult
|
||||
import eu.darken.capod.pods.core.DualPods
|
||||
import eu.darken.capod.pods.core.PodDevice
|
||||
import eu.darken.capod.pods.core.airpods.models.AirPodsGen1
|
||||
import eu.darken.capod.pods.core.airpods.models.AirPodsPro
|
||||
import eu.darken.capod.pods.core.airpods.models.UnknownAppleDevice
|
||||
import eu.darken.capod.pods.core.airpods.protocol.ContinuityProtocol
|
||||
import eu.darken.capod.pods.core.airpods.protocol.ProximityPairing
|
||||
import eu.darken.capod.pods.core.apple.airpods.AirPodsGen1
|
||||
import eu.darken.capod.pods.core.apple.airpods.AirPodsPro
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.types.instanceOf
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import testhelper.BaseTest
|
||||
|
||||
class AirPodsFactoryTest : BaseTest() {
|
||||
|
||||
@MockK lateinit var scanResult: ScanResult
|
||||
@MockK lateinit var scanRecord: ScanRecord
|
||||
@MockK lateinit var device: BluetoothDevice
|
||||
|
||||
val factory = AirPodsFactory(
|
||||
proximityPairingDecoder = ProximityPairing.Decoder(),
|
||||
continuityProtocolDecoder = ContinuityProtocol.Decoder(),
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
MockKAnnotations.init(this)
|
||||
every { scanResult.scanRecord } returns scanRecord
|
||||
every { scanResult.rssi } returns -66
|
||||
every { scanResult.timestampNanos } returns 136136027721826
|
||||
every { scanResult.device } returns device
|
||||
every { device.address } returns "77:49:4C:D8:25:0C"
|
||||
}
|
||||
|
||||
private suspend inline fun <reified T : PodDevice?> create(hex: String, block: T.() -> Unit) {
|
||||
val trimmed = hex
|
||||
.replace(" ", "")
|
||||
.replace(">", "")
|
||||
.replace("<", "")
|
||||
require(trimmed.length % 2 == 0) { "Not a HEX string" }
|
||||
val bytes = trimmed.chunked(2).map { it.toInt(16).toByte() }.toByteArray()
|
||||
mockData(bytes)
|
||||
block.invoke(factory.create(scanResult) as T)
|
||||
}
|
||||
|
||||
private fun mockData(hex: String) {
|
||||
val trimmed = hex
|
||||
.replace(" ", "")
|
||||
.replace(">", "")
|
||||
.replace("<", "")
|
||||
require(trimmed.length % 2 == 0) { "Not a HEX string" }
|
||||
val bytes = trimmed.chunked(2).map { it.toInt(16).toByte() }.toByteArray()
|
||||
return mockData(bytes)
|
||||
}
|
||||
|
||||
private fun mockData(data: ByteArray) {
|
||||
every { scanRecord.getManufacturerSpecificData(ContinuityProtocol.APPLE_COMPANY_IDENTIFIER) } returns data
|
||||
}
|
||||
class AirPodsFactoryTest : BaseAirPodsTest() {
|
||||
|
||||
@Test
|
||||
fun `test AirPodDevice - active microphone`() = runBlockingTest {
|
||||
+9
-4
@@ -1,14 +1,16 @@
|
||||
package eu.darken.capod.pods.core.airpods
|
||||
package eu.darken.capod.pods.core.apple
|
||||
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.bluetooth.le.ScanRecord
|
||||
import android.bluetooth.le.ScanResult
|
||||
import eu.darken.capod.common.SystemClockWrap
|
||||
import eu.darken.capod.pods.core.PodDevice
|
||||
import eu.darken.capod.pods.core.airpods.protocol.ContinuityProtocol
|
||||
import eu.darken.capod.pods.core.airpods.protocol.ProximityPairing
|
||||
import eu.darken.capod.pods.core.apple.protocol.ContinuityProtocol
|
||||
import eu.darken.capod.pods.core.apple.protocol.ProximityPairing
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.mockkObject
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import testhelper.BaseTest
|
||||
|
||||
@@ -18,7 +20,7 @@ abstract class BaseAirPodsTest : BaseTest() {
|
||||
@MockK lateinit var scanRecord: ScanRecord
|
||||
@MockK lateinit var device: BluetoothDevice
|
||||
|
||||
val factory = AirPodsFactory(
|
||||
val factory = AppleFactory(
|
||||
proximityPairingDecoder = ProximityPairing.Decoder(),
|
||||
continuityProtocolDecoder = ContinuityProtocol.Decoder(),
|
||||
)
|
||||
@@ -31,6 +33,9 @@ abstract class BaseAirPodsTest : BaseTest() {
|
||||
every { scanResult.timestampNanos } returns 136136027721826
|
||||
every { scanResult.device } returns device
|
||||
every { device.address } returns "77:49:4C:D8:25:0C"
|
||||
|
||||
mockkObject(SystemClockWrap)
|
||||
every { SystemClockWrap.elapsedRealtimeNanos } returns 1000L
|
||||
}
|
||||
|
||||
suspend inline fun <reified T : PodDevice?> create(hex: String, block: T.() -> Unit) {
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package eu.darken.capod.pods.core.airpods.models
|
||||
package eu.darken.capod.pods.core.apple.airpods
|
||||
|
||||
import eu.darken.capod.pods.core.airpods.BaseAirPodsTest
|
||||
import eu.darken.capod.pods.core.airpods.DualApplePods
|
||||
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
|
||||
import eu.darken.capod.pods.core.apple.DualApplePods
|
||||
import io.kotest.matchers.shouldBe
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.junit.jupiter.api.Test
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package eu.darken.capod.pods.core.airpods.models
|
||||
package eu.darken.capod.pods.core.apple.airpods
|
||||
|
||||
import eu.darken.capod.pods.core.airpods.BaseAirPodsTest
|
||||
import eu.darken.capod.pods.core.airpods.DualApplePods
|
||||
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
|
||||
import eu.darken.capod.pods.core.apple.DualApplePods
|
||||
import io.kotest.matchers.shouldBe
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -0,0 +1,55 @@
|
||||
package eu.darken.capod.pods.core.apple.airpods
|
||||
|
||||
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
|
||||
import io.kotest.matchers.shouldBe
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AirPodsMaxTest : BaseAirPodsTest() {
|
||||
|
||||
// Test data from https://github.com/adolfintel/OpenPods/issues/124
|
||||
@Test
|
||||
fun `default AirPods Max`() = runBlockingTest {
|
||||
create<AirPodsMax>("07 19 01 0A 20 62 04 80 01 0F 40 0D 70 50 16 F2 40 83 16 BF 10 16 34 9B 74 84 E8") {
|
||||
rawPrefix shouldBe 0x01.toUByte()
|
||||
rawDeviceModel shouldBe 0x0A20.toUShort()
|
||||
rawStatus shouldBe 0x62.toUByte()
|
||||
rawPodsBattery shouldBe 0x04.toUByte()
|
||||
rawCaseBattery shouldBe 0x80.toUByte()
|
||||
rawCaseLidState shouldBe 0x01.toUByte()
|
||||
rawDeviceColor shouldBe 0x0F.toUByte()
|
||||
rawSuffix shouldBe 0x40.toUByte()
|
||||
|
||||
batteryHeadsetPercent shouldBe 0.4f
|
||||
|
||||
isCaseCharging shouldBe false
|
||||
isHeadsetBeingCharged shouldBe false
|
||||
|
||||
isHeadsetBeingWorn shouldBe true
|
||||
batteryCasePercent shouldBe 0.0f
|
||||
}
|
||||
}
|
||||
|
||||
// Test data from https://github.com/adolfintel/OpenPods/issues/124
|
||||
@Test
|
||||
fun `default AirPods Max flipped values`() = runBlockingTest {
|
||||
create<AirPodsMax>("07 19 01 0A 20 02 05 80 04 0F 44 A7 60 9B F8 3C FD B1 D8 1C 61 EA 82 60 A3 2C 4E") {
|
||||
rawPrefix shouldBe 0x01.toUByte()
|
||||
rawDeviceModel shouldBe 0x0A20.toUShort()
|
||||
rawStatus shouldBe 0x02.toUByte()
|
||||
rawPodsBattery shouldBe 0x05.toUByte()
|
||||
rawCaseBattery shouldBe 0x80.toUByte()
|
||||
rawCaseLidState shouldBe 0x04.toUByte()
|
||||
rawDeviceColor shouldBe 0x0F.toUByte()
|
||||
rawSuffix shouldBe 0x44.toUByte()
|
||||
|
||||
batteryHeadsetPercent shouldBe 0.5f
|
||||
|
||||
isCaseCharging shouldBe false
|
||||
isHeadsetBeingCharged shouldBe false
|
||||
|
||||
isHeadsetBeingWorn shouldBe true
|
||||
batteryCasePercent shouldBe 0.0f
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
package eu.darken.capod.pods.core.airpods.models
|
||||
package eu.darken.capod.pods.core.apple.airpods
|
||||
|
||||
import eu.darken.capod.pods.core.DualPods
|
||||
import eu.darken.capod.pods.core.airpods.BaseAirPodsTest
|
||||
import eu.darken.capod.pods.core.airpods.DualApplePods
|
||||
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
|
||||
import eu.darken.capod.pods.core.apple.DualApplePods
|
||||
import io.kotest.matchers.shouldBe
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -0,0 +1,26 @@
|
||||
package eu.darken.capod.pods.core.apple.beats
|
||||
|
||||
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
|
||||
import io.kotest.matchers.shouldBe
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class BeatsFlexText : BaseAirPodsTest() {
|
||||
|
||||
// Raw data from https://github.com/adolfintel/OpenPods/issues/105
|
||||
@Test
|
||||
fun `default BeatsFlex`() = runBlockingTest {
|
||||
create<BeatsFlex>("07 19 01 10 20 0A F4 8F 00 01 00 C4 71 9F 9C EF A2 E3 BA 66 FE 1D 45 9F C9 2F A0") {
|
||||
rawPrefix shouldBe 0x01.toUByte()
|
||||
rawDeviceModel shouldBe 0x1020.toUShort()
|
||||
rawStatus shouldBe 0x0A.toUByte()
|
||||
rawPodsBattery shouldBe 0xF4.toUByte()
|
||||
rawCaseBattery shouldBe 0x8F.toUByte()
|
||||
rawCaseLidState shouldBe 0x00.toUByte()
|
||||
rawDeviceColor shouldBe 0x01.toUByte()
|
||||
rawSuffix shouldBe 0x00.toUByte()
|
||||
|
||||
batteryHeadsetPercent shouldBe 0.4f
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package eu.darken.capod.pods.core.apple.beats
|
||||
|
||||
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
|
||||
import io.kotest.matchers.shouldBe
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class BeatsSolo3Test : BaseAirPodsTest() {
|
||||
|
||||
// TODO This is handcrafted data, get actual data for tests
|
||||
@Test
|
||||
fun `default BeatsSolo3`() = runBlockingTest {
|
||||
create<BeatsSolo3>("07 19 01 06 20 62 04 80 01 0F 40 0D 70 50 16 F2 40 83 16 BF 10 16 34 9B 74 84 E8") {
|
||||
rawPrefix shouldBe 0x01.toUByte()
|
||||
rawDeviceModel shouldBe 0x0620.toUShort()
|
||||
rawStatus shouldBe 0x62.toUByte()
|
||||
rawPodsBattery shouldBe 0x04.toUByte()
|
||||
rawCaseBattery shouldBe 0x80.toUByte()
|
||||
rawCaseLidState shouldBe 0x01.toUByte()
|
||||
rawDeviceColor shouldBe 0x0F.toUByte()
|
||||
rawSuffix shouldBe 0x40.toUByte()
|
||||
|
||||
batteryHeadsetPercent shouldBe 0.4f
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package eu.darken.capod.pods.core.apple.beats
|
||||
|
||||
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
|
||||
import io.kotest.matchers.shouldBe
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class BeatsStudio3Test : BaseAirPodsTest() {
|
||||
|
||||
// TODO This is handcrafted data, get actual data for tests
|
||||
@Test
|
||||
fun `default BeatsStudio3`() = runBlockingTest {
|
||||
create<BeatsStudio3>("07 19 01 09 20 62 04 80 01 0F 40 0D 70 50 16 F2 40 83 16 BF 10 16 34 9B 74 84 E8") {
|
||||
rawPrefix shouldBe 0x01.toUByte()
|
||||
rawDeviceModel shouldBe 0x0920.toUShort()
|
||||
rawStatus shouldBe 0x62.toUByte()
|
||||
rawPodsBattery shouldBe 0x04.toUByte()
|
||||
rawCaseBattery shouldBe 0x80.toUByte()
|
||||
rawCaseLidState shouldBe 0x01.toUByte()
|
||||
rawDeviceColor shouldBe 0x0F.toUByte()
|
||||
rawSuffix shouldBe 0x40.toUByte()
|
||||
|
||||
batteryHeadsetPercent shouldBe 0.4f
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package eu.darken.capod.pods.core.apple.beats
|
||||
|
||||
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
|
||||
import io.kotest.matchers.shouldBe
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class BeatsXTest : BaseAirPodsTest() {
|
||||
|
||||
// Raw data from https://github.com/adolfintel/OpenPods/issues/105
|
||||
@Test
|
||||
fun `default BeatsX`() = runBlockingTest {
|
||||
create<BeatsX>("07 19 01 05 20 00 F5 0F 01 01 00 6D CE C0 04 22 0A 85 31 2D 82 6B 42 80 01 20 1A") {
|
||||
rawPrefix shouldBe 0x01.toUByte()
|
||||
rawDeviceModel shouldBe 0x0520.toUShort()
|
||||
rawStatus shouldBe 0x00.toUByte()
|
||||
rawPodsBattery shouldBe 0xF5.toUByte()
|
||||
rawCaseBattery shouldBe 0x0F.toUByte()
|
||||
rawCaseLidState shouldBe 0x01.toUByte()
|
||||
rawDeviceColor shouldBe 0x01.toUByte()
|
||||
rawSuffix shouldBe 0x00.toUByte()
|
||||
|
||||
batteryHeadsetPercent shouldBe 0.5f
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package eu.darken.capod.pods.core.apple.beats
|
||||
|
||||
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
|
||||
import io.kotest.matchers.shouldBe
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class PowerBeats3Test : BaseAirPodsTest() {
|
||||
|
||||
// TODO This is handcrafted data, get actual data for tests
|
||||
@Test
|
||||
fun `default PowerBeats3`() = runBlockingTest {
|
||||
create<PowerBeats3>("07 19 01 03 20 62 04 80 01 0F 40 0D 70 50 16 F2 40 83 16 BF 10 16 34 9B 74 84 E8") {
|
||||
rawPrefix shouldBe 0x01.toUByte()
|
||||
rawDeviceModel shouldBe 0x0320.toUShort()
|
||||
rawStatus shouldBe 0x62.toUByte()
|
||||
rawPodsBattery shouldBe 0x04.toUByte()
|
||||
rawCaseBattery shouldBe 0x80.toUByte()
|
||||
rawCaseLidState shouldBe 0x01.toUByte()
|
||||
rawDeviceColor shouldBe 0x0F.toUByte()
|
||||
rawSuffix shouldBe 0x40.toUByte()
|
||||
|
||||
batteryHeadsetPercent shouldBe 0.4f
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package eu.darken.capod.pods.core.apple.beats
|
||||
|
||||
import eu.darken.capod.pods.core.DualPods
|
||||
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
|
||||
import eu.darken.capod.pods.core.apple.DualApplePods
|
||||
import io.kotest.matchers.shouldBe
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class PowerBeatsProTest : BaseAirPodsTest() {
|
||||
|
||||
// TODO This is handcrafted data, get actual data for tests
|
||||
@Test
|
||||
fun `test PowerBeatsPro`() = runBlockingTest {
|
||||
create<PowerBeatsPro>("07 19 01 0B 20 54 AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") {
|
||||
rawPrefix shouldBe 0x01.toUByte()
|
||||
rawDeviceModel shouldBe 0x0B20.toUShort()
|
||||
rawStatus shouldBe 0x54.toUByte()
|
||||
rawPodsBattery shouldBe 0xAA.toUByte()
|
||||
rawCaseBattery shouldBe 0xB5.toUByte()
|
||||
rawCaseLidState shouldBe 0x31.toUByte()
|
||||
rawDeviceColor shouldBe 0x00.toUByte()
|
||||
rawSuffix shouldBe 0x00.toUByte()
|
||||
|
||||
microPhonePod shouldBe DualPods.Pod.RIGHT
|
||||
|
||||
batteryLeftPodPercent shouldBe 1.0f
|
||||
batteryRightPodPercent shouldBe 1.0f
|
||||
|
||||
isCaseCharging shouldBe false
|
||||
isRightPodCharging shouldBe true
|
||||
isLeftPodCharging shouldBe true
|
||||
batteryCasePercent shouldBe 0.5f
|
||||
|
||||
deviceColor shouldBe DualApplePods.DeviceColor.WHITE
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user