From c6dbadce2109dd52d699432edf0a8dddc5eabaad Mon Sep 17 00:00:00 2001 From: Albert Vaca Cintora Date: Mon, 13 May 2024 12:31:43 +0200 Subject: [PATCH] Upgrade to Mockito 5, remove PowerMock and update test PowerMock is no longer maintained and is only compatible with Mockito 3. However, modern Mockito supports mocking static methods, so PowerMock is no longer needed (after adjusting the tests a bit). --- build.gradle.kts | 5 +-- gradle/libs.versions.toml | 6 +--- src/org/kde/kdeconnect/PairingHandler.kt | 4 ++- tests/android/util/Log.java | 29 +++++++++++++++ tests/org/kde/kdeconnect/DeviceTest.java | 45 +++++++++++++----------- 5 files changed, 59 insertions(+), 30 deletions(-) create mode 100644 tests/android/util/Log.java diff --git a/build.gradle.kts b/build.gradle.kts index 513483e1..47ba2ab6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -219,10 +219,7 @@ dependencies { // Testing testImplementation(libs.junit) - testImplementation(libs.powermock.core) - testImplementation(libs.powermock.module.junit4) - testImplementation(libs.powermock.api.mockito2) - testImplementation(libs.mockito.core) // powermock isn't compatible with mockito 4 + testImplementation(libs.mockito.core) testImplementation(libs.jsonassert) // For device controls diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ad5db261..1faa8a25 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,8 +28,7 @@ material = "1.12.0" material3 = "1.2.1" media = "1.7.0" minaCore = "2.0.19" -mockitoCore = "3.12.4" -powermockModuleJunit4 = "2.0.9" +mockitoCore = "5.12.0" preferenceKtx = "1.2.1" reactiveStreams = "1.0.4" recyclerview = "1.3.2" @@ -78,9 +77,6 @@ material = { module = "com.google.android.material:material", version.ref = "mat apache-mina-core = { module = "org.apache.mina:mina-core", version.ref = "minaCore" } apache-sshd-core = { module = "org.apache.sshd:sshd-core", version.ref = "sshdCore" } mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockitoCore" } -powermock-api-mockito2 = { module = "org.powermock:powermock-api-mockito2", version.ref = "powermockModuleJunit4" } -powermock-core = { module = "org.powermock:powermock-core", version.ref = "powermockModuleJunit4" } -powermock-module-junit4 = { module = "org.powermock:powermock-module-junit4", version.ref = "powermockModuleJunit4" } reactive-streams = { module = "org.reactivestreams:reactive-streams", version.ref = "reactiveStreams" } rxjava = { module = "io.reactivex.rxjava2:rxjava", version.ref = "rxjava" } univocity-parsers = { module = "com.univocity:univocity-parsers", version.ref = "univocityParsers" } diff --git a/src/org/kde/kdeconnect/PairingHandler.kt b/src/org/kde/kdeconnect/PairingHandler.kt index bad7a070..6373e3bf 100644 --- a/src/org/kde/kdeconnect/PairingHandler.kt +++ b/src/org/kde/kdeconnect/PairingHandler.kt @@ -6,6 +6,7 @@ package org.kde.kdeconnect import android.util.Log +import androidx.annotation.VisibleForTesting import kotlinx.coroutines.* import org.kde.kdeconnect_tp.R import kotlin.time.Duration.Companion.seconds @@ -155,7 +156,8 @@ class PairingHandler(private val device: Device, private val callback: PairingCa callback.pairingFailed(device.context.getString(R.string.error_canceled_by_user)) } - private fun pairingDone() { + @VisibleForTesting + fun pairingDone() { Log.i("PairingHandler", "Pairing done") state = PairState.Paired kotlin.runCatching { diff --git a/tests/android/util/Log.java b/tests/android/util/Log.java new file mode 100644 index 00000000..351cafec --- /dev/null +++ b/tests/android/util/Log.java @@ -0,0 +1,29 @@ +package android.util; + +// Based on https://stackoverflow.com/questions/36787449/how-to-mock-method-e-in-log +public class Log { + public static int d(String tag, String msg) { + System.out.println("DEBUG: " + tag + ": " + msg); + return 0; + } + + public static int i(String tag, String msg) { + System.out.println("INFO: " + tag + ": " + msg); + return 0; + } + + public static int w(String tag, String msg) { + System.out.println("WARN: " + tag + ": " + msg); + return 0; + } + + public static int e(String tag, String msg) { + System.out.println("ERROR: " + tag + ": " + msg); + return 0; + } + + public static int e(String tag, String msg, Throwable e) { + System.out.println("ERROR: " + tag + ": " + msg + ": " + e); + return 0; + } +} diff --git a/tests/org/kde/kdeconnect/DeviceTest.java b/tests/org/kde/kdeconnect/DeviceTest.java index d727ab7d..3fa2f12d 100644 --- a/tests/org/kde/kdeconnect/DeviceTest.java +++ b/tests/org/kde/kdeconnect/DeviceTest.java @@ -24,32 +24,37 @@ import android.util.Log; import androidx.core.content.ContextCompat; +import org.junit.After; 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.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper; import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper; +import org.mockito.MockedStatic; 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.InvocationTargetException; -import java.lang.reflect.Method; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.Arrays; import java.util.HashSet; -@RunWith(PowerMockRunner.class) -@PrepareForTest({Base64.class, Log.class, PreferenceManager.class, ContextCompat.class}) 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() { @@ -76,11 +81,11 @@ public class DeviceTest { 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])); + 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])); - PowerMockito.mockStatic(Log.class); + Mockito.mockStatic(Log.class).close(); //Store device information needed to create a Device object in a future MockSharedPreference deviceSettings = new MockSharedPreference(); @@ -101,13 +106,14 @@ public class DeviceTest { Mockito.when(context.getSharedPreferences(eq("unpairedTestDevice"), eq(Context.MODE_PRIVATE))).thenReturn(untrustedSettings); //Default shared prefs, including our own private key - PowerMockito.mockStatic(PreferenceManager.class); + preferenceManager = Mockito.mockStatic(PreferenceManager.class); MockSharedPreference defaultSettings = new MockSharedPreference(); - PowerMockito.when(PreferenceManager.getDefaultSharedPreferences(any())).thenReturn(defaultSettings); + preferenceManager.when(() -> PreferenceManager.getDefaultSharedPreferences(any(Context.class))).thenReturn(defaultSettings); + RsaHelper.initialiseRsaKeys(context); - PowerMockito.mockStatic(ContextCompat.class); - PowerMockito.when(ContextCompat.getSystemService(context, NotificationManager.class)).thenReturn(Mockito.mock(NotificationManager.class)); + contextCompat = Mockito.mockStatic(ContextCompat.class); + contextCompat.when(() -> ContextCompat.getSystemService(context, NotificationManager.class)).thenReturn(Mockito.mock(NotificationManager.class)); } @Test @@ -151,7 +157,8 @@ public class DeviceTest { assertNotNull(device.deviceInfo.certificate); } - public void testPairingDone() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, CertificateException { + @Test + public void testPairingDone() throws CertificateException { NetworkPacket fakeNetworkPacket = new NetworkPacket(NetworkPacket.PACKET_TYPE_IDENTITY); String deviceId = "unpairedTestDevice"; @@ -194,9 +201,7 @@ public class DeviceTest { assertEquals(device.getDeviceType(), DeviceType.PHONE); assertNotNull(device.deviceInfo.certificate); - Method method = PairingHandler.class.getDeclaredMethod("pairingDone"); - method.setAccessible(true); - method.invoke(device.pairingHandler); + device.pairingHandler.pairingDone(); assertTrue(device.isPaired());