From 271f09ac347d638333aac5a2e9655086b4d22df7 Mon Sep 17 00:00:00 2001 From: Alexande B Date: Wed, 24 May 2023 16:20:56 +0200 Subject: [PATCH 01/16] feat(test): move tests to separate module to run them against obfuscated sdk --- .github/workflows/ci.yml | 2 +- MAINTAINERS.md | 13 ++ benchmark/build.gradle | 4 +- gradle.properties | 2 + sdk/build.gradle | 24 +-- sdk/consumer-rules.pro | 2 + sdk/proguard-rules.pro | 3 - sdk/src/androidTest/AndroidManifest.xml | 7 - .../hcaptcha/sdk/ExampleInstrumentedTest.java | 25 --- .../java/com/hcaptcha/sdk/TestActivity.java | 6 - .../hcaptcha/sdk/HCaptchaDialogFragment.java | 2 +- settings.gradle | 5 +- test/build.gradle | 70 ++++++++ test/proguard-rules.pro | 15 ++ test/src/androidTest/AndroidManifest.xml | 16 ++ .../java/com/hcaptcha/sdk/AssertUtil.java | 1 + .../sdk/HCaptchaDialogFragmentTest.java | 6 +- .../sdk/HCaptchaHeadlessWebViewTest.java | 3 + .../sdk/HCaptchaStateTestAdapter.java | 0 .../java/com/hcaptcha/sdk/HCaptchaTest.java | 2 + .../com/hcaptcha/sdk/HCaptchaTestHtml.java | 0 test/src/main/AndroidManifest.xml | 21 +++ .../com/hcaptcha/sdk/test/TestActivity.java | 4 + .../res/drawable/ic_launcher_background.xml | 170 ++++++++++++++++++ .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 24126 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 34163 bytes test/src/main/res/values/strings.xml | 3 + 27 files changed, 340 insertions(+), 66 deletions(-) delete mode 100644 sdk/src/androidTest/AndroidManifest.xml delete mode 100644 sdk/src/androidTest/java/com/hcaptcha/sdk/ExampleInstrumentedTest.java delete mode 100644 sdk/src/androidTest/java/com/hcaptcha/sdk/TestActivity.java create mode 100644 test/build.gradle create mode 100644 test/proguard-rules.pro create mode 100644 test/src/androidTest/AndroidManifest.xml rename {sdk => test}/src/androidTest/java/com/hcaptcha/sdk/AssertUtil.java (99%) rename {sdk => test}/src/androidTest/java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java (98%) rename {sdk => test}/src/androidTest/java/com/hcaptcha/sdk/HCaptchaHeadlessWebViewTest.java (99%) rename {sdk => test}/src/androidTest/java/com/hcaptcha/sdk/HCaptchaStateTestAdapter.java (100%) rename {sdk => test}/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java (99%) rename {sdk => test}/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTestHtml.java (100%) create mode 100644 test/src/main/AndroidManifest.xml create mode 100644 test/src/main/java/com/hcaptcha/sdk/test/TestActivity.java create mode 100644 test/src/main/res/drawable/ic_launcher_background.xml create mode 100644 test/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 test/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 test/src/main/res/values/strings.xml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bdde3c48..f501e9f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,7 +120,7 @@ jobs: profile: Nexus 6 script: | brew install parallel - parallel --retries 3 ::: "./gradlew sdk:connectedCheck" + parallel --retries 3 ::: "./gradlew test:connectedCheck -PtestingMinimizedBuild=true" - if: failure() uses: actions/upload-artifact@v3 with: diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 7703a48f..8ee010f6 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -10,6 +10,19 @@ You can manually test before pushing by running both unit tests and instrumented * ```gradlew test``` * ```gradlew connectedDebugAndroidTest``` +## Manual testing (full) + ++ {normal,invisible,compact} -> verify -> success -> mark used ++ {normal,invisible,compact} -> verify -> success -> token timeout + ++ {normal,invisible,compact} -> verify -> touch outside -> challenge closed ++ {normal,invisible,compact} -> verify -> back button -> challenge closed + ++ {normal,invisible,compact} -> verify -> rotate device (recreate activity) -> hcaptcha gone, no callbacks fired ++ {normal,invisible,compact} -> verify -> send app to background -> open app from history again -> hcaptcha is displayed + ++ {hide dialog} -> verify -> token obtained -> mark used + # Publishing To publish a new version follow the next steps: diff --git a/benchmark/build.gradle b/benchmark/build.gradle index 0c487b3c..ee602874 100644 --- a/benchmark/build.gradle +++ b/benchmark/build.gradle @@ -36,8 +36,8 @@ android { } dependencies { - androidTestImplementation 'androidx.test:runner:1.5.1' - androidTestImplementation 'androidx.test.ext:junit:1.1.4' + androidTestImplementation 'androidx.test:runner:1.5.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.benchmark:benchmark-junit4:1.1.1' diff --git a/gradle.properties b/gradle.properties index a7680f1f..710b2f09 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,3 +19,5 @@ android.useAndroidX=true android.enableJetifier=true # Software Components will not be created automatically for Maven publishing from Android Gradle Plugin 8.0 android.disableAutomaticComponentCreation=true +# To test more aggressive optimizations +android.enableR8.fullMode=true \ No newline at end of file diff --git a/sdk/build.gradle b/sdk/build.gradle index 9ea14427..a0cde7a6 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -1,15 +1,14 @@ plugins { + id "com.android.library" id "pmd" id "jacoco" id "checkstyle" + id "maven-publish" id "com.github.spotbugs" version "4.8.0" id "org.owasp.dependencycheck" version "7.1.1" id "org.sonarqube" version "3.4.0.2513" } -apply plugin: 'com.android.library' -apply plugin: 'maven-publish' - android { compileSdkVersion 33 buildToolsVersion "30.0.3" @@ -30,10 +29,8 @@ android { buildConfigField 'String', 'VERSION_NAME', "\"${defaultConfig.versionName}_${defaultConfig.versionCode}\"" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - multiDexEnabled true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt') } compileOptions { @@ -43,6 +40,8 @@ android { } buildTypes { + debug { + } release { minifyEnabled true } @@ -65,7 +64,6 @@ android { } dependencies { - implementation fileTree(dir: "libs", include: ["*.jar"]) //noinspection GradleDependency implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3' @@ -76,17 +74,6 @@ dependencies { testImplementation 'org.mockito:mockito-inline:3.6.28' testImplementation 'org.skyscreamer:jsonassert:1.5.0' - androidTestImplementation 'androidx.test:core:1.5.0' - androidTestImplementation 'androidx.test:runner:1.5.2' - androidTestImplementation 'androidx.test:rules:1.5.0' - androidTestImplementation 'androidx.test.ext:junit:1.1.5' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - androidTestImplementation 'androidx.test.espresso:espresso-web:3.5.1' - androidTestImplementation 'androidx.fragment:fragment-testing:1.4.1' // cannot upgrade because https://issuetracker.google.com/issues/266687550 - - androidTestImplementation "androidx.multidex:multidex:2.0.1" - androidTestImplementation "org.mockito:mockito-android:3.6.28" - compileOnly 'com.google.code.findbugs:annotations:3.0.1' } @@ -300,3 +287,4 @@ sonarqube { } project.tasks["sonarqube"].dependsOn "check" + diff --git a/sdk/consumer-rules.pro b/sdk/consumer-rules.pro index 903bd613..2c70114f 100644 --- a/sdk/consumer-rules.pro +++ b/sdk/consumer-rules.pro @@ -4,3 +4,5 @@ # Prevent obfuscating the names when serializing to JSON -keep class com.hcaptcha.sdk.HCaptchaConfig { *; } + +-keepclasseswithmembernames public enum com.hcaptcha.sdk.** { *; } diff --git a/sdk/proguard-rules.pro b/sdk/proguard-rules.pro index 3935452c..b19b3096 100644 --- a/sdk/proguard-rules.pro +++ b/sdk/proguard-rules.pro @@ -4,6 +4,3 @@ # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html - -# Keep all sdk classes --keep class com.hcaptcha.sdk.** { *; } diff --git a/sdk/src/androidTest/AndroidManifest.xml b/sdk/src/androidTest/AndroidManifest.xml deleted file mode 100644 index f3d6cf30..00000000 --- a/sdk/src/androidTest/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/sdk/src/androidTest/java/com/hcaptcha/sdk/ExampleInstrumentedTest.java b/sdk/src/androidTest/java/com/hcaptcha/sdk/ExampleInstrumentedTest.java deleted file mode 100644 index 7fc12d44..00000000 --- a/sdk/src/androidTest/java/com/hcaptcha/sdk/ExampleInstrumentedTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.hcaptcha.sdk; - -import static org.junit.Assert.assertEquals; - -import android.content.Context; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.platform.app.InstrumentationRegistry; - -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() { - // Context of the app under test. - final Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - assertEquals("com.hcaptcha.sdk.test", appContext.getPackageName()); - } -} diff --git a/sdk/src/androidTest/java/com/hcaptcha/sdk/TestActivity.java b/sdk/src/androidTest/java/com/hcaptcha/sdk/TestActivity.java deleted file mode 100644 index 64d17547..00000000 --- a/sdk/src/androidTest/java/com/hcaptcha/sdk/TestActivity.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.hcaptcha.sdk; - -import androidx.fragment.app.FragmentActivity; - -public class TestActivity extends FragmentActivity { -} diff --git a/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaDialogFragment.java b/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaDialogFragment.java index ef3acb47..17cd6938 100644 --- a/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaDialogFragment.java +++ b/sdk/src/main/java/com/hcaptcha/sdk/HCaptchaDialogFragment.java @@ -121,7 +121,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, loadingContainer.setVisibility(config.getLoading() ? View.VISIBLE : View.GONE); webViewHelper = new HCaptchaWebViewHelper(new Handler(Looper.getMainLooper()), requireContext(), config, internalConfig, this, listener, webView); - } catch (BadParcelableException | InflateException | ClassCastException e) { + } catch (AssertionError | BadParcelableException | InflateException | ClassCastException e) { HCaptchaLog.w("Cannot create view. Dismissing dialog..."); // Happens when fragment tries to reconstruct because the activity was killed // And thus there is no way of communicating back diff --git a/settings.gradle b/settings.gradle index 96e2fce7..892e2df7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,5 @@ -include ':example-app' -include ':sdk' rootProject.name = "hcaptcha-android-sdk" +include ':sdk' +include ':test' include ':benchmark' +include ':example-app' diff --git a/test/build.gradle b/test/build.gradle new file mode 100644 index 00000000..818e93f6 --- /dev/null +++ b/test/build.gradle @@ -0,0 +1,70 @@ +apply plugin: 'com.android.application' + +//if (project.hasProperty("testingMinimizedBuild")) { +// apply plugin: 'com.slack.keeper' +// +// keeper { +// r8JvmArgs = [] +// } +//} + +android { + compileSdkVersion 33 + buildToolsVersion "30.0.3" + namespace 'com.hcaptcha.sdk.test' + + defaultConfig { + applicationId "com.hcaptcha.sdk.test" + minSdkVersion 16 + targetSdkVersion 33 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + debug {} + release { + signingConfig signingConfigs.debug + minifyEnabled true + shrinkResources true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + if (project.hasProperty("testingMinimizedBuild")) { + testBuildType "release" + +// androidComponents { +// beforeVariants(selector().all()) { builder -> +// builder.registerExtension(com.slack.keeper.KeeperVariantMarker.class, com.slack.keeper.KeeperVariantMarker.INSTANCE) +// } +// } + } +} + +dependencies { + implementation project(path: ':sdk') + + implementation 'androidx.appcompat:appcompat:1.3.1' + //noinspection FragmentGradleConfiguration + implementation ('androidx.fragment:fragment-testing:1.5.7') { // noinspection FragmentGradleConfiguration + exclude group: 'androidx.test', module: 'core' + } + + testImplementation 'junit:junit:4.13.2' + + androidTestImplementation 'androidx.test:core:1.5.0' + androidTestImplementation 'androidx.test:runner:1.5.2' + androidTestImplementation 'androidx.test:rules:1.5.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + androidTestImplementation 'androidx.test.espresso:espresso-web:3.5.1' + androidTestImplementation 'org.mockito:mockito-core:4.8.1' +} \ No newline at end of file diff --git a/test/proguard-rules.pro b/test/proguard-rules.pro new file mode 100644 index 00000000..dfb683e5 --- /dev/null +++ b/test/proguard-rules.pro @@ -0,0 +1,15 @@ +-keep public class androidx.tracing.Trace { + *; +} +# +#-keep public class com.hcaptcha.sdk.test.** { +# *; +#} +# +## -dontobfuscate +# +#-keepclassmembers @org.junit.runner.RunWith public class ** { +# @org.junit.runner.Test public *; +#} + +-keep class com.hcaptcha.sdk.** \ No newline at end of file diff --git a/test/src/androidTest/AndroidManifest.xml b/test/src/androidTest/AndroidManifest.xml new file mode 100644 index 00000000..6a0244e1 --- /dev/null +++ b/test/src/androidTest/AndroidManifest.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sdk/src/androidTest/java/com/hcaptcha/sdk/AssertUtil.java b/test/src/androidTest/java/com/hcaptcha/sdk/AssertUtil.java similarity index 99% rename from sdk/src/androidTest/java/com/hcaptcha/sdk/AssertUtil.java rename to test/src/androidTest/java/com/hcaptcha/sdk/AssertUtil.java index c937dfdf..0877d4f0 100644 --- a/sdk/src/androidTest/java/com/hcaptcha/sdk/AssertUtil.java +++ b/test/src/androidTest/java/com/hcaptcha/sdk/AssertUtil.java @@ -18,6 +18,7 @@ import android.view.View; import android.webkit.ValueCallback; import android.webkit.WebView; + import androidx.test.espresso.PerformException; import androidx.test.espresso.UiController; import androidx.test.espresso.ViewAction; diff --git a/sdk/src/androidTest/java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java similarity index 98% rename from sdk/src/androidTest/java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java rename to test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java index 1f7be016..88242456 100644 --- a/sdk/src/androidTest/java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java +++ b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java @@ -27,6 +27,7 @@ import android.os.Bundle; import android.view.InflateException; import android.view.LayoutInflater; + import androidx.fragment.app.testing.FragmentScenario; import androidx.lifecycle.Lifecycle; import androidx.test.core.app.ActivityScenario; @@ -34,8 +35,11 @@ import androidx.test.espresso.web.webdriver.Locator; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.hcaptcha.sdk.test.TestActivity; + import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -186,7 +190,7 @@ void onFailure(HCaptchaException exception) { @Test public void webViewNotInstalled() throws InterruptedException { final LayoutInflater inflater = mock(LayoutInflater.class); - when(inflater.inflate(eq(R.layout.hcaptcha_fragment), any(), eq(false))) + when(inflater.inflate(ArgumentMatchers.eq(R.layout.hcaptcha_fragment), any(), eq(false))) .thenThrow(InflateException.class); final CountDownLatch latch = new CountDownLatch(1); diff --git a/sdk/src/androidTest/java/com/hcaptcha/sdk/HCaptchaHeadlessWebViewTest.java b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaHeadlessWebViewTest.java similarity index 99% rename from sdk/src/androidTest/java/com/hcaptcha/sdk/HCaptchaHeadlessWebViewTest.java rename to test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaHeadlessWebViewTest.java index 9dc2073d..a9d1efb6 100644 --- a/sdk/src/androidTest/java/com/hcaptcha/sdk/HCaptchaHeadlessWebViewTest.java +++ b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaHeadlessWebViewTest.java @@ -8,11 +8,14 @@ import android.view.View; import android.view.ViewGroup; + import androidx.annotation.NonNull; import androidx.test.core.app.ActivityScenario; import androidx.test.ext.junit.rules.ActivityScenarioRule; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.hcaptcha.sdk.test.TestActivity; + import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/sdk/src/androidTest/java/com/hcaptcha/sdk/HCaptchaStateTestAdapter.java b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaStateTestAdapter.java similarity index 100% rename from sdk/src/androidTest/java/com/hcaptcha/sdk/HCaptchaStateTestAdapter.java rename to test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaStateTestAdapter.java diff --git a/sdk/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java similarity index 99% rename from sdk/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java rename to test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java index 102dc9cd..cd32ab2b 100644 --- a/sdk/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java +++ b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java @@ -9,6 +9,8 @@ import androidx.test.ext.junit.rules.ActivityScenarioRule; import com.hcaptcha.sdk.tasks.OnSuccessListener; +import com.hcaptcha.sdk.test.TestActivity; + import org.junit.Rule; import org.junit.Test; diff --git a/sdk/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTestHtml.java b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTestHtml.java similarity index 100% rename from sdk/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTestHtml.java rename to test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTestHtml.java diff --git a/test/src/main/AndroidManifest.xml b/test/src/main/AndroidManifest.xml new file mode 100644 index 00000000..26f851f4 --- /dev/null +++ b/test/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/src/main/java/com/hcaptcha/sdk/test/TestActivity.java b/test/src/main/java/com/hcaptcha/sdk/test/TestActivity.java new file mode 100644 index 00000000..0287f9a1 --- /dev/null +++ b/test/src/main/java/com/hcaptcha/sdk/test/TestActivity.java @@ -0,0 +1,4 @@ +package com.hcaptcha.sdk.test; + +public class TestActivity extends androidx.appcompat.app.AppCompatActivity { +} \ No newline at end of file diff --git a/test/src/main/res/drawable/ic_launcher_background.xml b/test/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..07d5da9c --- /dev/null +++ b/test/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/test/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..317d6ccf4b0bea35d2e557b57d5742c3901724f5 GIT binary patch literal 24126 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 B?zaE{ literal 0 HcmV?d00001 diff --git a/test/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/test/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..b45165c8bcab70336190adcf6cf34171188abffb GIT binary patch literal 34163 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 + test + \ No newline at end of file From 8dc030447215b97b9f768ff14cae65f94552395f Mon Sep 17 00:00:00 2001 From: Alexande B Date: Sun, 3 Sep 2023 10:11:38 +0200 Subject: [PATCH 02/16] Fix sonar sonar.tests property after moving tests to separate module --- sdk/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/build.gradle b/sdk/build.gradle index 49cbd95f..c00fc47e 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -276,7 +276,7 @@ sonarqube { property "sonar.sources", "src/main" property "sonar.java.binaries", "${project.buildDir}/intermediates/javac/debug/classes" - property "sonar.tests", ["src/test/", "src/androidTest/"] + property "sonar.tests", ["src/test/", "../test/src/androidTest/"] property "sonar.android.lint.report", "${project.buildDir}/outputs/lint-results.xml" property "sonar.java.spotbugs.reportPaths", ["${project.buildDir}/reports/spotbugs/debug.xml", "${project.buildDir}/reports/spotbugs/release.xml"] From 6c08f278940e95de04a24fd14dceecdaa87e039e Mon Sep 17 00:00:00 2001 From: Alexande B Date: Tue, 14 Nov 2023 12:04:51 +0100 Subject: [PATCH 03/16] chore: upgrade android gradle plugin to 8.1.3 --- .github/workflows/ci.yml | 15 +++++++----- benchmark/build.gradle | 8 +++---- build.gradle | 4 ++-- example-app/build.gradle | 1 - .../main/java/com/hcaptcha/example/App.java | 2 +- gradle.properties | 2 -- gradle/wrapper/gradle-wrapper.properties | 2 +- sdk/build.gradle | 23 +++++++++++-------- sdk/consumer-rules.pro | 4 ++++ .../java/com/hcaptcha/sdk/HCaptchaTest.java | 12 +++++----- 10 files changed, 40 insertions(+), 33 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 02a5f622..78e741c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,9 @@ on: paths-ignore: - '**.md' +env: + JAVA_VERSION: '17' + jobs: test: name: 'Test Unit' @@ -21,7 +24,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: - java-version: '11' + java-version: ${{ env.JAVA_VERSION }} distribution: adopt - uses: gradle/gradle-build-action@v2 with: @@ -60,7 +63,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: - java-version: '11' + java-version: ${{ env.JAVA_VERSION }} distribution: adopt - uses: gradle/gradle-build-action@v2 with: @@ -82,7 +85,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: - java-version: '11' + java-version: ${{ env.JAVA_VERSION }} distribution: adopt - uses: gradle/gradle-build-action@v2 with: @@ -140,7 +143,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: - java-version: '11' + java-version: ${{ env.JAVA_VERSION }} distribution: adopt - uses: gradle/gradle-build-action@v2 with: @@ -215,7 +218,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-java@v3 with: - java-version: '11' + java-version: ${{ env.JAVA_VERSION }} distribution: adopt - uses: gradle/gradle-build-action@v2 with: @@ -241,7 +244,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-java@v3 with: - java-version: '11' + java-version: ${{ env.JAVA_VERSION }} distribution: adopt - uses: gradle/gradle-build-action@v2 with: diff --git a/benchmark/build.gradle b/benchmark/build.gradle index ee602874..beae395e 100644 --- a/benchmark/build.gradle +++ b/benchmark/build.gradle @@ -4,7 +4,7 @@ plugins { } android { - compileSdkVersion 33 + compileSdkVersion 34 namespace 'com.hcaptcha.sdk.bench' compileOptions { @@ -14,7 +14,7 @@ android { defaultConfig { minSdkVersion 16 - targetSdkVersion 33 + targetSdkVersion 34 testInstrumentationRunner 'androidx.benchmark.junit4.AndroidBenchmarkRunner' testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE,EMULATOR,LOW-BATTERY,UNLOCKED" @@ -39,10 +39,10 @@ dependencies { androidTestImplementation 'androidx.test:runner:1.5.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.benchmark:benchmark-junit4:1.1.1' + androidTestImplementation 'androidx.benchmark:benchmark-junit4:1.2.0' implementation project(path: ':sdk') - implementation 'androidx.appcompat:appcompat:1.5.1' + implementation 'androidx.appcompat:appcompat:1.6.1' } // Workaround for: java.lang.ClassNotFoundException: com.android.tools.lint.client.api.Vendor diff --git a/build.gradle b/build.gradle index 562958a8..953b8fde 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,8 @@ buildscript { maven { url 'https://jitpack.io' } } dependencies { - classpath 'com.android.tools.build:gradle:7.2.2' - classpath 'androidx.benchmark:benchmark-gradle-plugin:1.1.1' + classpath 'com.android.tools.build:gradle:8.1.3' + classpath 'androidx.benchmark:benchmark-gradle-plugin:1.2.0' } } diff --git a/example-app/build.gradle b/example-app/build.gradle index dc4c3f08..9a5a1076 100644 --- a/example-app/build.gradle +++ b/example-app/build.gradle @@ -10,7 +10,6 @@ def prop(name, fallback) { android { compileSdkVersion intProp("exampleCompileSdkVersion", 32) - buildToolsVersion "30.0.3" namespace 'com.hcaptcha.example' defaultConfig { diff --git a/example-app/src/main/java/com/hcaptcha/example/App.java b/example-app/src/main/java/com/hcaptcha/example/App.java index d783b16a..28bc8ff8 100644 --- a/example-app/src/main/java/com/hcaptcha/example/App.java +++ b/example-app/src/main/java/com/hcaptcha/example/App.java @@ -8,7 +8,7 @@ public class App extends Application { public void onCreate() { super.onCreate(); - if (BuildConfig.DEBUG) { + if (com.hcaptcha.sdk.BuildConfig.DEBUG) { StrictMode.setThreadPolicy( new StrictMode.ThreadPolicy.Builder() .detectAll() diff --git a/gradle.properties b/gradle.properties index 710b2f09..2b1e584a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,5 @@ org.gradle.jvmargs=-Xmx2048m android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true -# Software Components will not be created automatically for Maven publishing from Android Gradle Plugin 8.0 -android.disableAutomaticComponentCreation=true # To test more aggressive optimizations android.enableR8.fullMode=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae04661e..84a0b92f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/sdk/build.gradle b/sdk/build.gradle index c00fc47e..2407aa63 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -4,16 +4,19 @@ plugins { id "jacoco" id "checkstyle" id "maven-publish" - id "com.github.spotbugs" version "4.8.0" + id "com.github.spotbugs" version "5.2.3" id "org.owasp.dependencycheck" version "7.1.1" id "org.sonarqube" version "3.4.0.2513" } android { compileSdkVersion 33 - buildToolsVersion "30.0.3" namespace 'com.hcaptcha.sdk' + buildFeatures { + buildConfig true + } + defaultConfig { minSdkVersion 16 targetSdkVersion 33 @@ -67,12 +70,12 @@ dependencies { //noinspection GradleDependency implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3' - compileOnly 'org.projectlombok:lombok:1.18.16' - annotationProcessor 'org.projectlombok:lombok:1.18.16' + compileOnly 'org.projectlombok:lombok:1.18.30' + annotationProcessor 'org.projectlombok:lombok:1.18.30' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-inline:3.6.28' - testImplementation 'org.skyscreamer:jsonassert:1.5.0' + testImplementation 'org.mockito:mockito-inline:4.8.1' + testImplementation 'org.skyscreamer:jsonassert:1.5.1' compileOnly 'com.google.code.findbugs:annotations:3.0.1' } @@ -231,8 +234,8 @@ spotbugs { gradle.taskGraph.beforeTask { task -> if (task.name.toLowerCase().contains('spotbugs')) { task.reports { - html.enabled = true - xml.enabled = true + html.enabled true + xml.enabled true } } } @@ -258,8 +261,8 @@ task jacocoUnitTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) executionData.from = "${project.buildDir}/jacoco/testDebugUnitTest.exec" reports { - xml.enabled = true - html.enabled = true + xml.required = true + html.required = true } } diff --git a/sdk/consumer-rules.pro b/sdk/consumer-rules.pro index 8d5fa2b0..d08a981c 100644 --- a/sdk/consumer-rules.pro +++ b/sdk/consumer-rules.pro @@ -1,6 +1,10 @@ -dontwarn javax.annotation.processing.** -dontwarn lombok.core.configuration.** -dontwarn org.apache.tools.** +-dontwarn android.content.pm.PackageManager$ApplicationInfoFlags +-dontwarn edu.umd.cs.findbugs.annotations.* +-dontwarn java.beans.* +-dontwarn lombok.* # Prevent obfuscating the names when serializing to JSON -keep class com.hcaptcha.sdk.HCaptchaConfig { *; } diff --git a/sdk/src/test/java/com/hcaptcha/sdk/HCaptchaTest.java b/sdk/src/test/java/com/hcaptcha/sdk/HCaptchaTest.java index ac5136d5..620dd902 100644 --- a/sdk/src/test/java/com/hcaptcha/sdk/HCaptchaTest.java +++ b/sdk/src/test/java/com/hcaptcha/sdk/HCaptchaTest.java @@ -125,11 +125,11 @@ public void test_verify_with_hcaptcha_passes_site_key_as_config() { final String siteKey = HCaptchaConfigTest.MOCK_SITE_KEY; final HCaptcha hCaptcha = HCaptcha.getClient(fragmentActivity); - dialogFragmentMock.verify(never(), () -> + dialogFragmentMock.verify(() -> HCaptchaDialogFragment.newInstance( any(HCaptchaConfig.class), any(HCaptchaInternalConfig.class), - any(HCaptchaStateListener.class))); + any(HCaptchaStateListener.class)), never()); hCaptcha.verifyWithHCaptcha(siteKey); @@ -199,11 +199,11 @@ public void test_verify_config_has_priority_over_setup_config() throws Exception .verifyWithHCaptcha(verifyConfig); verify(packageManager, never()).getApplicationInfo(any(String.class), anyInt()); - dialogFragmentMock.verify(times(2), () -> + dialogFragmentMock.verify(() -> HCaptchaDialogFragment.newInstance( hCaptchaConfigCaptor.capture(), any(HCaptchaInternalConfig.class), - any(HCaptchaStateListener.class))); + any(HCaptchaStateListener.class)), times(2)); assertEquals(verifyConfig, hCaptchaConfigCaptor.getValue()); } @@ -216,11 +216,11 @@ public void test_verify_site_key_has_priority_over_setup_config() throws Excepti .verifyWithHCaptcha(siteKey); verify(packageManager, never()).getApplicationInfo(any(String.class), anyInt()); - dialogFragmentMock.verify(times(2), () -> + dialogFragmentMock.verify(() -> HCaptchaDialogFragment.newInstance( hCaptchaConfigCaptor.capture(), any(HCaptchaInternalConfig.class), - any(HCaptchaStateListener.class))); + any(HCaptchaStateListener.class)), times(2)); assertEquals(siteKey, hCaptchaConfigCaptor.getValue().getSiteKey()); } From c87d6f50d19f890727a515e122f95501150d8003 Mon Sep 17 00:00:00 2001 From: Alexande B Date: Tue, 14 Nov 2023 14:13:58 +0100 Subject: [PATCH 04/16] ci: add androidTests for minified build --- .github/workflows/ci.yml | 60 ++++++++++++++++++- build.gradle | 1 + test/.gitignore | 1 + test/build.gradle | 46 ++++++-------- test/proguard-rules.pro | 32 +++++----- test/src/androidTest/AndroidManifest.xml | 16 ----- .../sdk/HCaptchaDialogFragmentTest.java | 14 +++-- .../java/com/hcaptcha/sdk/HCaptchaTest.java | 2 +- test/src/main/AndroidManifest.xml | 9 +++ 9 files changed, 117 insertions(+), 64 deletions(-) create mode 100644 test/.gitignore delete mode 100644 test/src/androidTest/AndroidManifest.xml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78e741c0..6d942abf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -122,7 +122,7 @@ jobs: profile: Nexus 6 script: | brew install parallel - parallel --retries 3 ::: "./gradlew test:connectedCheck -PtestingMinimizedBuild=true" + parallel --retries 3 ::: "./gradlew test:connectedCheck" - if: failure() uses: actions/upload-artifact@v3 with: @@ -131,6 +131,64 @@ jobs: sdk/build/outputs/androidTest-results sdk/build/reports/androidTests + test-minified: + name: 'Test UI Minified' + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + api-level: [29] + target: [default] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + java-version: ${{ env.JAVA_VERSION }} + distribution: adopt + - uses: gradle/gradle-build-action@v2 + with: + cache-read-only: false + - name: 'Cache AVD' + uses: actions/cache@v3 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-api-${{ matrix.api-level }}-target-${{ matrix.target }} + - name: 'Create AVD' + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.api-level }} + target: ${{ matrix.target }} + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + force-avd-creation: false + disable-animations: false + arch: x86_64 + profile: Nexus 6 + script: echo "Generated AVD snapshot for caching." + - name: 'Tests' + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.api-level }} + target: ${{ matrix.target }} + emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + force-avd-creation: false + disable-animations: true + arch: x86_64 + profile: Nexus 6 + script: | + brew install parallel + parallel --retries 3 ::: "./gradlew test:connectedCheck -P testingMinimizedBuild=true -P android.enableR8.fullMode=false" + - if: failure() + uses: actions/upload-artifact@v3 + with: + name: androidTest-minified-results + path: | + sdk/build/outputs/androidTest-results + sdk/build/reports/androidTests + test-benchmark: name: 'Test Benchmark' runs-on: macos-latest diff --git a/build.gradle b/build.gradle index 953b8fde..292133d2 100644 --- a/build.gradle +++ b/build.gradle @@ -8,6 +8,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:8.1.3' classpath 'androidx.benchmark:benchmark-gradle-plugin:1.2.0' + classpath 'com.slack.keeper:keeper:0.15.0' } } diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/test/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/test/build.gradle b/test/build.gradle index 818e93f6..c2c52ca0 100644 --- a/test/build.gradle +++ b/test/build.gradle @@ -1,21 +1,16 @@ apply plugin: 'com.android.application' - -//if (project.hasProperty("testingMinimizedBuild")) { -// apply plugin: 'com.slack.keeper' -// -// keeper { -// r8JvmArgs = [] -// } -//} +if (project.hasProperty("testingMinimizedBuild")) { + project.setProperty("android.enableR8.fullMode", false) + apply plugin: 'com.slack.keeper' +} android { compileSdkVersion 33 - buildToolsVersion "30.0.3" namespace 'com.hcaptcha.sdk.test' defaultConfig { applicationId "com.hcaptcha.sdk.test" - minSdkVersion 16 + minSdkVersion 19 targetSdkVersion 33 versionCode 1 versionName "1.0" @@ -24,12 +19,11 @@ android { } buildTypes { - debug {} release { signingConfig signingConfigs.debug minifyEnabled true shrinkResources true - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro', '../sdk/consumer-rules.pro' } } @@ -38,33 +32,31 @@ android { targetCompatibility JavaVersion.VERSION_1_8 } - if (project.hasProperty("testingMinimizedBuild")) { - testBuildType "release" + testBuildType = project.hasProperty("testingMinimizedBuild") ? "release" : "debug" +} -// androidComponents { -// beforeVariants(selector().all()) { builder -> -// builder.registerExtension(com.slack.keeper.KeeperVariantMarker.class, com.slack.keeper.KeeperVariantMarker.INSTANCE) -// } -// } +androidComponents { + beforeVariants(selector().all()) { variantBuilder -> + if (variantBuilder.name == "release") { + variantBuilder.registerExtension( + com.slack.keeper.KeeperVariantMarker.class, + com.slack.keeper.KeeperVariantMarker.INSTANCE) + } } } dependencies { implementation project(path: ':sdk') - - implementation 'androidx.appcompat:appcompat:1.3.1' - //noinspection FragmentGradleConfiguration - implementation ('androidx.fragment:fragment-testing:1.5.7') { // noinspection FragmentGradleConfiguration - exclude group: 'androidx.test', module: 'core' - } + implementation 'androidx.appcompat:appcompat:1.6.1' testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.fragment:fragment-testing:1.6.2' androidTestImplementation 'androidx.test:core:1.5.0' - androidTestImplementation 'androidx.test:runner:1.5.2' androidTestImplementation 'androidx.test:rules:1.5.0' + androidTestImplementation 'androidx.test:runner:1.5.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation 'androidx.test.espresso:espresso-web:3.5.1' - androidTestImplementation 'org.mockito:mockito-core:4.8.1' + androidTestImplementation 'org.mockito:mockito-android:5.3.1' } \ No newline at end of file diff --git a/test/proguard-rules.pro b/test/proguard-rules.pro index dfb683e5..2a143c68 100644 --- a/test/proguard-rules.pro +++ b/test/proguard-rules.pro @@ -1,15 +1,19 @@ --keep public class androidx.tracing.Trace { - *; +# Proguard rules that are applied to your test apk/code. +-ignorewarnings + +# -keepattributes *Annotation* +-keepattributes Signature + +#-keep class androidx.fragment.app.testing.** { *; } + +-assumenosideeffects class com.hcaptcha.sdk.HCaptchaDialogFragmentTest { + *** webViewNotInstalled(...); } -# -#-keep public class com.hcaptcha.sdk.test.** { -# *; -#} -# -## -dontobfuscate -# -#-keepclassmembers @org.junit.runner.RunWith public class ** { -# @org.junit.runner.Test public *; -#} - --keep class com.hcaptcha.sdk.** \ No newline at end of file + +# rules to keep compiller happy with android.enableR8.fullMode=true + +-keepnames class android.test.** { *; } +-keep class androidx.lifecycle.Lifecycle$Event$Companion { *; } +-keep class androidx.lifecycle.** { *; } +-keep class com.google.errorprone.annotations.MustBeClosed { *; } +-dontwarn com.sun.jna.** diff --git a/test/src/androidTest/AndroidManifest.xml b/test/src/androidTest/AndroidManifest.xml deleted file mode 100644 index 6a0244e1..00000000 --- a/test/src/androidTest/AndroidManifest.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java index b1edab69..8812a910 100644 --- a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java +++ b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java @@ -43,6 +43,7 @@ import com.hcaptcha.sdk.test.TestActivity; +import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; @@ -220,6 +221,8 @@ void onFailure(HCaptchaException exception) { @Test public void webViewNotInstalled() throws InterruptedException { + Assume.assumeTrue("Skip test for release, because impossible to mock LayoutInflater", BuildConfig.DEBUG); + final LayoutInflater inflater = mock(LayoutInflater.class); when(inflater.inflate(ArgumentMatchers.eq(R.layout.hcaptcha_fragment), any(), eq(false))) .thenThrow(InflateException.class); @@ -353,13 +356,14 @@ public void testVerifyOnStoppedFragmentNoException() throws InterruptedException @Test(expected = IllegalArgumentException.class) public void testReset() { - final FragmentScenario scenario = launchInContainer( - config, new HCaptchaStateTestAdapter()); + try (final FragmentScenario scenario = launchInContainer( + config, new HCaptchaStateTestAdapter())) { - scenario.onFragment(HCaptchaDialogFragment::reset); + scenario.onFragment(HCaptchaDialogFragment::reset); - // The fragment has been removed from the FragmentManager already. - scenario.onFragment(fragment -> assertTrue(fragment.isDetached())); + // The fragment has been removed from the FragmentManager already. + scenario.onFragment(fragment -> assertTrue(fragment.isDetached())); + } } @Test diff --git a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java index ec4b955b..1754eb8d 100644 --- a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java +++ b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java @@ -101,7 +101,7 @@ public void e2eWithDebugTokenFragmentDialog() throws Exception { response.markUsed(); latch.countDown(); }) - .addOnFailureListener(exception -> fail("No errors expected"))); + .addOnFailureListener(exception -> fail("No errors expected but received: " + exception.getHCaptchaError()))); assertTrue(latch.await(E2E_AWAIT_CALLBACK_MS, TimeUnit.MILLISECONDS)); } diff --git a/test/src/main/AndroidManifest.xml b/test/src/main/AndroidManifest.xml index 26f851f4..d9151625 100644 --- a/test/src/main/AndroidManifest.xml +++ b/test/src/main/AndroidManifest.xml @@ -13,6 +13,15 @@ + + + + + + + From 2e03f6a320d23042fc9437cdf30720080c5f43e7 Mon Sep 17 00:00:00 2001 From: Alexande B Date: Tue, 14 Nov 2023 23:54:19 +0100 Subject: [PATCH 05/16] ci: fix benchmarkData.json file name after plugin upgrade --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6d942abf..c8af1ac6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -235,13 +235,13 @@ jobs: disable-animations: true arch: x86_64 profile: Nexus 6 - script: ./gradlew bench:connectedReleaseAndroidTest + script: ./gradlew benchmark:connectedReleaseAndroidTest - name: Diff benchmark result id: diff-benchmark uses: ./.github/actions/android-benchmark-diff with: reference: benchmark/data/ci-benchmarkData.json - compare-with: benchmark/build/outputs/connected_android_test_additional_output/releaseAndroidTest/connected/test(AVD) - 10/com.hcaptcha.sdk.bench.test-benchmarkData.json + compare-with: benchmark/build/outputs/connected_android_test_additional_output/releaseAndroidTest/connected/test(AVD) - 10/com_hcaptcha_sdk_bench_test-benchmarkData.json - name: Log diff benchmark reuslts run: echo "${{ steps.diff-benchmark.outputs.markdown-table }}" - uses: peter-evans/find-comment@v2 From d1e73af9afcc180585c525a1493a51abc4df59db Mon Sep 17 00:00:00 2001 From: Alexande B Date: Tue, 14 Nov 2023 23:57:03 +0100 Subject: [PATCH 06/16] gradle: add benchmark:pullBenchmarkData --- benchmark/build.gradle | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/benchmark/build.gradle b/benchmark/build.gradle index beae395e..e8015157 100644 --- a/benchmark/build.gradle +++ b/benchmark/build.gradle @@ -4,7 +4,7 @@ plugins { } android { - compileSdkVersion 34 + compileSdk 34 namespace 'com.hcaptcha.sdk.bench' compileOptions { @@ -46,4 +46,19 @@ dependencies { } // Workaround for: java.lang.ClassNotFoundException: com.android.tools.lint.client.api.Vendor -lint.enabled = false \ No newline at end of file +lint.enabled = false + +tasks.register('pullBenchmarkData', Exec) { + def packageName = android.namespace + ".test" + + commandLine java.nio.file.Paths.get(android.sdkDirectory.absolutePath, "platform-tools", "adb"), + "pull", + "/storage/emulated/0/Android/media/" \ + + "${packageName}/additional_test_output/" \ + + "${packageName.replace('.', '_')}-benchmarkData.json", + "${projectDir}/build/outputs/" + + doFirst { + println "pullBenchmarkData: ${commandLine.join(' ')}" + } +} \ No newline at end of file From 765d092362cb4f5bf349b7cf633923dc14e0497c Mon Sep 17 00:00:00 2001 From: Alexande B Date: Tue, 14 Nov 2023 23:57:39 +0100 Subject: [PATCH 07/16] gradle: fix archivePath deprecation warning --- sdk/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/build.gradle b/sdk/build.gradle index 2407aa63..5a02fe30 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -133,8 +133,8 @@ android.libraryVariants.all { variant -> dependsOn variant.packageLibraryProvider doFirst { - String aarPath = variant.packageLibraryProvider.get().archivePath - long aarSizeKb = file(aarPath).length() / 1024 + var aarPath = variant.packageLibraryProvider.get().archiveFile.get().getAsFile() + long aarSizeKb = aarPath.length() / 1024 println("File ${aarPath} is ${aarSizeKb}Kbyte") } } @@ -145,8 +145,8 @@ android.libraryVariants.all { variant -> dependsOn variant.packageLibraryProvider doFirst { - String aarPath = variant.packageLibraryProvider.get().archivePath - long aarSizeKb = file(aarPath).length() / 1024 + var aarFile = variant.packageLibraryProvider.get().archiveFile.get().getAsFile() + long aarSizeKb = aarFile.length() / 1024 if (aarSizeKb > MAX_AAR_SIZE_KB) { throw new GradleException("${aarPath} size exceeded! ${aarSizeKb}Kbyte > ${MAX_AAR_SIZE_KB}Kbyte") } From 91aa9368aa7cd88571d718166057500b61fddbf7 Mon Sep 17 00:00:00 2001 From: Alexande B Date: Tue, 14 Nov 2023 23:57:59 +0100 Subject: [PATCH 08/16] chore: fix checkstyle issues --- test/src/androidTest/java/com/hcaptcha/sdk/AssertUtil.java | 1 - .../java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/test/src/androidTest/java/com/hcaptcha/sdk/AssertUtil.java b/test/src/androidTest/java/com/hcaptcha/sdk/AssertUtil.java index 0877d4f0..c937dfdf 100644 --- a/test/src/androidTest/java/com/hcaptcha/sdk/AssertUtil.java +++ b/test/src/androidTest/java/com/hcaptcha/sdk/AssertUtil.java @@ -18,7 +18,6 @@ import android.view.View; import android.webkit.ValueCallback; import android.webkit.WebView; - import androidx.test.espresso.PerformException; import androidx.test.espresso.UiController; import androidx.test.espresso.ViewAction; diff --git a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java index 8812a910..c4ef51c4 100644 --- a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java +++ b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaDialogFragmentTest.java @@ -46,7 +46,6 @@ import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentMatchers; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -224,7 +223,7 @@ public void webViewNotInstalled() throws InterruptedException { Assume.assumeTrue("Skip test for release, because impossible to mock LayoutInflater", BuildConfig.DEBUG); final LayoutInflater inflater = mock(LayoutInflater.class); - when(inflater.inflate(ArgumentMatchers.eq(R.layout.hcaptcha_fragment), any(), eq(false))) + when(inflater.inflate(eq(R.layout.hcaptcha_fragment), any(), eq(false))) .thenThrow(InflateException.class); final CountDownLatch latch = new CountDownLatch(1); From a7caa97b8c43188d53ac4c55cbae1dd3f4298ef0 Mon Sep 17 00:00:00 2001 From: Alexande B Date: Wed, 15 Nov 2023 07:25:51 +0100 Subject: [PATCH 09/16] ci: fix wrong test html report path --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8af1ac6..445627d3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -128,8 +128,8 @@ jobs: with: name: androidTest-results path: | - sdk/build/outputs/androidTest-results - sdk/build/reports/androidTests + test/build/outputs/androidTest-results + test/build/reports/androidTests test-minified: name: 'Test UI Minified' @@ -186,8 +186,8 @@ jobs: with: name: androidTest-minified-results path: | - sdk/build/outputs/androidTest-results - sdk/build/reports/androidTests + test/build/outputs/androidTest-results + test/build/reports/androidTests test-benchmark: name: 'Test Benchmark' From ca38332f268d715d8670f73e6177ac51e2bbfb43 Mon Sep 17 00:00:00 2001 From: Alexande B Date: Wed, 15 Nov 2023 07:36:22 +0100 Subject: [PATCH 10/16] docs: add command to run minified tests to MAINTAINERS.md --- MAINTAINERS.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 8ee010f6..f776e392 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -7,10 +7,11 @@ There is automated testing for every `push` command through github actions (see `.github/workflows/ci.yml`). You can manually test before pushing by running both unit tests and instrumented tests: -* ```gradlew test``` -* ```gradlew connectedDebugAndroidTest``` +* `./gradlew sdk:test` +* `./gradlew test:connectedAndroidTest` +* `./gradlew test:connectedAndroidTest -P testingMinimizedBuild=true -P android.enableR8.fullMode=false` -## Manual testing (full) +## Manual testing + {normal,invisible,compact} -> verify -> success -> mark used + {normal,invisible,compact} -> verify -> success -> token timeout From de6ca19b5bf81d6d7fe9c4e0f181c9ba8c8b4f41 Mon Sep 17 00:00:00 2001 From: Alexande B Date: Wed, 15 Nov 2023 07:41:19 +0100 Subject: [PATCH 11/16] gradle: upgrade compileSdk to 34 --- .github/workflows/ci.yml | 3 +++ example-app/build.gradle | 5 ++--- sdk/build.gradle | 4 ++-- test/build.gradle | 7 +++---- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 445627d3..8b82005c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,6 +47,9 @@ jobs: fail-fast: false matrix: include: + - compile: 34 + target: 34 + appcompat: 1.6.2 - compile: 33 target: 33 appcompat: 1.5.1 diff --git a/example-app/build.gradle b/example-app/build.gradle index 9a5a1076..fbc7f607 100644 --- a/example-app/build.gradle +++ b/example-app/build.gradle @@ -9,13 +9,12 @@ def prop(name, fallback) { } android { - compileSdkVersion intProp("exampleCompileSdkVersion", 32) + compileSdk intProp("exampleCompileSdkVersion", 34) namespace 'com.hcaptcha.example' defaultConfig { - applicationId "com.hcaptcha.example" minSdkVersion 16 - targetSdkVersion intProp("exampleTargetSdkVersion", 32) + targetSdkVersion intProp("exampleTargetSdkVersion", 34) versionCode 1 versionName "0.0.1" diff --git a/sdk/build.gradle b/sdk/build.gradle index 5a02fe30..449ce482 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -10,7 +10,7 @@ plugins { } android { - compileSdkVersion 33 + compileSdk 34 namespace 'com.hcaptcha.sdk' buildFeatures { @@ -19,7 +19,7 @@ android { defaultConfig { minSdkVersion 16 - targetSdkVersion 33 + targetSdkVersion 34 // See https://developer.android.com/studio/publish/versioning // versionCode must be integer and be incremented by one for every new update diff --git a/test/build.gradle b/test/build.gradle index c2c52ca0..8b7dffd9 100644 --- a/test/build.gradle +++ b/test/build.gradle @@ -1,17 +1,16 @@ apply plugin: 'com.android.application' if (project.hasProperty("testingMinimizedBuild")) { - project.setProperty("android.enableR8.fullMode", false) apply plugin: 'com.slack.keeper' } android { - compileSdkVersion 33 + compileSdk 34 namespace 'com.hcaptcha.sdk.test' defaultConfig { applicationId "com.hcaptcha.sdk.test" minSdkVersion 19 - targetSdkVersion 33 + targetSdkVersion 34 versionCode 1 versionName "1.0" @@ -23,7 +22,7 @@ android { signingConfig signingConfigs.debug minifyEnabled true shrinkResources true - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro', '../sdk/consumer-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android.txt'), '../sdk/consumer-rules.pro' } } From 59e0fd6828f8bbefd34de603325fb6a02e62a3d9 Mon Sep 17 00:00:00 2001 From: Alexande B Date: Wed, 15 Nov 2023 07:41:56 +0100 Subject: [PATCH 12/16] tests: clean test/proguard-rules.pro --- test/proguard-rules.pro | 18 ------------------ .../sdk/HCaptchaHeadlessWebViewTest.java | 1 - 2 files changed, 19 deletions(-) diff --git a/test/proguard-rules.pro b/test/proguard-rules.pro index 2a143c68..e23fa22e 100644 --- a/test/proguard-rules.pro +++ b/test/proguard-rules.pro @@ -1,19 +1 @@ # Proguard rules that are applied to your test apk/code. --ignorewarnings - -# -keepattributes *Annotation* --keepattributes Signature - -#-keep class androidx.fragment.app.testing.** { *; } - --assumenosideeffects class com.hcaptcha.sdk.HCaptchaDialogFragmentTest { - *** webViewNotInstalled(...); -} - -# rules to keep compiller happy with android.enableR8.fullMode=true - --keepnames class android.test.** { *; } --keep class androidx.lifecycle.Lifecycle$Event$Companion { *; } --keep class androidx.lifecycle.** { *; } --keep class com.google.errorprone.annotations.MustBeClosed { *; } --dontwarn com.sun.jna.** diff --git a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaHeadlessWebViewTest.java b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaHeadlessWebViewTest.java index a9d1efb6..45bafe1d 100644 --- a/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaHeadlessWebViewTest.java +++ b/test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaHeadlessWebViewTest.java @@ -8,7 +8,6 @@ import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.test.core.app.ActivityScenario; import androidx.test.ext.junit.rules.ActivityScenarioRule; From c7b7c676b8729c02ba5bcb6417ae12a7849c074d Mon Sep 17 00:00:00 2001 From: Alexande B Date: Wed, 15 Nov 2023 07:55:33 +0100 Subject: [PATCH 13/16] ci: fix appcompat version for compileSdk 34 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b82005c..e3476ae8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: include: - compile: 34 target: 34 - appcompat: 1.6.2 + appcompat: 1.6.1 - compile: 33 target: 33 appcompat: 1.5.1 From 6b80a95b1297e2fb1230e15f5c48c8ab9de8f8cf Mon Sep 17 00:00:00 2001 From: Alexande B Date: Wed, 15 Nov 2023 15:21:56 +0100 Subject: [PATCH 14/16] fix: put back occasionally removed sdk/proguard-rules.pro --- sdk/build.gradle | 2 +- sdk/proguard-rules.pro | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/sdk/build.gradle b/sdk/build.gradle index 449ce482..c77a3c77 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -33,7 +33,7 @@ android { buildConfigField 'String', 'VERSION_NAME', "\"${defaultConfig.versionName}_${defaultConfig.versionCode}\"" consumerProguardFiles "consumer-rules.pro" - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt') + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } compileOptions { diff --git a/sdk/proguard-rules.pro b/sdk/proguard-rules.pro index b19b3096..21d4d2b1 100644 --- a/sdk/proguard-rules.pro +++ b/sdk/proguard-rules.pro @@ -4,3 +4,6 @@ # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html + +# Keep all sdk classes +-keep class com.hcaptcha.sdk.** { *; } \ No newline at end of file From 7d903b79048c9465cb88233eadbcc9086dd4b5b1 Mon Sep 17 00:00:00 2001 From: Alexande B Date: Wed, 15 Nov 2023 15:41:32 +0100 Subject: [PATCH 15/16] chore: remove unnecessary debug section in buildTypes sdk/build.gradle --- sdk/build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/sdk/build.gradle b/sdk/build.gradle index c77a3c77..84535379 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -43,8 +43,6 @@ android { } buildTypes { - debug { - } release { minifyEnabled true } From 085e03d56c0714348efc5d11c4d12c5cc5263750 Mon Sep 17 00:00:00 2001 From: Sergiu Danalachi Date: Wed, 15 Nov 2023 16:56:25 +0200 Subject: [PATCH 16/16] refactor: revert newline --- sdk/proguard-rules.pro | 2 +- test/.gitignore | 2 +- test/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/proguard-rules.pro b/sdk/proguard-rules.pro index 21d4d2b1..3935452c 100644 --- a/sdk/proguard-rules.pro +++ b/sdk/proguard-rules.pro @@ -6,4 +6,4 @@ # http://developer.android.com/guide/developing/tools/proguard.html # Keep all sdk classes --keep class com.hcaptcha.sdk.** { *; } \ No newline at end of file +-keep class com.hcaptcha.sdk.** { *; } diff --git a/test/.gitignore b/test/.gitignore index 42afabfd..796b96d1 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -1 +1 @@ -/build \ No newline at end of file +/build diff --git a/test/build.gradle b/test/build.gradle index 8b7dffd9..e4387dca 100644 --- a/test/build.gradle +++ b/test/build.gradle @@ -58,4 +58,4 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation 'androidx.test.espresso:espresso-web:3.5.1' androidTestImplementation 'org.mockito:mockito-android:5.3.1' -} \ No newline at end of file +}