Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into feature/event-type…
Browse files Browse the repository at this point in the history
…d-properties
  • Loading branch information
guperrot committed Oct 10, 2018
2 parents cca488d + 6eaf73b commit 8c08c23
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 5 deletions.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.support.annotation.VisibleForTesting;
import android.support.annotation.WorkerThread;

import com.microsoft.appcenter.AppCenter;
import com.microsoft.appcenter.channel.AbstractChannelListener;
import com.microsoft.appcenter.channel.Channel;
import com.microsoft.appcenter.ingestion.models.Log;
Expand Down Expand Up @@ -86,10 +87,16 @@ void initInBackground(Context context, Channel channel) {

/**
* Add an authentication provider to associate logs with user identifiers.
* Events tracked previous to calling this method are not impacted by this call.
* If the callback from the authentication provider takes a long time to process,
* events might be tracked anonymously.
* <p>
* If you want to wait for authentication callback to complete before processing logs,
* you can pause and resume Analytics or a specific transmission target.
*
* @param authenticationProvider The authentication provider.
*/
public static synchronized void addAuthenticationProvider(AuthenticationProvider authenticationProvider) {
public static synchronized void addAuthenticationProvider(final AuthenticationProvider authenticationProvider) {

/* Validate input. */
if (authenticationProvider == null) {
Expand All @@ -110,6 +117,27 @@ public static synchronized void addAuthenticationProvider(AuthenticationProvider
}

/* Update current provider. */
if (AppCenter.isConfigured()) {
Analytics.getInstance().postCommandEvenIfDisabled(new Runnable() {

@Override
public void run() {
updateProvider(authenticationProvider);
}
});
} else {
updateProvider(authenticationProvider);
}
}

/**
* Update authentication provider.
*
* @param authenticationProvider the new authentication provider.
*/
private static void updateProvider(AuthenticationProvider authenticationProvider) {

/* Update reference. */
sAuthenticationProvider = authenticationProvider;

/* Request token now. */
Expand Down Expand Up @@ -333,7 +361,7 @@ public void onPreparingLog(@NonNull Log log, @NonNull String groupName) {
/**
* Add ticket to common schema logs.
*/
private synchronized static void addTicketToLog(@NonNull Log log) {
private static void addTicketToLog(@NonNull Log log) {

/* Decorate only common schema logs when an authentication provider was registered. */
if (sAuthenticationProvider != null && log instanceof CommonSchemaLog) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import android.content.Context;

import com.microsoft.appcenter.AppCenter;
import com.microsoft.appcenter.AppCenterHandler;
import com.microsoft.appcenter.analytics.ingestion.models.EventLog;
import com.microsoft.appcenter.analytics.ingestion.models.one.CommonSchemaEventLog;
import com.microsoft.appcenter.channel.Channel;
Expand All @@ -15,8 +17,11 @@

import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -35,13 +40,15 @@
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.contains;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.powermock.api.mockito.PowerMockito.doAnswer;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.verifyStatic;

Expand Down Expand Up @@ -376,19 +383,33 @@ public void addAuthenticationProvider() {
}

@Test
public void addTicketToLog() {
public void addTicketToLogBeforeStart() {

/* Simulate not started. */
Analytics.unsetInstance();
when(AppCenter.isConfigured()).thenReturn(false);

/* No actions are prepared without authentication provider. */
CommonSchemaLog log = new CommonSchemaEventLog();
AnalyticsTransmissionTarget.getChannelListener().onPreparingLog(log, "test");

/* Add authentication provider. */
/* Add authentication provider before start. */
AuthenticationProvider.TokenProvider tokenProvider = mock(AuthenticationProvider.TokenProvider.class);
AuthenticationProvider authenticationProvider = spy(new AuthenticationProvider(AuthenticationProvider.Type.MSA_COMPACT, "key1", tokenProvider));
AnalyticsTransmissionTarget.addAuthenticationProvider(authenticationProvider);
assertEquals(authenticationProvider, AnalyticsTransmissionTarget.sAuthenticationProvider);
verify(authenticationProvider).acquireTokenAsync();

/* Start analytics. */
when(AppCenter.isConfigured()).thenReturn(true);
Analytics analytics = Analytics.getInstance();
AppCenterHandler handler = mock(AppCenterHandler.class);
ArgumentCaptor<Runnable> normalRunnable = ArgumentCaptor.forClass(Runnable.class);
ArgumentCaptor<Runnable> disabledRunnable = ArgumentCaptor.forClass(Runnable.class);
doNothing().when(handler).post(normalRunnable.capture(), disabledRunnable.capture());
analytics.onStarting(handler);
analytics.onStarted(mock(Context.class), mChannel, null, null, false);

/* No actions are prepared with no CommonSchemaLog. */
AnalyticsTransmissionTarget.getChannelListener().onPreparingLog(mock(Log.class), "test");
verify(authenticationProvider, never()).checkTokenExpiry();
Expand All @@ -407,6 +428,130 @@ public void addTicketToLog() {
verify(authenticationProvider).checkTokenExpiry();
}

@Test
public void updateAuthProviderAndLog() {

/* When we enqueue app center log from track event. */
final List<CommonSchemaLog> sentLogs = new ArrayList<>();
doAnswer(new Answer<Object>() {

@Override
public Object answer(InvocationOnMock invocation) {
ProtocolExtension protocol = new ProtocolExtension();
Extensions ext = new Extensions();
ext.setProtocol(protocol);
CommonSchemaLog log = new CommonSchemaEventLog();
log.setExt(ext);
sentLogs.add(log);

/* Call the listener after conversion of common schema for authentication decoration. */
AnalyticsTransmissionTarget.getChannelListener().onPreparingLog(log, "test");
return null;
}
}).when(mChannel).enqueue(any(Log.class), anyString());

/* Start analytics and simulate background thread handler (we hold the thread command and run it in the test). */
Analytics analytics = Analytics.getInstance();
AppCenterHandler handler = mock(AppCenterHandler.class);
ArgumentCaptor<Runnable> backgroundRunnable = ArgumentCaptor.forClass(Runnable.class);
doNothing().when(handler).post(backgroundRunnable.capture(), any(Runnable.class));
analytics.onStarting(handler);
analytics.onStarted(mock(Context.class), mChannel, null, "test", true);

/* Add first authentication provider. */
AuthenticationProvider.TokenProvider tokenProvider1 = mock(AuthenticationProvider.TokenProvider.class);
AuthenticationProvider authenticationProvider1 = spy(new AuthenticationProvider(AuthenticationProvider.Type.MSA_COMPACT, "key1", tokenProvider1));
AnalyticsTransmissionTarget.addAuthenticationProvider(authenticationProvider1);

/* Check provider updated in background thread only when AppCenter is configured/started. */
assertNull(AnalyticsTransmissionTarget.sAuthenticationProvider);
verify(authenticationProvider1, never()).acquireTokenAsync();
assertNotNull(backgroundRunnable.getValue());

/* Run background thread. */
backgroundRunnable.getValue().run();

/* Check update. */
assertEquals(authenticationProvider1, AnalyticsTransmissionTarget.sAuthenticationProvider);
verify(authenticationProvider1).acquireTokenAsync();

/* Track an event. */
Analytics.trackEvent("test1");
Runnable trackEvent1Command = backgroundRunnable.getValue();

/* Update authentication provider before the commands run and track a second event. */
AuthenticationProvider.TokenProvider tokenProvider2 = mock(AuthenticationProvider.TokenProvider.class);
AuthenticationProvider authenticationProvider2 = spy(new AuthenticationProvider(AuthenticationProvider.Type.MSA_COMPACT, "key2", tokenProvider2));
AnalyticsTransmissionTarget.addAuthenticationProvider(authenticationProvider2);
Runnable addAuthProvider2Command = backgroundRunnable.getValue();
Analytics.trackEvent("test2");
Runnable trackEvent2Command = backgroundRunnable.getValue();

/* Simulate background thread doing everything in a sequence. */
trackEvent1Command.run();
addAuthProvider2Command.run();
trackEvent2Command.run();

/* Verify first log has first ticket. */
assertEquals(Collections.singletonList(authenticationProvider1.getTicketKeyHash()), sentLogs.get(0).getExt().getProtocol().getTicketKeys());

/* And that we checked expiry. */
verify(authenticationProvider1).checkTokenExpiry();

/* Verify second log has the second ticket. */
assertEquals(Collections.singletonList(authenticationProvider2.getTicketKeyHash()), sentLogs.get(1).getExt().getProtocol().getTicketKeys());

/* And that we checked expiry. */
verify(authenticationProvider2).checkTokenExpiry();
}

@Test
public void registerCallbackWhenDisabledWorks() {

/* Simulate disabling and background thread. */
Analytics analytics = Analytics.getInstance();
AppCenterHandler handler = mock(AppCenterHandler.class);
ArgumentCaptor<Runnable> backgroundRunnable = ArgumentCaptor.forClass(Runnable.class);
ArgumentCaptor<Runnable> disabledRunnable = ArgumentCaptor.forClass(Runnable.class);
doNothing().when(handler).post(backgroundRunnable.capture(), disabledRunnable.capture());
analytics.onStarting(handler);
analytics.onStarted(mock(Context.class), mChannel, null, "test", true);

/* Disable. */
Analytics.setEnabled(false);
backgroundRunnable.getValue().run();

/* Add authentication provider while disabled. */
AuthenticationProvider.TokenProvider tokenProvider = mock(AuthenticationProvider.TokenProvider.class);
AuthenticationProvider authenticationProvider = spy(new AuthenticationProvider(AuthenticationProvider.Type.MSA_COMPACT, "key1", tokenProvider));
AnalyticsTransmissionTarget.addAuthenticationProvider(authenticationProvider);

/* Unlock command. */
disabledRunnable.getValue().run();

/* Verify update while disabled. */
assertEquals(authenticationProvider, AnalyticsTransmissionTarget.sAuthenticationProvider);
verify(authenticationProvider).acquireTokenAsync();

/* Enable. */
Analytics.setEnabled(true);
disabledRunnable.getValue().run();

/* Call prepare log. */
ProtocolExtension protocol = new ProtocolExtension();
Extensions ext = new Extensions();
ext.setProtocol(protocol);
CommonSchemaLog log = new CommonSchemaEventLog();
log.setExt(ext);
AnalyticsTransmissionTarget.getChannelListener().onPreparingLog(log, "test");

/* Verify log. */
assertEquals(Collections.singletonList(authenticationProvider.getTicketKeyHash()), protocol.getTicketKeys());

/* And that we check expiry. */
verify(authenticationProvider).checkTokenExpiry();
}

@Test
public void pauseResume() {

Expand Down

0 comments on commit 8c08c23

Please sign in to comment.