diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..977ded4
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,29 @@
+name: CI - Tests
+on:
+ push:
+ branches-ignore:
+ - 'main'
+ - 'releases/**'
+jobs:
+ Tests:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up JDK
+ uses: actions/setup-java@v1
+ with:
+ java-version: '11'
+ - name: Setup Android SDK
+ uses: android-actions/setup-android@v2
+ - name: Make gradlew executable
+ run: chmod +x ./gradlew
+ - name: Run Tests
+ run: ./gradlew test
+ - name: Test Report
+ uses: dorny/test-reporter@v1
+ if: always()
+ with:
+ name: Test Results
+ path: app/build/test-results/testDebugUnitTest/TEST-*.xml # Path to test results
+ reporter: java-junit # Format of test results
+ fail-on-error: true
\ No newline at end of file
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index c9d1714..3add3e3 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -1,5 +1,10 @@
# Release Notes
+### 6.10.1
+* Update Android SDK to v6.10.1
+* Added unit tests.
+* Added github workflows.
+
### 6.8.2
* Update Android SDK to v6.8.2
diff --git a/Readme.md b/Readme.md
index 5eb1ef6..034a0a8 100644
--- a/Readme.md
+++ b/Readme.md
@@ -21,7 +21,7 @@ You can track installs, updates and sessions and also track additional in-app ev
---
-Built with AppsFlyer Android SDK `v6.8.2`
+Built with AppsFlyer Android SDK `v6.10.1`
## Table of content
diff --git a/app/build.gradle b/app/build.gradle
index 0a095e4..01ce7b8 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,13 +1,15 @@
apply plugin: 'com.android.library'
android {
- compileSdkVersion 26
+ compileSdkVersion 32
defaultConfig {
minSdkVersion 14
- targetSdkVersion 26
+ targetSdkVersion 32
versionCode 1
versionName "1.0"
+ testApplicationId "com.example.test"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
@@ -19,15 +21,42 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
+
+ testOptions {
+ unitTests {
+ all {
+ testLogging {
+ exceptionFormat = "full"
+ events "PASSED", "FAILED", "SKIPPED"
+ }
+ forkEvery 1
+ }
+ includeAndroidResources = true
+ returnDefaultValues = true
+ }
+ }
}
dependencies {
- implementation 'com.appsflyer:af-android-sdk:6.8.2'
+ implementation 'androidx.test.ext:junit:1.1.5'
+ implementation 'com.appsflyer:af-android-sdk:6.10.1'
compileOnly 'com.android.installreferrer:installreferrer:2.1'
compileOnly 'com.segment.analytics.android:analytics:4.+'
- testImplementation 'junit:junit:4.12'
- testImplementation 'org.mockito:mockito-core:1.10.19'
+ testImplementation 'androidx.test:core:1.4.0'
+ testImplementation 'junit:junit:4.13.2'
+ testImplementation 'org.robolectric:robolectric:4.9.2'
+ testImplementation 'com.android.installreferrer:installreferrer:2.1'
+ testImplementation 'com.segment.analytics.android:analytics:4.+'
+ testImplementation 'org.mockito:mockito-core:4.2.0'
testImplementation 'com.segment.analytics.android:analytics-tests:4.+'
}
-apply from: file('publish.gradle')
\ No newline at end of file
+tasks.withType(Test) {
+ testLogging {
+ exceptionFormat "full"
+ events "started", "skipped", "passed", "failed"
+ showStandardStreams true
+ }
+}
+
+apply from:file("publish.gradle")
\ No newline at end of file
diff --git a/app/publish.gradle b/app/publish.gradle
index fb33ec9..961640c 100644
--- a/app/publish.gradle
+++ b/app/publish.gradle
@@ -1,63 +1,129 @@
-apply plugin: 'maven'
+apply plugin: 'maven-publish'
apply plugin: 'signing'
-afterEvaluate { project ->
- uploadArchives {
- repositories {
- mavenDeployer {
- beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
- pom.groupId = GROUP
- pom.artifactId = POM_ARTIFACT_ID
- pom.version = VERSION_NAME
- repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
- authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
- }
- pom.project {
- name POM_NAME
- packaging POM_PACKAGING
- description POM_DESCRIPTION
- url POM_URL
- scm {
- url POM_SCM_URL
- connection POM_SCM_CONNECTION
- developerConnection POM_SCM_DEV_CONNECTION
- }
- licenses {
- license {
- name POM_LICENCE_NAME
- url POM_LICENCE_URL
- distribution POM_LICENCE_DIST
- }
- }
- developers {
- developer {
- id POM_DEVELOPER_ID
- name POM_DEVELOPER_NAME
+def isReleaseBuild() {
+ return !VERSION_NAME.contains("SNAPSHOT")
+}
+
+def getReleaseRepositoryUrl() {
+ return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL
+ : "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
+}
+
+def getSnapshotRepositoryUrl() {
+ return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL
+ : "https://oss.sonatype.org/content/repositories/snapshots/"
+}
+
+task androidJavadocs(type: Javadoc) {
+ exclude "**/*.orig" // exclude files created by source control
+ source = android.sourceSets.main.java.srcDirs
+ classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
+ failOnError false
+}
+
+task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
+ archiveClassifier.set("javadoc")
+
+ from androidJavadocs.destinationDir
+}
+
+task androidSourcesJar(type: Jar) {
+ archiveClassifier.set("sources")
+
+ from android.sourceSets.main.java.source
+}
+
+def logger(log) {
+ println log
+}
+
+def configurePom(pom) {
+ logger("configurePom")
+ pom.name = POM_NAME
+ pom.packaging = POM_PACKAGING
+ pom.description = POM_DESCRIPTION
+ pom.url = POM_URL
+
+ pom.scm {
+ url = POM_SCM_URL
+ connection = POM_SCM_CONNECTION
+ developerConnection = POM_SCM_DEV_CONNECTION
+ }
+
+ pom.licenses {
+ license {
+ name = POM_LICENCE_NAME
+ url = POM_LICENCE_URL
+ distribution = POM_LICENCE_DIST
+ }
+ }
+
+ pom.developers {
+ developer {
+ id = POM_DEVELOPER_ID
+ name = POM_DEVELOPER_NAME
+ }
+ }
+}
+
+afterEvaluate {
+ publishing {
+ publications {
+ release(MavenPublication) {
+ logger("release")
+ // The coordinates of the library, being set from variables that
+ // we'll set up in a moment
+ groupId GROUP
+ artifactId POM_ARTIFACT_ID
+ version VERSION_NAME
+
+ // Two artifacts, the `aar` and the sources
+ // artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
+ artifact bundleReleaseAar
+ artifact androidSourcesJar
+ artifact androidJavadocsJar
+
+ // Self-explanatory metadata for the most part
+ pom {
+ configurePom(pom)
+ // A slight fix so that the generated POM will include any transitive dependencies
+ // that the library builds upon
+ withXml {
+ def dependenciesNode = asNode().appendNode('dependencies')
+
+ project.configurations.implementation.allDependencies.each {
+ def dependencyNode = dependenciesNode.appendNode('dependency')
+ dependencyNode.appendNode('groupId', it.group)
+ dependencyNode.appendNode('artifactId', it.name)
+ dependencyNode.appendNode('version', it.version)
}
}
}
}
}
- }
- signing {
- required { gradle.taskGraph.hasTask("uploadArchives") }
- sign configurations.archives
- }
- task androidJavadocs(type: Javadoc) {
- classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
- if (JavaVersion.current().isJava8Compatible()) {
- allprojects {
- tasks.withType(Javadoc) { options.addStringOption('Xdoclint:none', '-quiet') }
+ repositories {
+ maven {
+ name = "sonatype"
+
+ // You only need this if you want to publish snapshots, otherwise just set the URL
+ // to the release repo directly
+ url = isReleaseBuild() ? getReleaseRepositoryUrl() : getSnapshotRepositoryUrl()
+
+ credentials(PasswordCredentials) {
+ username = getRepositoryUsername()
+ password = getRepositoryPassword()
+ }
}
}
}
- task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
- archiveClassifier = 'javadoc'
- from androidJavadocs.destinationDir
- }
- task androidSourcesJar(type: Jar) {
- classifier = 'sources'
- from android.sourceSets.main.java.sourceFiles
- }
- artifacts { archives androidJavadocsJar }
-}
\ No newline at end of file
+}
+
+signing {
+ logger("signing")
+ sign publishing.publications
+}
+
+
+publish.dependsOn build
+publishToMavenLocal.dependsOn build
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/segment/analytics/android/integrations/appsflyer/ApplicationTest.java b/app/src/androidTest/java/com/segment/analytics/android/integrations/appsflyer/ApplicationTest.java
index 56a28c8..5d4cb89 100644
--- a/app/src/androidTest/java/com/segment/analytics/android/integrations/appsflyer/ApplicationTest.java
+++ b/app/src/androidTest/java/com/segment/analytics/android/integrations/appsflyer/ApplicationTest.java
@@ -1,13 +1,13 @@
-package com.segment.analytics.android.integration.appsflyer;
-
-import android.app.Application;
-import android.test.ApplicationTestCase;
-
-/**
- * Testing Fundamentals
- */
-public class ApplicationTest extends ApplicationTestCase {
- public ApplicationTest() {
- super(Application.class);
- }
-}
\ No newline at end of file
+//package com.segment.analytics.android.integration.appsflyer;
+//
+//import android.app.Application;
+//import android.test.ApplicationTestCase;
+//
+///**
+// * Testing Fundamentals
+// */
+//public class ApplicationTest extends ApplicationTestCase {
+// public ApplicationTest() {
+// super(Application.class);
+// }
+//}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d9d6c4e..f49da06 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,4 @@
-
-
-
+
\ No newline at end of file
diff --git a/app/src/main/java/com/segment/analytics/android/integrations/appsflyer/AppsflyerIntegration.java b/app/src/main/java/com/segment/analytics/android/integrations/appsflyer/AppsflyerIntegration.java
index 22e10cf..145961b 100644
--- a/app/src/main/java/com/segment/analytics/android/integrations/appsflyer/AppsflyerIntegration.java
+++ b/app/src/main/java/com/segment/analytics/android/integrations/appsflyer/AppsflyerIntegration.java
@@ -80,6 +80,9 @@ public static void startAppsFlyer(@NonNull Context context){
public static final Factory FACTORY = new Integration.Factory() {
@Override
public Integration create(ValueMap settings, Analytics analytics) {
+ if(settings == null || analytics == null){
+ return null;
+ }
Logger logger = analytics.logger(APPSFLYER_KEY);
AppsFlyerLib afLib = AppsFlyerLib.getInstance();
diff --git a/app/src/test/java/com/segment/analytics/android/integrations/appsflyer/AppsflyerIntegrationConversionListenerTests.java b/app/src/test/java/com/segment/analytics/android/integrations/appsflyer/AppsflyerIntegrationConversionListenerTests.java
new file mode 100644
index 0000000..32f2378
--- /dev/null
+++ b/app/src/test/java/com/segment/analytics/android/integrations/appsflyer/AppsflyerIntegrationConversionListenerTests.java
@@ -0,0 +1,213 @@
+package com.segment.analytics.android.integrations.appsflyer;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import android.app.Application;
+import android.content.Context;
+import android.content.SharedPreferences;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.segment.analytics.Analytics;
+import com.segment.analytics.Properties;
+import com.segment.analytics.ValueMap;
+import static org.mockito.Mockito.*;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+public class AppsflyerIntegrationConversionListenerTests {
+
+ @Test
+ public void testAppsflyerIntegration_ConversionListener_ctor_happyFlow() {
+ Analytics analytics = mock(Analytics.class);
+ AppsflyerIntegration.ConversionListener conversionListener = new AppsflyerIntegration.ConversionListener(analytics);
+
+ Assert.assertEquals(conversionListener.analytics, analytics);
+
+ reset(analytics);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_ConversionListener_ctor_nullFlow() {
+ Analytics analytics = null;
+ AppsflyerIntegration.ConversionListener conversionListener = new AppsflyerIntegration.ConversionListener(analytics);
+
+ Assert.assertEquals(conversionListener.analytics, analytics);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_ConversionListener_onConversionDataSuccess_happyFlow() {
+ //I want just to check the conversionListener gets the map.
+ AppsflyerIntegration.conversionListener = mock(AppsflyerIntegration.ExternalAppsFlyerConversionListener.class);
+ Analytics analytics = mock(Analytics.class);
+ Map conversionData = new ValueMap();
+ Application app = mock(Application.class);
+ Context context = mock(Context.class);
+ SharedPreferences sharedPreferences = mock(SharedPreferences.class);
+ when(analytics.getApplication()).thenReturn(app);
+ when(app.getApplicationContext()).thenReturn(context);
+ when(context.getSharedPreferences("appsflyer-segment-data",0)).thenReturn(sharedPreferences);
+ when(sharedPreferences.getBoolean("AF_onConversion_Data",false)).thenReturn(true);
+ AppsflyerIntegration.ConversionListener conversionListener = new AppsflyerIntegration.ConversionListener(analytics);
+
+ conversionListener.onConversionDataSuccess(conversionData);
+
+ verify(AppsflyerIntegration.conversionListener).onConversionDataSuccess(conversionData);
+
+ reset(AppsflyerIntegration.conversionListener,analytics,app,context,sharedPreferences);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_ConversionListener_onAttributionFailure_happyFlow() {
+ AppsflyerIntegration.conversionListener = mock(AppsflyerIntegration.ExternalAppsFlyerConversionListener.class);
+ Analytics analytics = Mockito.mock(Analytics.class);
+ AppsflyerIntegration.ConversionListener conversionListener = new AppsflyerIntegration.ConversionListener(analytics);
+ String errorMsg = "error - test";
+
+ conversionListener.onAttributionFailure(errorMsg);
+
+ verify(AppsflyerIntegration.conversionListener,times(1)).onAttributionFailure(errorMsg);
+
+ reset(analytics,AppsflyerIntegration.conversionListener);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_ConversionListener_onAttributionFailure_nullFlow() {
+ AppsflyerIntegration.conversionListener = mock(AppsflyerIntegration.ExternalAppsFlyerConversionListener.class);
+ Analytics analytics = Mockito.mock(Analytics.class);
+ AppsflyerIntegration.ConversionListener conversionListener = new AppsflyerIntegration.ConversionListener(analytics);
+ String errorMsg = null;
+ conversionListener.onAttributionFailure(errorMsg);
+ verify(AppsflyerIntegration.conversionListener,times(1)).onAttributionFailure(null);
+
+ reset(analytics,AppsflyerIntegration.conversionListener);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_ConversionListener_trackInstallAttributed_happyFlow() {
+ Analytics analytics =mock(Analytics.class);
+ Map attributionData = new HashMap<>();
+ attributionData.put("media_source", "media_source_moris");
+ attributionData.put("campaign", "campaign_moris");
+ attributionData.put("adgroup", "adgroup_moris");
+
+ Map campaign = new ValueMap()
+ .putValue("source", attributionData.get("media_source"))
+ .putValue("name", attributionData.get("campaign"))
+ .putValue("ad_group", attributionData.get("adgroup"));
+ Properties properties = new Properties().putValue("provider", "AppsFlyer");
+ properties.putAll(attributionData);
+ properties.remove("media_source");
+ properties.remove("adgroup");
+ properties.putValue("campaign", campaign);
+ AppsflyerIntegration.ConversionListener conversionListener = new AppsflyerIntegration.ConversionListener(analytics);
+
+ conversionListener.trackInstallAttributed(attributionData);
+
+ verify(analytics,times(1)).track("Install Attributed", properties);
+
+ reset(analytics);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_ConversionListener_trackInstallAttributed_negativeFlow() {
+ Analytics analytics =mock(Analytics.class);
+ Map attributionData = new HashMap();
+ Map campaign = new ValueMap() //
+ .putValue("source", "")
+ .putValue("name", "")
+ .putValue("ad_group", "");
+ Properties properties = new Properties().putValue("provider", "AppsFlyer");
+ properties.putAll(attributionData);
+ properties.remove("media_source");
+ properties.remove("adgroup");
+ properties.putValue("campaign", campaign);
+ AppsflyerIntegration.ConversionListener conversionListener = new AppsflyerIntegration.ConversionListener(analytics);
+ conversionListener.trackInstallAttributed(attributionData);
+
+ verify(analytics,times(1)).track("Install Attributed", properties);
+
+ reset(analytics);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_ConversionListener_getFlag_happyFlow() throws Exception {
+ String key="key";
+ Analytics analytics = mock(Analytics.class);
+ Application app = mock(Application.class);
+ Context context = mock(Context.class);
+ SharedPreferences sharedPreferences = mock(SharedPreferences.class);
+ when(analytics.getApplication()).thenReturn(app);
+ when(app.getApplicationContext()).thenReturn(context);
+ when(context.getSharedPreferences("appsflyer-segment-data",0)).thenReturn(sharedPreferences);
+ when(sharedPreferences.getBoolean(key,false)).thenReturn(true);
+ AppsflyerIntegration.ConversionListener conversionListener = new AppsflyerIntegration.ConversionListener(analytics);
+
+ boolean resBoolean = (Boolean) TestHelper.getPrivateMethodForObjectReadyToInvoke("getFlag",String.class).invoke(conversionListener,key);
+
+ Assert.assertTrue(resBoolean);
+
+ reset(analytics,app,context,sharedPreferences);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_ConversionListener_setFlag_happyFlow() throws Exception {
+ String key="key";
+ boolean value=true;
+ Analytics analytics = mock(Analytics.class);
+ Application app = mock(Application.class);
+ Context context = mock(Context.class);
+ SharedPreferences sharedPreferences = mock(SharedPreferences.class);
+ SharedPreferences.Editor editor = mock(SharedPreferences.Editor.class);
+ when(analytics.getApplication()).thenReturn(app);
+ when(app.getApplicationContext()).thenReturn(context);
+ when(context.getSharedPreferences("appsflyer-segment-data",0)).thenReturn(sharedPreferences);
+ when(sharedPreferences.edit()).thenReturn(editor);
+ AppsflyerIntegration.ConversionListener conversionListener = new AppsflyerIntegration.ConversionListener(analytics);
+
+ TestHelper.getPrivateMethodForObjectReadyToInvoke("setFlag",String.class,boolean.class).invoke(conversionListener,key,value);
+
+ if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD){
+ verify(editor,times(1)).apply();
+ }
+ else{
+ verify(editor,times(1)).commit();
+ }
+
+ reset(analytics,app,context,sharedPreferences);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_ConversionListener_getContext_happyFlow() throws Exception {
+ Analytics analytics = mock(Analytics.class);
+ Application app = mock(Application.class);
+ Context context = mock(Context.class);
+ when(analytics.getApplication()).thenReturn(app);
+ when(app.getApplicationContext()).thenReturn(context);
+ AppsflyerIntegration.ConversionListener conversionListener = new AppsflyerIntegration.ConversionListener(analytics);
+
+ Context resContext = (Context) TestHelper.getPrivateMethodForObjectReadyToInvoke("getContext").invoke(conversionListener);
+
+ Assert.assertEquals(resContext, context);
+
+ reset(analytics,app,context);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_ConversionListener_getContext_nullFlow() throws Exception{
+ Analytics analytics = mock(Analytics.class);
+ Application app = mock(Application.class);
+ Context context = null;
+ when(analytics.getApplication()).thenReturn(app);
+ when(app.getApplicationContext()).thenReturn(context);
+ AppsflyerIntegration.ConversionListener conversionListener = new AppsflyerIntegration.ConversionListener(analytics);
+
+ Context resContext = (Context) TestHelper.getPrivateMethodForObjectReadyToInvoke("getContext").invoke(conversionListener);
+
+ Assert.assertEquals(resContext, context);
+
+ reset(analytics,app);
+ }
+}
diff --git a/app/src/test/java/com/segment/analytics/android/integrations/appsflyer/AppsflyerIntegrationTests.java b/app/src/test/java/com/segment/analytics/android/integrations/appsflyer/AppsflyerIntegrationTests.java
new file mode 100644
index 0000000..823b26b
--- /dev/null
+++ b/app/src/test/java/com/segment/analytics/android/integrations/appsflyer/AppsflyerIntegrationTests.java
@@ -0,0 +1,221 @@
+package com.segment.analytics.android.integrations.appsflyer;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import android.app.Application;
+import android.content.Context;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.appsflyer.AFInAppEventParameterName;
+import com.appsflyer.AppsFlyerLib;
+import com.segment.analytics.Analytics;
+import com.segment.analytics.Properties;
+import com.segment.analytics.Traits;
+import com.segment.analytics.ValueMap;
+import com.segment.analytics.integrations.IdentifyPayload;
+import com.segment.analytics.integrations.Integration;
+import com.segment.analytics.integrations.Logger;
+import com.segment.analytics.integrations.TrackPayload;
+import static org.mockito.Mockito.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+public class AppsflyerIntegrationTests {
+ private TestHelper testHelper = new TestHelper();
+
+ @Test
+ public void testAppsflyerIntegration_ctor_happyFlow() throws Exception {
+ Context context = mock(Context.class);
+ Logger logger = new Logger("test", Analytics.LogLevel.INFO);
+ AppsFlyerLib appsflyer = mock(AppsFlyerLib.class);
+ String appsflyerDevKey = "appsflyerDevKey";
+ boolean isDebug = logger.logLevel != Analytics.LogLevel.NONE;
+
+ AppsflyerIntegration appsflyerIntegration = new AppsflyerIntegration(context,logger,appsflyer,appsflyerDevKey);
+ Assert.assertEquals(appsflyerIntegration.isDebug , isDebug);
+ Assert.assertEquals(appsflyerIntegration.appsFlyerDevKey, appsflyerDevKey);
+ Assert.assertEquals(appsflyerIntegration.appsflyer, appsflyer);
+ Assert.assertEquals(appsflyerIntegration.logger, logger);
+ Context contextInappsflyerIntegration = (Context) TestHelper.getPrivateFieldForObject("context",AppsflyerIntegration.class,appsflyerIntegration);
+ Assert.assertEquals(contextInappsflyerIntegration, context);
+// checking the static clause
+ Assert.assertEquals(AppsflyerIntegration.MAPPER.get("revenue"), AFInAppEventParameterName.REVENUE);
+ Assert.assertEquals(AppsflyerIntegration.MAPPER.get("currency"), AFInAppEventParameterName.CURRENCY);
+
+ reset(context,appsflyer);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_setManualMode_happyFlow() {
+ Assert.assertFalse(AppsflyerIntegration.manualMode);
+ AppsflyerIntegration.setManualMode(true);
+ Assert.assertTrue(AppsflyerIntegration.manualMode);
+ AppsflyerIntegration.setManualMode(false);
+ Assert.assertFalse(AppsflyerIntegration.manualMode);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_startAppsFlyer_happyFlow() {
+ AppsFlyerLib appsFlyerLib = testHelper.mockAppsflyerLib();
+ Context context = mock(Context.class);
+
+ AppsflyerIntegration.startAppsFlyer(context);
+
+ verify(appsFlyerLib).start(context);
+
+ reset(appsFlyerLib,context);
+ testHelper.closeMockAppsflyerLib();
+ }
+
+ @Test
+ public void testAppsflyerIntegration_startAppsFlyer_nilFlow() {
+ AppsFlyerLib appsFlyerLib = testHelper.mockAppsflyerLib();
+
+ AppsflyerIntegration.startAppsFlyer(null);
+
+ verify(appsFlyerLib,never()).start(any());
+
+ reset(appsFlyerLib);
+ testHelper.closeMockAppsflyerLib();
+ }
+
+ @Test
+ public void testAppsflyerIntegration_FACTORYCreate_happyFlow() {
+ AppsFlyerLib appsFlyerLib = testHelper.mockAppsflyerLib();
+ Analytics analytics = mock(Analytics.class);
+ ValueMap settings = new ValueMap();
+ settings.put("appsFlyerDevKey" , "devKey");
+ settings.put("trackAttributionData" , true);
+ Logger logger = new Logger("test", Analytics.LogLevel.INFO);
+ Mockito.when(analytics.logger("AppsFlyer")).thenReturn(logger);
+ Application app = mock(Application.class);
+ Mockito.when(analytics.getApplication()).thenReturn(app);
+ AppsflyerIntegration.deepLinkListener = mock(AppsflyerIntegration.ExternalDeepLinkListener.class);
+
+ Integration integration= (Integration) AppsflyerIntegration.FACTORY.create(settings,analytics);
+
+ verify(appsFlyerLib).setDebugLog(logger.logLevel!=Analytics.LogLevel.NONE);
+ ArgumentCaptor captorListener = ArgumentCaptor.forClass(AppsflyerIntegration.ConversionListener.class);
+ ArgumentCaptor captorDevKey = ArgumentCaptor.forClass(String.class);
+ ArgumentCaptor captorContext = ArgumentCaptor.forClass(Context.class);
+ verify(appsFlyerLib).init(captorDevKey.capture(), captorListener.capture() , captorContext.capture());
+ Assert.assertNotNull(captorListener.getValue());
+ Assert.assertEquals(captorDevKey.getValue(), settings.getString("appsFlyerDevKey"));
+ Assert.assertEquals(captorContext.getValue(), app.getApplicationContext());
+ verify(appsFlyerLib).subscribeForDeepLink(AppsflyerIntegration.deepLinkListener);
+
+ reset(appsFlyerLib,analytics,app,AppsflyerIntegration.deepLinkListener);
+ testHelper.closeMockAppsflyerLib();
+ }
+
+ @Test
+ public void testAppsflyerIntegration_FACTORYCreate_nilFlow() {
+ Analytics analytics = null;
+ ValueMap settings = null;
+
+ Integration integration= (Integration) AppsflyerIntegration.FACTORY.create(settings,analytics);
+
+ Assert.assertNull(integration);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_FACTORYKEY_happyFlow() {
+ Assert.assertEquals(AppsflyerIntegration.FACTORY.key(),"AppsFlyer");
+ }
+
+ @Test
+ public void testAppsflyerIntegration_getUnderlyingInstance_happyFlow() {
+ AppsFlyerLib appsFlyerLib = mock(AppsFlyerLib.class);
+ Logger logger = new Logger("test", Analytics.LogLevel.INFO);
+ AppsflyerIntegration appsflyerIntegration = new AppsflyerIntegration(null,logger,appsFlyerLib,null);
+
+ Assert.assertEquals(appsflyerIntegration.getUnderlyingInstance(),appsFlyerLib);
+
+ reset(appsFlyerLib);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_identify_happyFlow() throws Exception {
+ AppsFlyerLib appsFlyerLib = mock(AppsFlyerLib.class);
+ Logger logger = spy(new Logger("test", Analytics.LogLevel.INFO));
+ AppsflyerIntegration appsflyerIntegration = new AppsflyerIntegration(null,logger,appsFlyerLib,null);
+ IdentifyPayload identifyPayload = mock(IdentifyPayload.class);
+ Traits traits = mock(Traits.class);
+ when(identifyPayload.userId()).thenReturn("moris");
+ when(identifyPayload.traits()).thenReturn(traits);
+ when(traits.getString("currencyCode")).thenReturn("ILS");
+
+ appsflyerIntegration.identify(identifyPayload);
+
+ verify(logger, never()).verbose(any());
+ String customerUserIdInappsflyerIntegration = (String) TestHelper.getPrivateFieldForObject("customerUserId",AppsflyerIntegration.class,appsflyerIntegration);
+ Assert.assertEquals(customerUserIdInappsflyerIntegration, "moris");
+ String currencyCodeInappsflyerIntegration = (String) TestHelper.getPrivateFieldForObject("currencyCode",AppsflyerIntegration.class,appsflyerIntegration);
+ Assert.assertEquals(currencyCodeInappsflyerIntegration, "ILS");
+
+ reset(appsFlyerLib,identifyPayload,traits);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_identify_nilflow() {
+ Logger logger = spy(new Logger("test", Analytics.LogLevel.INFO));
+ AppsflyerIntegration appsflyerIntegration = new AppsflyerIntegration(null,logger,null,null);
+ IdentifyPayload identifyPayload = mock(IdentifyPayload.class);
+ Traits traits = mock(Traits.class);
+ when(identifyPayload.traits()).thenReturn(traits);
+
+ appsflyerIntegration.identify(identifyPayload);
+
+ verify(logger, times(1)).verbose("couldn't update 'Identify' attributes");
+
+ reset(identifyPayload,traits);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_updateEndUserAttributes_happyflow() throws Exception {
+ AppsFlyerLib appsFlyerLib = mock(AppsFlyerLib.class);
+ Logger logger = spy(new Logger("test", Analytics.LogLevel.INFO));
+ AppsflyerIntegration appsflyerIntegration = new AppsflyerIntegration(null,logger,appsFlyerLib,null);
+ Method updateEndUserAttributes = AppsflyerIntegration.class.getDeclaredMethod("updateEndUserAttributes");
+ updateEndUserAttributes.setAccessible(true);
+ TestHelper.setPrivateFieldForObject("customerUserId",AppsflyerIntegration.class,appsflyerIntegration,String.class,"Moris");
+ TestHelper.setPrivateFieldForObject("currencyCode",AppsflyerIntegration.class,appsflyerIntegration,String.class,"ILS");
+ updateEndUserAttributes.invoke(appsflyerIntegration);
+
+ verify(logger, times(1)).verbose("appsflyer.setCustomerUserId(%s)", "Moris");
+ verify(logger, times(1)).verbose("appsflyer.setCurrencyCode(%s)", "ILS");
+ verify(logger, times(1)).verbose("appsflyer.setDebugLog(%s)", true);
+
+ reset(appsFlyerLib);
+ }
+
+ @Test
+ public void testAppsflyerIntegration_track_happyflow() throws Exception {
+ AppsFlyerLib appsFlyerLib = mock(AppsFlyerLib.class);
+ Logger logger = spy(new Logger("test", Analytics.LogLevel.INFO));
+ AppsflyerIntegration appsflyerIntegration = new AppsflyerIntegration(null,logger,appsFlyerLib,null);
+ TrackPayload trackPayload = mock(TrackPayload.class);
+ String event = "event";
+ Properties properties= mock(Properties.class);
+ Map afProperties = mock(Map.class);
+ MockedStatic staticUtils = mockStatic(com.segment.analytics.internal.Utils.class);
+ when(trackPayload.event()).thenReturn(event);
+ when(trackPayload.properties()).thenReturn(properties);
+ staticUtils.when(()->com.segment.analytics.internal.Utils.transform(any(),any())).thenReturn(afProperties);
+
+ appsflyerIntegration.track(trackPayload);
+
+ Context contextInAppsflyerIntegration = (Context) TestHelper.getPrivateFieldForObject("context",AppsflyerIntegration.class,appsflyerIntegration);
+ verify(appsFlyerLib, times(1)).logEvent(contextInAppsflyerIntegration,event,afProperties);
+ verify(logger, times(1)).verbose("appsflyer.logEvent(context, %s, %s)", event, properties);
+
+ reset(appsFlyerLib,trackPayload,properties,afProperties);
+ staticUtils.close();
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/com/segment/analytics/android/integrations/appsflyer/ExampleUnitTest.java b/app/src/test/java/com/segment/analytics/android/integrations/appsflyer/ExampleUnitTest.java
deleted file mode 100644
index 210297a..0000000
--- a/app/src/test/java/com/segment/analytics/android/integrations/appsflyer/ExampleUnitTest.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.segment.analytics.android.integration.appsflyer;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * To work on unit tests, switch the Test Artifact in the Build Variants view.
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() throws Exception {
- assertEquals(4, 2 + 2);
- }
-}
\ No newline at end of file
diff --git a/app/src/test/java/com/segment/analytics/android/integrations/appsflyer/TestHelper.java b/app/src/test/java/com/segment/analytics/android/integrations/appsflyer/TestHelper.java
new file mode 100644
index 0000000..15f3ff0
--- /dev/null
+++ b/app/src/test/java/com/segment/analytics/android/integrations/appsflyer/TestHelper.java
@@ -0,0 +1,42 @@
+package com.segment.analytics.android.integrations.appsflyer;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+
+import com.appsflyer.AppsFlyerLib;
+
+import org.mockito.MockedStatic;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class TestHelper {
+ MockedStatic staticAppsFlyerLib;
+ public AppsFlyerLib mockAppsflyerLib(){
+ this.staticAppsFlyerLib = mockStatic(AppsFlyerLib.class);
+ AppsFlyerLib appsFlyerLib = mock(AppsFlyerLib.class);
+ this.staticAppsFlyerLib.when(AppsFlyerLib::getInstance).thenReturn(appsFlyerLib);
+ return appsFlyerLib;
+ }
+ public void closeMockAppsflyerLib(){
+ this.staticAppsFlyerLib.close();
+ }
+
+ public static Object getPrivateFieldForObject(String fieldName, Class classObject, Object objToGetValueFrom) throws Exception{
+ Field field = classObject.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ return field.get(classObject.cast(objToGetValueFrom));
+ }
+
+ public static void setPrivateFieldForObject(String fieldName, Class classObject, Object objToGetValueFrom, Class valueClass, Object value) throws Exception{
+ Field field = classObject.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(objToGetValueFrom,valueClass.cast(value));
+ }
+
+ public static Method getPrivateMethodForObjectReadyToInvoke(String funcName,Class>... parameterTypesForMethod) throws Exception{
+ Method getFlagMethod = AppsflyerIntegration.ConversionListener.class.getDeclaredMethod(funcName,parameterTypesForMethod);
+ getFlagMethod.setAccessible(true);
+ return getFlagMethod;
+ }
+}
diff --git a/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..1f0955d
--- /dev/null
+++ b/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
diff --git a/build.gradle b/build.gradle
index 3bf9fda..77267c5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.4.3'
+ classpath 'com.android.tools.build:gradle:7.2.2'
classpath "io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.21.2"
}
}
diff --git a/gradle.properties b/gradle.properties
index a18c791..394d461 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -19,8 +19,8 @@
GROUP=com.appsflyer
-VERSION_CODE=10
-VERSION_NAME=6.8.2
+VERSION_CODE=11
+VERSION_NAME=6.10.1
POM_ARTIFACT_ID=segment-android-integration
POM_PACKAGING=aar
@@ -40,4 +40,4 @@ POM_DEVELOPER_ID=appsflyer
POM_DEVELOPER_NAME=AppsFlyer, Inc.
android.useAndroidX=true
-android.enableJetifier=true
+android.enableJetifier=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index b0ec43a..50ecda4 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu May 30 11:45:15 IDT 2019
+#Tue Jan 10 15:43:55 IST 2023
distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
+zipStoreBase=GRADLE_USER_HOME
diff --git a/segmenttestapp/build.gradle b/segmenttestapp/build.gradle
index 0736c1d..5d50692 100644
--- a/segmenttestapp/build.gradle
+++ b/segmenttestapp/build.gradle
@@ -27,12 +27,7 @@ dependencies {
implementation project(path: ':app')
testImplementation 'junit:junit:4.12'
implementation 'com.android.support:appcompat-v7:28.0.0'
- implementation 'com.appsflyer:af-android-sdk:6.8.2'
- //noinspection GradleDynamicVersion
+ implementation 'com.appsflyer:af-android-sdk:6.10.1'
implementation 'com.segment.analytics.android:analytics:4.+'
-// compile 'com.appsflyer:segment-android-integration:6.8.2'
implementation 'com.android.installreferrer:installreferrer:2.1'
- //compile project(':app')
- // compile 'com.google.firebase:firebase-crash:9.4.0'
-}
-//apply plugin: 'com.google.gms.google-services'
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/segmenttestapp/src/main/java/com/appsflyer/segment/app/SampleApplication.java b/segmenttestapp/src/main/java/com/appsflyer/segment/app/SampleApplication.java
index 4fdc1f4..998dc15 100644
--- a/segmenttestapp/src/main/java/com/appsflyer/segment/app/SampleApplication.java
+++ b/segmenttestapp/src/main/java/com/appsflyer/segment/app/SampleApplication.java
@@ -15,7 +15,7 @@
public class SampleApplication extends Application {
- static final String SEGMENT_WRITE_KEY = "";
+ static final String SEGMENT_WRITE_KEY = "";
static final String TAG = "SEG_AF";
@Override public void onCreate() {
diff --git a/segmenttestapp/src/main/res/layout-v11/activity_main.xml b/segmenttestapp/src/main/res/layout-v11/activity_main.xml
index 35118c3..2b36d7b 100644
--- a/segmenttestapp/src/main/res/layout-v11/activity_main.xml
+++ b/segmenttestapp/src/main/res/layout-v11/activity_main.xml
@@ -1,5 +1,6 @@
+ app:srcCompat="@drawable/segment_logo" />
+ app:srcCompat="@drawable/segment_logo" />