diff --git a/build.gradle b/build.gradle index c167b6a7..c377e766 100644 --- a/build.gradle +++ b/build.gradle @@ -15,8 +15,6 @@ android { defaultConfig { minSdkVersion 14 targetSdkVersion 28 - //multiDexEnabled true - //testInstrumentationRunner "com.android.test.runner.MultiDexTestRunner" } dexOptions { javaMaxHeapSize "2g" @@ -33,7 +31,7 @@ android { res.srcDirs = ['res'] assets.srcDirs = ['assets'] } - androidTest { + test { java.srcDirs = ['tests'] } } @@ -87,9 +85,10 @@ dependencies { annotationProcessor 'org.atteo.classindex:classindex:3.6' // Testing - androidTestImplementation 'org.mockito:mockito-core:1.10.19' - androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:1.1'// Because mockito has some problems with dex environment - androidTestImplementation 'org.skyscreamer:jsonassert:1.3.0' testImplementation 'junit:junit:4.12' - + testImplementation 'org.powermock:powermock-core:2.0.0' + testImplementation 'org.powermock:powermock-module-junit4:2.0.0' + testImplementation 'org.powermock:powermock-api-mockito2:2.0.0' + testImplementation 'org.mockito:mockito-core:2.23.0' + testImplementation 'org.skyscreamer:jsonassert:1.3.0' } diff --git a/tests/org/kde/kdeconnect/DeviceTest.java b/tests/org/kde/kdeconnect/DeviceTest.java index fd3d1f20..61477bce 100644 --- a/tests/org/kde/kdeconnect/DeviceTest.java +++ b/tests/org/kde/kdeconnect/DeviceTest.java @@ -20,44 +20,52 @@ package org.kde.kdeconnect; +import android.app.NotificationManager; import android.content.Context; import android.content.SharedPreferences; +import android.preference.PreferenceManager; import android.util.Base64; import android.util.Log; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import org.kde.kdeconnect.Backends.BasePairingHandler; import org.kde.kdeconnect.Backends.LanBackend.LanLink; import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider; import org.kde.kdeconnect.Backends.LanBackend.LanPairingHandler; +import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper; +import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper; import org.mockito.Mockito; -import org.spongycastle.asn1.x500.X500NameBuilder; -import org.spongycastle.asn1.x500.style.BCStyle; -import org.spongycastle.cert.X509v3CertificateBuilder; -import org.spongycastle.cert.jcajce.JcaX509CertificateConverter; -import org.spongycastle.cert.jcajce.JcaX509v3CertificateBuilder; -import org.spongycastle.jce.provider.BouncyCastleProvider; -import org.spongycastle.operator.ContentSigner; -import org.spongycastle.operator.jcajce.JcaContentSignerBuilder; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; import java.lang.reflect.Method; -import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; -import java.security.cert.X509Certificate; -import java.util.Date; -class DeviceTest extends AndroidTestCase { +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +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.Matchers.eq; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({Base64.class, Log.class, PreferenceManager.class}) +public class DeviceTest { + + Context context; // Creating a paired device before each test case - @Override - protected void setUp() throws Exception { - super.setUp(); - - // Dexmaker has problems guessing cache directory, setting manually - System.setProperty("dexmaker.dexcache", getContext().getCacheDir().getPath()); + @Before + public void setUp() { // Save new test device in settings - Context context = getContext(); String deviceId = "testDevice"; String name = "Test Device"; @@ -73,45 +81,46 @@ class DeviceTest extends AndroidTestCase { return; } - SharedPreferences settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE); + this.context = Mockito.mock(Context.class); + + PowerMockito.mockStatic(Base64.class); + PowerMockito.when(Base64.encodeToString(any(), anyInt())).thenAnswer(invocation -> java.util.Base64.getMimeEncoder().encodeToString((byte[]) invocation.getArguments()[0])); + PowerMockito.when(Base64.decode(anyString(), anyInt())).thenAnswer(invocation -> java.util.Base64.getMimeDecoder().decode((String) invocation.getArguments()[0])); + + PowerMockito.mockStatic(Log.class); //Store device information needed to create a Device object in a future - SharedPreferences.Editor editor = settings.edit(); + MockSharedPreference deviceSettings = new MockSharedPreference(); + SharedPreferences.Editor editor = deviceSettings.edit(); editor.putString("deviceName", name); editor.putString("deviceType", Device.DeviceType.Phone.toString()); editor.putString("publicKey", Base64.encodeToString(keyPair.getPublic().getEncoded(), 0).trim() + "\n"); editor.apply(); + Mockito.when(context.getSharedPreferences(eq(deviceId), eq(Context.MODE_PRIVATE))).thenReturn(deviceSettings); - SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); - preferences.edit().putBoolean(deviceId, true).apply(); - } + //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); - // Removing paired device info after each test case - @Override - protected void tearDown() throws Exception { - super.tearDown(); + //Default shared prefs, including our own private key + PowerMockito.mockStatic(PreferenceManager.class); + MockSharedPreference defaultSettings = new MockSharedPreference(); + PowerMockito.when(PreferenceManager.getDefaultSharedPreferences(any())).thenReturn(defaultSettings); + RsaHelper.initialiseRsaKeys(context); - // Remove saved test device - Context context = getContext(); - - String deviceId = "testDevice"; - - SharedPreferences settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE); - - //Store device information needed to create a Device object in a future - SharedPreferences.Editor editor = settings.edit(); - editor.clear(); - editor.apply(); - - SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); - preferences.edit().remove(deviceId).apply(); - } + Mockito.when(context.getSystemService(eq(Context.NOTIFICATION_SERVICE))).thenReturn(Mockito.mock(NotificationManager.class)); +} // Basic paired device testing + @Test public void testDevice() { - Device device = new Device(getContext(), "testDevice"); + Device device = new Device(context, "testDevice"); assertEquals(device.getDeviceId(), "testDevice"); assertEquals(device.getDeviceType(), Device.DeviceType.Phone); @@ -122,6 +131,7 @@ class DeviceTest extends AndroidTestCase { // Testing pairing done using reflection since it is private // Created an unpaired device inside this test + @Test public void testPairingDone() { NetworkPacket fakeNetworkPacket = new NetworkPacket(NetworkPacket.PACKET_TYPE_IDENTITY); @@ -134,8 +144,8 @@ class DeviceTest extends AndroidTestCase { Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider"); LanLink link = Mockito.mock(LanLink.class); Mockito.when(link.getLinkProvider()).thenReturn(linkProvider); - Mockito.when(link.getPairingHandler(Mockito.any(Device.class), Mockito.any(BasePairingHandler.PairingHandlerCallback.class))).thenReturn(Mockito.mock(LanPairingHandler.class)); - Device device = new Device(getContext(), fakeNetworkPacket, link); + Mockito.when(link.getPairingHandler(any(Device.class), any(BasePairingHandler.PairingHandlerCallback.class))).thenReturn(Mockito.mock(LanPairingHandler.class)); + Device device = new Device(context, fakeNetworkPacket, link); KeyPair keyPair; try { @@ -167,10 +177,10 @@ class DeviceTest extends AndroidTestCase { assertTrue(device.isPaired()); - SharedPreferences preferences = getContext().getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); + SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); assertTrue(preferences.getBoolean(device.getDeviceId(), false)); - SharedPreferences settings = getContext().getSharedPreferences(device.getDeviceId(), Context.MODE_PRIVATE); + SharedPreferences settings = context.getSharedPreferences(device.getDeviceId(), Context.MODE_PRIVATE); assertEquals(settings.getString("deviceName", "Unknown device"), "Unpaired Test Device"); assertEquals(settings.getString("deviceType", "tablet"), "phone"); @@ -180,6 +190,7 @@ class DeviceTest extends AndroidTestCase { } + @Test public void testPairingDoneWithCertificate() throws Exception { KeyPair keyPair = null; try { @@ -191,46 +202,35 @@ class DeviceTest extends AndroidTestCase { Log.e("KDE/initializeRsaKeys", "Exception"); } - X509Certificate certificate = null; - try { - - BouncyCastleProvider BC = new BouncyCastleProvider(); - - X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); - nameBuilder.addRDN(BCStyle.CN, "testDevice"); - nameBuilder.addRDN(BCStyle.OU, "KDE Connect"); - nameBuilder.addRDN(BCStyle.O, "KDE"); - Date notBefore = new Date(System.currentTimeMillis()); - Date notAfter = new Date(System.currentTimeMillis() + System.currentTimeMillis()); - X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder( - nameBuilder.build(), - BigInteger.ONE, - notBefore, - notAfter, - nameBuilder.build(), - keyPair.getPublic() - ); - ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(BC).build(keyPair.getPrivate()); - certificate = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateBuilder.build(contentSigner)); - - } catch (Exception e) { - e.printStackTrace(); - Log.e("KDE/initialiseCert", "Exception"); - } - NetworkPacket fakeNetworkPacket = new NetworkPacket(NetworkPacket.PACKET_TYPE_IDENTITY); fakeNetworkPacket.set("deviceId", "unpairedTestDevice"); fakeNetworkPacket.set("deviceName", "Unpaired Test Device"); fakeNetworkPacket.set("protocolVersion", NetworkPacket.ProtocolVersion); fakeNetworkPacket.set("deviceType", Device.DeviceType.Phone.toString()); - fakeNetworkPacket.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0)); + fakeNetworkPacket.set("certificate", + "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=="); LanLinkProvider linkProvider = Mockito.mock(LanLinkProvider.class); Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider"); LanLink link = Mockito.mock(LanLink.class); - Mockito.when(link.getPairingHandler(Mockito.any(Device.class), Mockito.any(BasePairingHandler.PairingHandlerCallback.class))).thenReturn(Mockito.mock(LanPairingHandler.class)); + Mockito.when(link.getPairingHandler(any(Device.class), any(BasePairingHandler.PairingHandlerCallback.class))).thenReturn(Mockito.mock(LanPairingHandler.class)); Mockito.when(link.getLinkProvider()).thenReturn(linkProvider); - Device device = new Device(getContext(), fakeNetworkPacket, link); + Device device = new Device(context, fakeNetworkPacket, link); device.publicKey = keyPair.getPublic(); assertNotNull(device); @@ -251,10 +251,10 @@ class DeviceTest extends AndroidTestCase { assertTrue(device.isPaired()); - SharedPreferences preferences = getContext().getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); + SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); assertTrue(preferences.getBoolean(device.getDeviceId(), false)); - SharedPreferences settings = getContext().getSharedPreferences(device.getDeviceId(), Context.MODE_PRIVATE); + SharedPreferences settings = context.getSharedPreferences(device.getDeviceId(), Context.MODE_PRIVATE); assertEquals(settings.getString("deviceName", "Unknown device"), "Unpaired Test Device"); assertEquals(settings.getString("deviceType", "tablet"), "phone"); @@ -263,15 +263,16 @@ class DeviceTest extends AndroidTestCase { settings.edit().clear().apply(); } + @Test public void testUnpair() { - Device device = new Device(getContext(), "testDevice"); + Device device = new Device(context, "testDevice"); device.unpair(); assertFalse(device.isPaired()); - SharedPreferences preferences = getContext().getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); + SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); assertFalse(preferences.getBoolean(device.getDeviceId(), false)); } diff --git a/tests/org/kde/kdeconnect/LanLinkProviderTest.java b/tests/org/kde/kdeconnect/LanLinkProviderTest.java index 7be34180..7c3a3758 100644 --- a/tests/org/kde/kdeconnect/LanLinkProviderTest.java +++ b/tests/org/kde/kdeconnect/LanLinkProviderTest.java @@ -20,30 +20,44 @@ package org.kde.kdeconnect; +import android.util.Log; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import org.kde.kdeconnect.Backends.LanBackend.LanLink; import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider; +import org.kde.kdeconnect.Helpers.DeviceHelper; import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.Socket; import java.util.HashMap; -class LanLinkProviderTest extends AndroidTestCase { +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; - private LanLinkProvider linkProvider; +@RunWith(PowerMockRunner.class) +@PrepareForTest({DeviceHelper.class, Log.class}) +public class LanLinkProviderTest { - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() { + PowerMockito.mockStatic(DeviceHelper.class); + PowerMockito.when(DeviceHelper.getDeviceId(any())).thenReturn("123"); - System.setProperty("dexmaker.dexcache", getContext().getCacheDir().getPath()); - - linkProvider = new LanLinkProvider(getContext()); + PowerMockito.mockStatic(Log.class); } + @Test public void testIdentityPacketReceived() throws Exception { + LanLinkProvider linkProvider = new LanLinkProvider(null); + NetworkPacket networkPacket = Mockito.mock(NetworkPacket.class); Mockito.when(networkPacket.getType()).thenReturn("kdeconnect.identity"); Mockito.when(networkPacket.getString("deviceId")).thenReturn("testDevice"); diff --git a/tests/org/kde/kdeconnect/LanLinkTest.java b/tests/org/kde/kdeconnect/LanLinkTest.java index 636def9c..0ac5bd83 100644 --- a/tests/org/kde/kdeconnect/LanLinkTest.java +++ b/tests/org/kde/kdeconnect/LanLinkTest.java @@ -20,13 +20,21 @@ package org.kde.kdeconnect; +import android.content.Context; import android.util.Log; import org.json.JSONException; import org.json.JSONObject; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import org.kde.kdeconnect.Backends.LanBackend.LanLink; import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider; +import org.kde.kdeconnect.Helpers.DeviceHelper; import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -36,7 +44,11 @@ import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; -class LanLinkTest extends AndroidTestCase { +import static org.junit.Assert.assertEquals; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({Log.class}) +public class LanLinkTest { private LanLink badLanLink; private LanLink goodLanLink; @@ -46,11 +58,9 @@ class LanLinkTest extends AndroidTestCase { private Device.SendPacketStatusCallback callback; - @Override - protected void setUp() throws Exception { - super.setUp(); - - System.setProperty("dexmaker.dexcache", getContext().getCacheDir().getPath()); + @Before + public void setUp() throws Exception { + PowerMockito.mockStatic(Log.class); LanLinkProvider linkProvider = Mockito.mock(LanLinkProvider.class); Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider"); @@ -70,10 +80,12 @@ class LanLinkTest extends AndroidTestCase { Mockito.when(socketBadMock.getRemoteSocketAddress()).thenReturn(new InetSocketAddress(5000)); Mockito.when(socketBadMock.getOutputStream()).thenReturn(badOutputStream); - goodLanLink = new LanLink(getContext(), "testDevice", linkProvider, socketMock, LanLink.ConnectionStarted.Remotely); - badLanLink = new LanLink(getContext(), "testDevice", linkProvider, socketBadMock, LanLink.ConnectionStarted.Remotely); + Context context = Mockito.mock(Context.class); + goodLanLink = new LanLink(context, "testDevice", linkProvider, socketMock, LanLink.ConnectionStarted.Remotely); + badLanLink = new LanLink(context, "testDevice", linkProvider, socketBadMock, LanLink.ConnectionStarted.Remotely); } + @Test public void testSendPacketSuccess() throws JSONException { NetworkPacket testPacket = Mockito.mock(NetworkPacket.class); @@ -87,6 +99,7 @@ class LanLinkTest extends AndroidTestCase { Mockito.verify(callback).onSuccess(); } + @Test public void testSendPacketFail() throws JSONException { NetworkPacket testPacket = Mockito.mock(NetworkPacket.class); @@ -97,11 +110,11 @@ class LanLinkTest extends AndroidTestCase { badLanLink.sendPacket(testPacket, callback); - Mockito.verify(callback).onFailure(Mockito.any(RuntimeException.class)); + Mockito.verify(callback).onFailure(Mockito.any(IOException.class)); } - + @Test public void testSendPayload() throws Exception { class Downloader extends Thread { @@ -127,7 +140,7 @@ class LanLinkTest extends AndroidTestCase { int tcpPort = np.getPayloadTransferInfo().getInt("port"); InetSocketAddress address = new InetSocketAddress(5000); socket.connect(new InetSocketAddress(address.getAddress(), tcpPort)); - np.setPayload(socket.getInputStream(), np.getPayloadSize()); + np.setPayload(new NetworkPacket.Payload(socket.getInputStream(), np.getPayloadSize())); } catch (Exception e) { socket.close(); e.printStackTrace(); @@ -135,7 +148,7 @@ class LanLinkTest extends AndroidTestCase { throw e; } - final InputStream input = np.getPayload(); + final InputStream input = np.getPayload().getInputStream(); final long fileLength = np.getPayloadSize(); byte data[] = new byte[1024]; @@ -193,7 +206,7 @@ class LanLinkTest extends AndroidTestCase { Mockito.when(sharePacket.hasPayload()).thenReturn(true); Mockito.when(sharePacket.hasPayloadTransferInfo()).thenReturn(true); Mockito.doAnswer(invocationOnMock -> sharePacketJson.toString()).when(sharePacket).serialize(); - Mockito.when(sharePacket.getPayload()).thenReturn(new ByteArrayInputStream(data)); + Mockito.when(sharePacket.getPayload()).thenReturn(new NetworkPacket.Payload(new ByteArrayInputStream(data), -1)); Mockito.when(sharePacket.getPayloadSize()).thenReturn((long) data.length); Mockito.doAnswer(invocationOnMock -> sharePacketJson.getJSONObject("payloadTransferInfo")).when(sharePacket).getPayloadTransferInfo(); Mockito.doAnswer(invocationOnMock -> { diff --git a/tests/org/kde/kdeconnect/MockSharedPreference.java b/tests/org/kde/kdeconnect/MockSharedPreference.java new file mode 100644 index 00000000..e8c0010c --- /dev/null +++ b/tests/org/kde/kdeconnect/MockSharedPreference.java @@ -0,0 +1,159 @@ +package org.kde.kdeconnect; + +import android.content.SharedPreferences; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import androidx.annotation.Nullable; + + +/** + * Mock implementation of shared preference, which just saves data in memory using map. + * + * It DOES NOT support transactions, changes are immediate + * + * From https://gist.github.com/amardeshbd/354173d00b988574ee5019c4ba0c8a0b + */ +public class MockSharedPreference implements SharedPreferences { + + private final HashMap preferenceMap; + private final MockSharedPreferenceEditor preferenceEditor; + + public MockSharedPreference() { + preferenceMap = new HashMap<>(); + preferenceEditor = new MockSharedPreferenceEditor(preferenceMap); + } + + @Override + public Map getAll() { + return preferenceMap; + } + + @Nullable + @Override + public String getString(final String s, @Nullable final String def) { + if (!preferenceMap.containsKey(s)) return def; + return (String) preferenceMap.get(s); + } + + @Nullable + @Override + public Set getStringSet(final String s, @Nullable final Set def) { + if (!preferenceMap.containsKey(s)) return def; + return (Set) preferenceMap.get(s); + } + + @Override + public int getInt(final String s, final int def) { + if (!preferenceMap.containsKey(s)) return def; + return (int) preferenceMap.get(s); + } + + @Override + public long getLong(final String s, final long def) { + if (!preferenceMap.containsKey(s)) return def; + return (long) preferenceMap.get(s); + } + + @Override + public float getFloat(final String s, final float def) { + if (!preferenceMap.containsKey(s)) return def; + return (float) preferenceMap.get(s); + } + + @Override + public boolean getBoolean(final String s, final boolean def) { + if (!preferenceMap.containsKey(s)) return def; + return (boolean) preferenceMap.get(s); + } + + @Override + public boolean contains(final String s) { + return preferenceMap.containsKey(s); + } + + @Override + public Editor edit() { + return preferenceEditor; + } + + @Override + public void registerOnSharedPreferenceChangeListener(final OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) { + + } + + @Override + public void unregisterOnSharedPreferenceChangeListener(final OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) { + + } + + public static class MockSharedPreferenceEditor implements Editor { + + private final HashMap preferenceMap; + + public MockSharedPreferenceEditor(final HashMap preferenceMap) { + this.preferenceMap = preferenceMap; + } + + @Override + public Editor putString(final String s, @Nullable final String s1) { + preferenceMap.put(s, s1); + return this; + } + + @Override + public Editor putStringSet(final String s, @Nullable final Set set) { + preferenceMap.put(s, set); + return this; + } + + @Override + public Editor putInt(final String s, final int i) { + preferenceMap.put(s, i); + return this; + } + + @Override + public Editor putLong(final String s, final long l) { + preferenceMap.put(s, l); + return this; + } + + @Override + public Editor putFloat(final String s, final float v) { + preferenceMap.put(s, v); + return this; + } + + @Override + public Editor putBoolean(final String s, final boolean b) { + preferenceMap.put(s, b); + return this; + } + + @Override + public Editor remove(final String s) { + preferenceMap.remove(s); + return this; + } + + @Override + public Editor clear() { + preferenceMap.clear(); + return this; + } + + @Override + public boolean commit() { + return true; + } + + @Override + public void apply() { + // Nothing to do, everything is saved in memory. + } + } + +} diff --git a/tests/org/kde/kdeconnect/NetworkPacketTest.java b/tests/org/kde/kdeconnect/NetworkPacketTest.java index 6a84aa6f..affe5bff 100644 --- a/tests/org/kde/kdeconnect/NetworkPacketTest.java +++ b/tests/org/kde/kdeconnect/NetworkPacketTest.java @@ -20,19 +20,40 @@ package org.kde.kdeconnect; +import android.content.Context; import android.util.Log; import org.json.JSONException; -import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper; -import org.skyscreamer.jsonassert.JSONAssert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.kde.kdeconnect.Helpers.DeviceHelper; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.PrivateKey; -import java.security.PublicKey; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; -class NetworkPacketTest extends AndroidTestCase { +@RunWith(PowerMockRunner.class) +@PrepareForTest({DeviceHelper.class, Log.class}) +public class NetworkPacketTest { + @Before + public void setUp() { + PowerMockito.mockStatic(DeviceHelper.class); + PowerMockito.when(DeviceHelper.getDeviceId(any())).thenReturn("123"); + PowerMockito.when(DeviceHelper.getDeviceType(any())).thenReturn(Device.DeviceType.Phone); + + PowerMockito.mockStatic(Log.class); + } + + @Test public void testNetworkPacket() throws JSONException { NetworkPacket np = new NetworkPacket("com.test"); @@ -61,67 +82,17 @@ class NetworkPacketTest extends AndroidTestCase { } + @Test public void testIdentity() { - NetworkPacket np = NetworkPacket.createIdentityPacket(getContext()); + Context context = Mockito.mock(Context.class); + MockSharedPreference settings = new MockSharedPreference(); + Mockito.when(context.getSharedPreferences(anyString(), anyInt())).thenReturn(settings); + + NetworkPacket np = NetworkPacket.createIdentityPacket(context); assertEquals(np.getInt("protocolVersion"), NetworkPacket.ProtocolVersion); } - public void testEncryption() throws JSONException { - NetworkPacket original = new NetworkPacket("com.test"); - original.set("hello", "hola"); - - NetworkPacket copy = NetworkPacket.unserialize(original.serialize()); - - NetworkPacket decrypted = new NetworkPacket(""); - - KeyPair keyPair; - try { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(2048); - keyPair = keyGen.genKeyPair(); - } catch (Exception e) { - e.printStackTrace(); - Log.e("KDE/initializeRsaKeys", "Exception"); - return; - } - - PrivateKey privateKey = keyPair.getPrivate(); - assertNotNull(privateKey); - - PublicKey publicKey = keyPair.getPublic(); - assertNotNull(publicKey); - - // Encrypt and decrypt np - assertEquals(original.getType(), "com.test"); - try { - NetworkPacket encrypted = RsaHelper.encrypt(original, publicKey); - assertEquals(encrypted.getType(), NetworkPacket.PACKET_TYPE_ENCRYPTED); - - decrypted = RsaHelper.decrypt(encrypted, privateKey); - assertEquals(decrypted.getType(), "com.test"); - - } catch (Exception e) { - e.printStackTrace(); - } - - // np should be equal to np2 - assertEquals(decrypted.getLong("id"), copy.getLong("id")); - assertEquals(decrypted.getType(), copy.getType()); - assertEquals(decrypted.getJSONArray("body"), copy.getJSONArray("body")); - - String json = "{\"body\":{\"nowPlaying\":\"A really long song name - A really long artist name\",\"player\":\"A really long player name\",\"the_meaning_of_life_the_universe_and_everything\":\"42\"},\"id\":945945945,\"type\":\"kdeconnect.a_really_really_long_package_type\"}\n"; - NetworkPacket longJsonNp = NetworkPacket.unserialize(json); - try { - NetworkPacket encrypted = RsaHelper.encrypt(longJsonNp, publicKey); - decrypted = RsaHelper.decrypt(encrypted, privateKey); - String decryptedJson = decrypted.serialize(); - JSONAssert.assertEquals(json, decryptedJson, true); - } catch (Exception e) { - e.printStackTrace(); - } - } - }