diff --git a/.github/workflows/emulate.yml b/.github/workflows/emulate.yml index 10a3ad2d5..8cf2e7fe7 100644 --- a/.github/workflows/emulate.yml +++ b/.github/workflows/emulate.yml @@ -35,7 +35,8 @@ jobs: api-level: ${{ matrix.android-api-level }} emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none disable-animations: true - script: ./gradlew :android:connectedAndroidTest + # Print emulator logs if tests fail + script: ./gradlew :android:connectedAndroidTest || (adb logcat -d System.out:I && exit 1) - uses: actions/upload-artifact@v3 if: always() diff --git a/android/build.gradle.kts b/android/build.gradle.kts index a63917f6d..b4a5071d7 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -7,7 +7,7 @@ android { namespace = "io.ably.lib" defaultConfig { minSdk = 19 - compileSdk = 30 + compileSdk = 34 buildConfigField("String", "LIBRARY_NAME", "\"android\"") buildConfigField("String", "VERSION", "\"${property("VERSION_NAME")}\"") testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" @@ -35,7 +35,7 @@ android { abortOnError = false } - testOptions.targetSdk = 30 + testOptions.targetSdk = 34 sourceSets { getByName("main") { diff --git a/android/src/androidTest/java/io/ably/lib/test/RetryTestRule.java b/android/src/androidTest/java/io/ably/lib/test/RetryTestRule.java new file mode 100644 index 000000000..6584ae3b0 --- /dev/null +++ b/android/src/androidTest/java/io/ably/lib/test/RetryTestRule.java @@ -0,0 +1,49 @@ +package io.ably.lib.test; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + + +public class RetryTestRule implements TestRule { + + private final int timesToRunTestCount; + + /** + * If `times` is 0, then we should run the test once. + */ + public RetryTestRule(int times) { + this.timesToRunTestCount = times + 1; + } + + @Override + public Statement apply(Statement base, Description description) { + return statement(base, description); + } + + private Statement statement(Statement base, Description description) { + return new Statement() { + + @Override + public void evaluate() throws Throwable { + Throwable latestException = null; + + for (int runCount = 0; runCount < timesToRunTestCount; runCount++) { + try { + base.evaluate(); + return; + } catch (Throwable t) { + latestException = t; + System.err.printf("%s: test failed on run: `%d`. Will run a maximum of `%d` times.%n", description.getDisplayName(), runCount, timesToRunTestCount); + t.printStackTrace(); + } + } + + if (latestException != null) { + System.err.printf("%s: giving up after `%d` failures%n", description.getDisplayName(), timesToRunTestCount); + throw latestException; + } + } + }; + } +} diff --git a/android/src/androidTest/java/io/ably/lib/test/android/AndroidPushTest.java b/android/src/androidTest/java/io/ably/lib/test/android/AndroidPushTest.java index 3ecd0407c..6efcc83f1 100644 --- a/android/src/androidTest/java/io/ably/lib/test/android/AndroidPushTest.java +++ b/android/src/androidTest/java/io/ably/lib/test/android/AndroidPushTest.java @@ -6,6 +6,7 @@ import android.content.IntentFilter; import android.os.Build; import android.preference.PreferenceManager; +import android.support.test.filters.SdkSuppress; import android.support.test.runner.AndroidJUnit4; import android.util.Log; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @@ -41,6 +42,7 @@ import io.ably.lib.rest.Auth; import io.ably.lib.rest.Channel; import io.ably.lib.rest.DeviceDetails; +import io.ably.lib.test.RetryTestRule; import io.ably.lib.test.common.Helpers; import io.ably.lib.test.common.Helpers.AsyncWaiter; import io.ably.lib.test.common.Helpers.CompletionWaiter; @@ -60,6 +62,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -80,12 +83,14 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; @RunWith(AndroidJUnit4.class) public class AndroidPushTest { private static final int TIMEOUT_SECONDS = 30; + @Rule + public RetryTestRule retryRule = new RetryTestRule(2); + private class TestActivation { private Helpers.RawHttpTracker httpTracker; private AblyRest rest; @@ -975,8 +980,8 @@ protected void setUpMachineState(TestCase testCase) throws AblyException { // RSH3d3 @Test + @SdkSuppress(minSdkVersion = 21) public void WaitingForNewPushDeviceDetails_on_GotPushDeviceDetails() throws Exception { - assumeTrue("Can only run on API Level 21 or newer because HttpURLConnection does not support PATCH", Build.VERSION.SDK_INT >= 21); new UpdateRegistrationTest() { @Override protected void setUpMachineState(TestCase testCase) throws AblyException { @@ -1435,6 +1440,7 @@ public void run() throws Exception { } @Test + @SdkSuppress(minSdkVersion = 21) public void Realtime_push_interface() throws Exception { AblyRealtime realtime = new AblyRealtime(new ClientOptions() {{ autoConnect = false; diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 542072c78..baa16e88f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,7 +12,7 @@ concurrentunit = "0.4.2" slf4j = "1.7.30" build-config = "5.4.0" firebase-messaging = "22.0.0" -android-test = "0.5" +android-test = "1.0.2" dexmaker = "1.4" android-retrostreams = "1.7.4" maven-publish = "0.29.0"