diff --git a/tests/org/kde/kdeconnect/DeviceTest.java b/tests/org/kde/kdeconnect/DeviceTest.java deleted file mode 100644 index a23867df..00000000 --- a/tests/org/kde/kdeconnect/DeviceTest.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015 Vineet Garg - * - * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL -*/ - -package org.kde.kdeconnect; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; - -import android.app.NotificationManager; -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import android.util.Base64; - -import androidx.core.content.ContextCompat; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.kde.kdeconnect.Backends.LanBackend.LanLink; -import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider; -import org.kde.kdeconnect.Helpers.DeviceHelper; -import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper; -import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper; -import org.mockito.MockedStatic; -import org.mockito.Mockito; - -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.util.Arrays; -import java.util.HashSet; - -public class DeviceTest { - - private Context context; - - MockedStatic mockBase64; - MockedStatic preferenceManager; - MockedStatic contextCompat; - - @After - public void tearDown() { - mockBase64.close(); - preferenceManager.close(); - contextCompat.close(); - } - - // Creating a paired device before each test case - @Before - public void setUp() { - // Save new test device in settings - - String deviceId = "testDevice"; - String name = "Test Device"; - String encodedCertificate = "MIIDVzCCAj+gAwIBAgIBCjANBgkqhkiG9w0BAQUFADBVMS8wLQYDVQQDDCZfZGExNzlhOTFfZjA2\n" + - "NF80NzhlX2JlOGNfMTkzNWQ3NTQ0ZDU0XzEMMAoGA1UECgwDS0RFMRQwEgYDVQQLDAtLZGUgY29u\n" + - "bmVjdDAeFw0xNTA2MDMxMzE0MzhaFw0yNTA2MDMxMzE0MzhaMFUxLzAtBgNVBAMMJl9kYTE3OWE5\n" + - "MV9mMDY0XzQ3OGVfYmU4Y18xOTM1ZDc1NDRkNTRfMQwwCgYDVQQKDANLREUxFDASBgNVBAsMC0tk\n" + - "ZSBjb25uZWN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzH9GxS1lctpwYdSGAoPH\n" + - "ws+MnVaL0PVDCuzrpxzXc+bChR87xofhQIesLPLZEcmUJ1MlEJ6jx4W+gVhvY2tUN7SoiKKbnq8s\n" + - "WjI5ovs5yML3C1zPbOSJAdK613FcdkK+UGd/9dQk54gIozinC58iyTAChVVpB3pAF38EPxwKkuo2\n" + - "qTzwk24d6PRxz1skkzwEphUQQzGboyHsAlJHN1MzM2/yFGB4l8iUua2d3ETyfy/xFEh/SwtGtXE5\n" + - "KLz4cpb0fxjeYQZVruBKxzE07kgDO3zOhmP3LJ/KSPHWYImd1DWmpY9iDvoXr6+V7FAnRloaEIyg\n" + - "7WwdlSCpo3TXVuIjLwIDAQABozIwMDAdBgNVHQ4EFgQUwmbHo8YbiR463GRKSLL3eIKyvDkwDwYD\n" + - "VR0TAQH/BAUwAwIBADANBgkqhkiG9w0BAQUFAAOCAQEAydijH3rbnvpBDB/30w2PCGMT7O0N/XYM\n" + - "wBtUidqa4NFumJrNrccx5Ehp4UP66BfP61HW8h2U/EekYfOsZyyWd4KnsDD6ycR8h/WvpK3BC2cn\n" + - "I299wbqCEZmk5ZFFaEIDHdLAdgMCuxJkAzy9mMrWEa05Soxi2/ZXdrU9nXo5dzuPGYlirVPDHl7r\n" + - "/urBxD6HVX3ObQJRJ7r/nAWyUVdX3/biJaDRsydftOpGU6Gi5c1JK4MWIz8Bsjh6mEjCsVatbPPl\n" + - "yygGiJbDZfAvN2XoaVEBii2GDDCWfaFwPVPYlNTvjkUkMP8YThlMsiJ8Q4693XoLOL94GpNlCfUg\n" + - "7n+KOQ=="; - - this.context = Mockito.mock(Context.class); - - mockBase64 = Mockito.mockStatic(Base64.class); - mockBase64.when(() -> Base64.encodeToString(any(byte[].class), anyInt())).thenAnswer(invocation -> java.util.Base64.getMimeEncoder().encodeToString((byte[]) invocation.getArguments()[0])); - mockBase64.when(() -> Base64.decode(anyString(), anyInt())).thenAnswer(invocation -> java.util.Base64.getMimeDecoder().decode((String) invocation.getArguments()[0])); - - //Store device information needed to create a Device object in a future - MockSharedPreference deviceSettings = new MockSharedPreference(); - SharedPreferences.Editor editor = deviceSettings.edit(); - editor.putString("deviceName", name); - editor.putString("deviceType", DeviceType.PHONE.toString()); - editor.putString("certificate", encodedCertificate); - editor.apply(); - Mockito.when(context.getSharedPreferences(eq(deviceId), eq(Context.MODE_PRIVATE))).thenReturn(deviceSettings); - - //Store the device as trusted - MockSharedPreference trustedSettings = new MockSharedPreference(); - trustedSettings.edit().putBoolean(deviceId, true).apply(); - Mockito.when(context.getSharedPreferences(eq("trusted_devices"), eq(Context.MODE_PRIVATE))).thenReturn(trustedSettings); - - //Store an untrusted device - MockSharedPreference untrustedSettings = new MockSharedPreference(); - Mockito.when(context.getSharedPreferences(eq("unpairedTestDevice"), eq(Context.MODE_PRIVATE))).thenReturn(untrustedSettings); - - //Default shared prefs, including our own private key - preferenceManager = Mockito.mockStatic(PreferenceManager.class); - MockSharedPreference defaultSettings = new MockSharedPreference(); - preferenceManager.when(() -> PreferenceManager.getDefaultSharedPreferences(any(Context.class))).thenReturn(defaultSettings); - - RsaHelper.initialiseRsaKeys(context); - - contextCompat = Mockito.mockStatic(ContextCompat.class); - contextCompat.when(() -> ContextCompat.getSystemService(context, NotificationManager.class)).thenReturn(Mockito.mock(NotificationManager.class)); - } - - @Test - public void testDeviceInfoToIdentityPacket() throws CertificateException { - String deviceId = "testDevice"; - SharedPreferences settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE); - DeviceInfo di = DeviceInfo.loadFromSettings(context, deviceId, settings); - di.protocolVersion = DeviceHelper.ProtocolVersion; - di.incomingCapabilities = new HashSet<>(Arrays.asList("kdeconnect.plugin1State", "kdeconnect.plugin2State")); - di.outgoingCapabilities = new HashSet<>(Arrays.asList("kdeconnect.plugin1State.request", "kdeconnect.plugin2State.request")); - - NetworkPacket np = di.toIdentityPacket(); - - assertEquals(di.id, np.getString("deviceId")); - assertEquals(di.name, np.getString("deviceName")); - assertEquals(di.protocolVersion, np.getInt("protocolVersion")); - assertEquals(di.type.toString(), np.getString("deviceType")); - assertEquals(di.incomingCapabilities, np.getStringSet("incomingCapabilities")); - assertEquals(di.outgoingCapabilities, np.getStringSet("outgoingCapabilities")); - } - - @Test - public void testIsValidIdentityPacket() { - NetworkPacket np = new NetworkPacket(NetworkPacket.PACKET_TYPE_IDENTITY); - assertFalse(DeviceInfo.Companion.isValidIdentityPacket(np)); - - String validName = "MyDevice"; - String validId = "123"; - np.set("deviceName", validName); - np.set("deviceId", validId); - assertTrue(DeviceInfo.Companion.isValidIdentityPacket(np)); - - np.set("deviceName", " "); - assertFalse(DeviceInfo.Companion.isValidIdentityPacket(np)); - np.set("deviceName", "<><><><><><><><><>"); // Only invalid characters - assertFalse(DeviceInfo.Companion.isValidIdentityPacket(np)); - - np.set("deviceName", validName); - np.set("deviceId", " "); - assertFalse(DeviceInfo.Companion.isValidIdentityPacket(np)); - } - - @Test - public void testDeviceType() { - assertEquals(DeviceType.PHONE, DeviceType.fromString(DeviceType.PHONE.toString())); - assertEquals(DeviceType.TABLET, DeviceType.fromString(DeviceType.TABLET.toString())); - assertEquals(DeviceType.DESKTOP, DeviceType.fromString(DeviceType.DESKTOP.toString())); - assertEquals(DeviceType.LAPTOP, DeviceType.fromString(DeviceType.LAPTOP.toString())); - assertEquals(DeviceType.TV, DeviceType.fromString(DeviceType.TV.toString())); - assertEquals(DeviceType.DESKTOP, DeviceType.fromString("invalid")); - } - - // Basic paired device testing - @Test - public void testDevice() throws CertificateException { - Device device = new Device(context, "testDevice"); - - assertEquals(device.getDeviceId(), "testDevice"); - assertEquals(device.getDeviceType(), DeviceType.PHONE); - assertEquals(device.getName(), "Test Device"); - assertTrue(device.isPaired()); - assertNotNull(device.getDeviceInfo().certificate); - } - - @Test - public void testPairingDone() throws CertificateException { - - NetworkPacket fakeNetworkPacket = new NetworkPacket(NetworkPacket.PACKET_TYPE_IDENTITY); - String deviceId = "unpairedTestDevice"; - fakeNetworkPacket.set("deviceId", deviceId); - fakeNetworkPacket.set("deviceName", "Unpaired Test Device"); - fakeNetworkPacket.set("protocolVersion", DeviceHelper.ProtocolVersion); - fakeNetworkPacket.set("deviceType", DeviceType.PHONE.toString()); - String certificateString = - "MIIDVzCCAj+gAwIBAgIBCjANBgkqhkiG9w0BAQUFADBVMS8wLQYDVQQDDCZfZGExNzlhOTFfZjA2\n" + - "NF80NzhlX2JlOGNfMTkzNWQ3NTQ0ZDU0XzEMMAoGA1UECgwDS0RFMRQwEgYDVQQLDAtLZGUgY29u\n" + - "bmVjdDAeFw0xNTA2MDMxMzE0MzhaFw0yNTA2MDMxMzE0MzhaMFUxLzAtBgNVBAMMJl9kYTE3OWE5\n" + - "MV9mMDY0XzQ3OGVfYmU4Y18xOTM1ZDc1NDRkNTRfMQwwCgYDVQQKDANLREUxFDASBgNVBAsMC0tk\n" + - "ZSBjb25uZWN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzH9GxS1lctpwYdSGAoPH\n" + - "ws+MnVaL0PVDCuzrpxzXc+bChR87xofhQIesLPLZEcmUJ1MlEJ6jx4W+gVhvY2tUN7SoiKKbnq8s\n" + - "WjI5ovs5yML3C1zPbOSJAdK613FcdkK+UGd/9dQk54gIozinC58iyTAChVVpB3pAF38EPxwKkuo2\n" + - "qTzwk24d6PRxz1skkzwEphUQQzGboyHsAlJHN1MzM2/yFGB4l8iUua2d3ETyfy/xFEh/SwtGtXE5\n" + - "KLz4cpb0fxjeYQZVruBKxzE07kgDO3zOhmP3LJ/KSPHWYImd1DWmpY9iDvoXr6+V7FAnRloaEIyg\n" + - "7WwdlSCpo3TXVuIjLwIDAQABozIwMDAdBgNVHQ4EFgQUwmbHo8YbiR463GRKSLL3eIKyvDkwDwYD\n" + - "VR0TAQH/BAUwAwIBADANBgkqhkiG9w0BAQUFAAOCAQEAydijH3rbnvpBDB/30w2PCGMT7O0N/XYM\n" + - "wBtUidqa4NFumJrNrccx5Ehp4UP66BfP61HW8h2U/EekYfOsZyyWd4KnsDD6ycR8h/WvpK3BC2cn\n" + - "I299wbqCEZmk5ZFFaEIDHdLAdgMCuxJkAzy9mMrWEa05Soxi2/ZXdrU9nXo5dzuPGYlirVPDHl7r\n" + - "/urBxD6HVX3ObQJRJ7r/nAWyUVdX3/biJaDRsydftOpGU6Gi5c1JK4MWIz8Bsjh6mEjCsVatbPPl\n" + - "yygGiJbDZfAvN2XoaVEBii2GDDCWfaFwPVPYlNTvjkUkMP8YThlMsiJ8Q4693XoLOL94GpNlCfUg\n" + - "7n+KOQ=="; - byte[] certificateBytes = Base64.decode(certificateString, 0); - Certificate certificate = SslHelper.parseCertificate(certificateBytes); - DeviceInfo deviceInfo = DeviceInfo.fromIdentityPacketAndCert(fakeNetworkPacket, certificate); - - LanLinkProvider linkProvider = Mockito.mock(LanLinkProvider.class); - Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider"); - LanLink link = Mockito.mock(LanLink.class); - Mockito.when(link.getLinkProvider()).thenReturn(linkProvider); - Mockito.when(link.getDeviceId()).thenReturn(deviceId); - Mockito.when(link.getDeviceInfo()).thenReturn(deviceInfo); - Device device = new Device(context, link); - - assertNotNull(device); - assertEquals(device.getDeviceId(), deviceId); - assertEquals(device.getName(), "Unpaired Test Device"); - assertEquals(device.getDeviceType(), DeviceType.PHONE); - assertNotNull(device.getDeviceInfo().certificate); - - device.getPairingHandler().pairingDone(); - - assertTrue(device.isPaired()); - - SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); - assertTrue(preferences.getBoolean(device.getDeviceId(), false)); - - SharedPreferences settings = context.getSharedPreferences(device.getDeviceId(), Context.MODE_PRIVATE); - assertEquals(settings.getString("deviceName", "Unknown device"), "Unpaired Test Device"); - assertEquals(settings.getString("deviceType", "tablet"), "phone"); - - // Cleanup for unpaired test device - preferences.edit().remove(device.getDeviceId()).apply(); - settings.edit().clear().apply(); - } - - @Test - public void testUnpair() throws CertificateException { - PairingHandler.PairingCallback pairingCallback = Mockito.mock(PairingHandler.PairingCallback.class); - Device device = new Device(context, "testDevice"); - device.addPairingCallback(pairingCallback); - - device.unpair(); - - assertFalse(device.isPaired()); - - SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); - assertFalse(preferences.getBoolean(device.getDeviceId(), false)); - - Mockito.verify(pairingCallback, Mockito.times(1)).unpaired(); - } -} diff --git a/tests/org/kde/kdeconnect/DeviceTest.kt b/tests/org/kde/kdeconnect/DeviceTest.kt new file mode 100644 index 00000000..9d3cfd7a --- /dev/null +++ b/tests/org/kde/kdeconnect/DeviceTest.kt @@ -0,0 +1,269 @@ +/* + * SPDX-FileCopyrightText: 2015 Vineet Garg + * + * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL +*/ +package org.kde.kdeconnect + +import android.app.NotificationManager +import android.content.Context +import android.preference.PreferenceManager +import android.util.Base64 +import androidx.core.content.ContextCompat +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.kde.kdeconnect.Backends.LanBackend.LanLink +import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider +import org.kde.kdeconnect.DeviceInfo.Companion.fromIdentityPacketAndCert +import org.kde.kdeconnect.DeviceInfo.Companion.isValidIdentityPacket +import org.kde.kdeconnect.DeviceInfo.Companion.loadFromSettings +import org.kde.kdeconnect.DeviceType.Companion.fromString +import org.kde.kdeconnect.Helpers.DeviceHelper +import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper +import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper +import org.kde.kdeconnect.PairingHandler.PairingCallback +import org.mockito.ArgumentMatchers +import org.mockito.MockedStatic +import org.mockito.Mockito +import org.mockito.invocation.InvocationOnMock +import java.security.cert.CertificateException + +class DeviceTest { + private lateinit var context: Context + private lateinit var mockBase64: MockedStatic + private lateinit var preferenceManager: MockedStatic + private lateinit var contextCompat: MockedStatic + + // Creating a paired device before each test case + @Before + fun setUp() { + // Save new test device in settings + + val deviceId = "testDevice" + val name = "Test Device" + val encodedCertificate = """ + MIIDVzCCAj+gAwIBAgIBCjANBgkqhkiG9w0BAQUFADBVMS8wLQYDVQQDDCZfZGExNzlhOTFfZjA2 + NF80NzhlX2JlOGNfMTkzNWQ3NTQ0ZDU0XzEMMAoGA1UECgwDS0RFMRQwEgYDVQQLDAtLZGUgY29u + bmVjdDAeFw0xNTA2MDMxMzE0MzhaFw0yNTA2MDMxMzE0MzhaMFUxLzAtBgNVBAMMJl9kYTE3OWE5 + MV9mMDY0XzQ3OGVfYmU4Y18xOTM1ZDc1NDRkNTRfMQwwCgYDVQQKDANLREUxFDASBgNVBAsMC0tk + ZSBjb25uZWN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzH9GxS1lctpwYdSGAoPH + ws+MnVaL0PVDCuzrpxzXc+bChR87xofhQIesLPLZEcmUJ1MlEJ6jx4W+gVhvY2tUN7SoiKKbnq8s + WjI5ovs5yML3C1zPbOSJAdK613FcdkK+UGd/9dQk54gIozinC58iyTAChVVpB3pAF38EPxwKkuo2 + qTzwk24d6PRxz1skkzwEphUQQzGboyHsAlJHN1MzM2/yFGB4l8iUua2d3ETyfy/xFEh/SwtGtXE5 + KLz4cpb0fxjeYQZVruBKxzE07kgDO3zOhmP3LJ/KSPHWYImd1DWmpY9iDvoXr6+V7FAnRloaEIyg + 7WwdlSCpo3TXVuIjLwIDAQABozIwMDAdBgNVHQ4EFgQUwmbHo8YbiR463GRKSLL3eIKyvDkwDwYD + VR0TAQH/BAUwAwIBADANBgkqhkiG9w0BAQUFAAOCAQEAydijH3rbnvpBDB/30w2PCGMT7O0N/XYM + wBtUidqa4NFumJrNrccx5Ehp4UP66BfP61HW8h2U/EekYfOsZyyWd4KnsDD6ycR8h/WvpK3BC2cn + I299wbqCEZmk5ZFFaEIDHdLAdgMCuxJkAzy9mMrWEa05Soxi2/ZXdrU9nXo5dzuPGYlirVPDHl7r + /urBxD6HVX3ObQJRJ7r/nAWyUVdX3/biJaDRsydftOpGU6Gi5c1JK4MWIz8Bsjh6mEjCsVatbPPl + yygGiJbDZfAvN2XoaVEBii2GDDCWfaFwPVPYlNTvjkUkMP8YThlMsiJ8Q4693XoLOL94GpNlCfUg + 7n+KOQ== + """.trimIndent() + + val context = Mockito.mock(Context::class.java) + val mockBase64 = Mockito.mockStatic(Base64::class.java) + + mockBase64.`when` { + Base64.encodeToString(ArgumentMatchers.any(ByteArray::class.java), ArgumentMatchers.anyInt()) + }.thenAnswer { invocation: InvocationOnMock -> + java.util.Base64.getMimeEncoder().encodeToString(invocation.arguments[0] as ByteArray) + } + + mockBase64.`when` { + Base64.decode(ArgumentMatchers.anyString(), ArgumentMatchers.anyInt()) + }.thenAnswer { invocation: InvocationOnMock -> + java.util.Base64.getMimeDecoder().decode(invocation.arguments[0] as String) + } + + // Store device information needed to create a Device object in a future + val deviceSettings = MockSharedPreference() + val editor = deviceSettings.edit() + editor.putString("deviceName", name) + editor.putString("deviceType", DeviceType.PHONE.toString()) + editor.putString("certificate", encodedCertificate) + editor.apply() + Mockito.`when`(context.getSharedPreferences(ArgumentMatchers.eq(deviceId), ArgumentMatchers.eq(Context.MODE_PRIVATE))).thenReturn(deviceSettings) + + // Store the device as trusted + val trustedSettings = MockSharedPreference() + trustedSettings.edit().putBoolean(deviceId, true).apply() + Mockito.`when`(context.getSharedPreferences(ArgumentMatchers.eq("trusted_devices"), ArgumentMatchers.eq(Context.MODE_PRIVATE))).thenReturn(trustedSettings) + + // Store an untrusted device + val untrustedSettings = MockSharedPreference() + Mockito.`when`(context.getSharedPreferences(ArgumentMatchers.eq("unpairedTestDevice"), ArgumentMatchers.eq(Context.MODE_PRIVATE))).thenReturn(untrustedSettings) + + // Default shared prefs, including our own private key + val preferenceManager = Mockito.mockStatic(PreferenceManager::class.java) + val defaultSettings = MockSharedPreference() + preferenceManager.`when` { + PreferenceManager.getDefaultSharedPreferences(ArgumentMatchers.any(Context::class.java)) + }.thenReturn(defaultSettings) + + RsaHelper.initialiseRsaKeys(context) + + val contextCompat = Mockito.mockStatic(ContextCompat::class.java) + contextCompat.`when` { + ContextCompat.getSystemService(context!!, NotificationManager::class.java) + }.thenReturn(Mockito.mock(NotificationManager::class.java)) + + this.context = context + this.mockBase64 = mockBase64 + this.preferenceManager = preferenceManager + this.contextCompat = contextCompat + } + + @After + fun tearDown() { + mockBase64.close() + preferenceManager.close() + contextCompat.close() + } + + @Test + @Throws(CertificateException::class) + fun testDeviceInfoToIdentityPacket() { + val deviceId = "testDevice" + val settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE) + val deviceInfo = loadFromSettings(context, deviceId, settings) + deviceInfo.protocolVersion = DeviceHelper.ProtocolVersion + deviceInfo.incomingCapabilities = hashSetOf("kdeconnect.plugin1State", "kdeconnect.plugin2State") + deviceInfo.outgoingCapabilities = hashSetOf("kdeconnect.plugin1State.request", "kdeconnect.plugin2State.request") + + val networkPacket = deviceInfo.toIdentityPacket() + Assert.assertEquals(deviceInfo.id, networkPacket.getString("deviceId")) + Assert.assertEquals(deviceInfo.name, networkPacket.getString("deviceName")) + Assert.assertEquals(deviceInfo.protocolVersion.toLong(), networkPacket.getInt("protocolVersion").toLong()) + Assert.assertEquals(deviceInfo.type.toString(), networkPacket.getString("deviceType")) + Assert.assertEquals(deviceInfo.incomingCapabilities, networkPacket.getStringSet("incomingCapabilities")) + Assert.assertEquals(deviceInfo.outgoingCapabilities, networkPacket.getStringSet("outgoingCapabilities")) + } + + @Test + fun testIsValidIdentityPacket() { + val np = NetworkPacket(NetworkPacket.PACKET_TYPE_IDENTITY) + Assert.assertFalse(isValidIdentityPacket(np)) + + val validName = "MyDevice" + val validId = "123" + np["deviceName"] = validName + np["deviceId"] = validId + Assert.assertTrue(isValidIdentityPacket(np)) + + np["deviceName"] = " " + Assert.assertFalse(isValidIdentityPacket(np)) + np["deviceName"] = "<><><><><><><><><>" // Only invalid characters + Assert.assertFalse(isValidIdentityPacket(np)) + + np["deviceName"] = validName + np["deviceId"] = " " + Assert.assertFalse(isValidIdentityPacket(np)) + } + + @Test + fun testDeviceType() { + Assert.assertEquals(DeviceType.PHONE, fromString(DeviceType.PHONE.toString())) + Assert.assertEquals(DeviceType.TABLET, fromString(DeviceType.TABLET.toString())) + Assert.assertEquals(DeviceType.DESKTOP, fromString(DeviceType.DESKTOP.toString())) + Assert.assertEquals(DeviceType.LAPTOP, fromString(DeviceType.LAPTOP.toString())) + Assert.assertEquals(DeviceType.TV, fromString(DeviceType.TV.toString())) + Assert.assertEquals(DeviceType.DESKTOP, fromString("invalid")) + } + + // Basic paired device testing + @Test + @Throws(CertificateException::class) + fun testDevice() { + val device = Device(context, "testDevice") + + Assert.assertEquals(device.deviceId, "testDevice") + Assert.assertEquals(device.deviceType, DeviceType.PHONE) + Assert.assertEquals(device.name, "Test Device") + Assert.assertTrue(device.isPaired) + Assert.assertNotNull(device.deviceInfo.certificate) + } + + @Test + @Throws(CertificateException::class) + fun testPairingDone() { + val fakeNetworkPacket = NetworkPacket(NetworkPacket.PACKET_TYPE_IDENTITY) + val deviceId = "unpairedTestDevice" + fakeNetworkPacket["deviceId"] = deviceId + fakeNetworkPacket["deviceName"] = "Unpaired Test Device" + fakeNetworkPacket["protocolVersion"] = DeviceHelper.ProtocolVersion + fakeNetworkPacket["deviceType"] = DeviceType.PHONE.toString() + val certificateString = + """ + MIIDVzCCAj+gAwIBAgIBCjANBgkqhkiG9w0BAQUFADBVMS8wLQYDVQQDDCZfZGExNzlhOTFfZjA2 + NF80NzhlX2JlOGNfMTkzNWQ3NTQ0ZDU0XzEMMAoGA1UECgwDS0RFMRQwEgYDVQQLDAtLZGUgY29u + bmVjdDAeFw0xNTA2MDMxMzE0MzhaFw0yNTA2MDMxMzE0MzhaMFUxLzAtBgNVBAMMJl9kYTE3OWE5 + MV9mMDY0XzQ3OGVfYmU4Y18xOTM1ZDc1NDRkNTRfMQwwCgYDVQQKDANLREUxFDASBgNVBAsMC0tk + ZSBjb25uZWN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzH9GxS1lctpwYdSGAoPH + ws+MnVaL0PVDCuzrpxzXc+bChR87xofhQIesLPLZEcmUJ1MlEJ6jx4W+gVhvY2tUN7SoiKKbnq8s + WjI5ovs5yML3C1zPbOSJAdK613FcdkK+UGd/9dQk54gIozinC58iyTAChVVpB3pAF38EPxwKkuo2 + qTzwk24d6PRxz1skkzwEphUQQzGboyHsAlJHN1MzM2/yFGB4l8iUua2d3ETyfy/xFEh/SwtGtXE5 + KLz4cpb0fxjeYQZVruBKxzE07kgDO3zOhmP3LJ/KSPHWYImd1DWmpY9iDvoXr6+V7FAnRloaEIyg + 7WwdlSCpo3TXVuIjLwIDAQABozIwMDAdBgNVHQ4EFgQUwmbHo8YbiR463GRKSLL3eIKyvDkwDwYD + VR0TAQH/BAUwAwIBADANBgkqhkiG9w0BAQUFAAOCAQEAydijH3rbnvpBDB/30w2PCGMT7O0N/XYM + wBtUidqa4NFumJrNrccx5Ehp4UP66BfP61HW8h2U/EekYfOsZyyWd4KnsDD6ycR8h/WvpK3BC2cn + I299wbqCEZmk5ZFFaEIDHdLAdgMCuxJkAzy9mMrWEa05Soxi2/ZXdrU9nXo5dzuPGYlirVPDHl7r + /urBxD6HVX3ObQJRJ7r/nAWyUVdX3/biJaDRsydftOpGU6Gi5c1JK4MWIz8Bsjh6mEjCsVatbPPl + yygGiJbDZfAvN2XoaVEBii2GDDCWfaFwPVPYlNTvjkUkMP8YThlMsiJ8Q4693XoLOL94GpNlCfUg + 7n+KOQ== + """.trimIndent() + val certificateBytes = Base64.decode(certificateString, 0) + val certificate = SslHelper.parseCertificate(certificateBytes) + val deviceInfo = fromIdentityPacketAndCert(fakeNetworkPacket, certificate) + + val linkProvider = Mockito.mock(LanLinkProvider::class.java) + Mockito.`when`(linkProvider.name).thenReturn("LanLinkProvider") + val link = Mockito.mock(LanLink::class.java) + Mockito.`when`(link.linkProvider).thenReturn(linkProvider) + Mockito.`when`(link.deviceId).thenReturn(deviceId) + Mockito.`when`(link.deviceInfo).thenReturn(deviceInfo) + val device = Device(context, link) + + Assert.assertNotNull(device) + Assert.assertEquals(device.deviceId, deviceId) + Assert.assertEquals(device.name, "Unpaired Test Device") + Assert.assertEquals(device.deviceType, DeviceType.PHONE) + Assert.assertNotNull(device.deviceInfo.certificate) + + device.pairingHandler.pairingDone() + + Assert.assertTrue(device.isPaired) + + val preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE) + Assert.assertTrue(preferences.getBoolean(device.deviceId, false)) + + val settings = context.getSharedPreferences(device.deviceId, Context.MODE_PRIVATE) + Assert.assertEquals( + settings.getString("deviceName", "Unknown device"), + "Unpaired Test Device" + ) + Assert.assertEquals(settings.getString("deviceType", "tablet"), "phone") + + // Cleanup for unpaired test device + preferences.edit().remove(device.deviceId).apply() + settings.edit().clear().apply() + } + + @Test + @Throws(CertificateException::class) + fun testUnpair() { + val pairingCallback = Mockito.mock(PairingCallback::class.java) + val device = Device(context, "testDevice") + device.addPairingCallback(pairingCallback) + + device.unpair() + + Assert.assertFalse(device.isPaired) + + val preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE) + Assert.assertFalse(preferences.getBoolean(device.deviceId, false)) + + Mockito.verify(pairingCallback, Mockito.times(1)).unpaired() + } +}