diff --git a/libcore/src/androidTest/java/com/mapbox/android/core/metrics/AbstractCompositeMetricsInstrumentedTest.java b/libcore/src/androidTest/java/com/mapbox/android/core/metrics/AbstractCompositeMetricsInstrumentedTest.java new file mode 100644 index 000000000..cc411dff1 --- /dev/null +++ b/libcore/src/androidTest/java/com/mapbox/android/core/metrics/AbstractCompositeMetricsInstrumentedTest.java @@ -0,0 +1,32 @@ +package com.mapbox.android.core.metrics; + +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; + +public class AbstractCompositeMetricsInstrumentedTest { + @Test + public void addShortSpanMetric() throws InterruptedException { + TestCompositeMetrics metrics = new TestCompositeMetrics(TimeUnit.SECONDS.toMillis(1)); + metrics.add("test", 100L); + Thread.sleep(1000L); + metrics.add("test", 10L); + Metrics firstMetric = metrics.getMetrics("test"); + Metrics secondMetric = metrics.getMetrics("test"); + assertEquals(100L, firstMetric.getValue()); + assertEquals(10L, secondMetric.getValue()); + } + + private static final class TestCompositeMetrics extends AbstractCompositeMetrics { + TestCompositeMetrics(long maxLength) { + super(maxLength); + } + + @Override + protected Metrics nextMetrics(long start, long end) { + return new MetricsImpl(start, end); + } + } +} \ No newline at end of file diff --git a/libcore/src/main/java/com/mapbox/android/core/metrics/AbstractCompositeMetrics.java b/libcore/src/main/java/com/mapbox/android/core/metrics/AbstractCompositeMetrics.java new file mode 100644 index 000000000..e51bc987e --- /dev/null +++ b/libcore/src/main/java/com/mapbox/android/core/metrics/AbstractCompositeMetrics.java @@ -0,0 +1,82 @@ +package com.mapbox.android.core.metrics; + +import android.os.SystemClock; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Tracks stats over rolling time window of the max length. + * Optimized for write heavy metrics. + */ +public abstract class AbstractCompositeMetrics { + private final Map> metricsMap = new ConcurrentHashMap<>(); + private final long maxLength; + + /** + * Create instance of the composite metric. + * + * @param maxLength max time window in milliseconds. + */ + public AbstractCompositeMetrics(long maxLength) { + this.maxLength = maxLength; + } + + /** + * Called by child class when new metrics is needed. + * Concrete implementation of the metric is delegated to child class. + * + * @param start of the time span. + * @param end of the time span. + * @return reference to the new metric object. + */ + protected abstract Metrics nextMetrics(long start, long end); + + /** + * Adds value to the metric and occasionally creates new metric + * if the delta is out of the exiting metric span. + * + * @param name name of the metric. + * @param delta value to increment. + */ + public void add(String name, long delta) { + long now = SystemClock.uptimeMillis(); + + Metrics last; + synchronized (this) { + Deque metrics = getOrCreateMetrics(name.trim()); + if (now >= metrics.getLast().getEnd()) { + metrics.add(nextMetrics(now, now + maxLength)); + } + last = metrics.getLast(); + } + last.add(delta); + } + + @Nullable + public Metrics getMetrics(@NonNull String name) { + Deque metrics = metricsMap.get(name.trim()); + synchronized (this) { + return metrics != null && !metrics.isEmpty() ? metrics.pop() : null; + } + } + + @NonNull + private Deque getOrCreateMetrics(@NonNull String name) { + Deque metrics; + if ((metrics = metricsMap.get(name)) == null) { + metrics = new ArrayDeque<>(); + metricsMap.put(name, metrics); + } + + if (metrics.isEmpty()) { + long now = SystemClock.uptimeMillis(); + metrics.add(nextMetrics(now, now + maxLength)); + } + return metrics; + } +} diff --git a/libcore/src/main/java/com/mapbox/android/core/metrics/Metrics.java b/libcore/src/main/java/com/mapbox/android/core/metrics/Metrics.java new file mode 100644 index 000000000..65c0b9f45 --- /dev/null +++ b/libcore/src/main/java/com/mapbox/android/core/metrics/Metrics.java @@ -0,0 +1,35 @@ +package com.mapbox.android.core.metrics; + +/** + * Metrics object counter over a time span + */ +public interface Metrics { + + /** + * Increment metric + * + * @param delta value + */ + void add(long delta); + + /** + * Return current metric value + * + * @return current state of the metric + */ + long getValue(); + + /** + * Return start of the time span [start, end] + * + * @return timestamp in milliseconds. + */ + long getStart(); + + /** + * Return end of the time span [start, end] + * + * @return timestamp in milliseconds. + */ + long getEnd(); +} \ No newline at end of file diff --git a/libcore/src/main/java/com/mapbox/android/core/metrics/MetricsImpl.java b/libcore/src/main/java/com/mapbox/android/core/metrics/MetricsImpl.java new file mode 100644 index 000000000..79f715695 --- /dev/null +++ b/libcore/src/main/java/com/mapbox/android/core/metrics/MetricsImpl.java @@ -0,0 +1,73 @@ +package com.mapbox.android.core.metrics; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Default implementation of the thread safe + * metric with time span. + */ +public class MetricsImpl implements Metrics { + private final long start; + private final long end; + private final AtomicLong value; + + MetricsImpl(long start, long end, long initialValue) { + if (start > end) { + this.start = end; + this.end = start; + } else { + this.start = start; + this.end = end; + } + this.value = new AtomicLong(initialValue); + } + + /** + * Intantiate new metric with a span. + * @param start timestamp + * @param end timestamp + */ + public MetricsImpl(long start, long end) { + this(start, end, 0L); + } + + /** + * Increment metric by delta. (thread safe) + * + * @param delta value + */ + @Override + public void add(long delta) { + value.addAndGet(delta); + } + + /** + * Return metric value. (thread safe) + * + * @return metric value + */ + @Override + public long getValue() { + return value.get(); + } + + /** + * Return span start timestamp. + * + * @return timestamp in milliseconds + */ + @Override + public long getStart() { + return start; + } + + /** + * Return span end timestamp. + * + * @return timestamp in milliseconds + */ + @Override + public long getEnd() { + return end; + } +} diff --git a/libcore/src/test/java/com/mapbox/android/core/metrics/AbstractCompositeMetricsTest.java b/libcore/src/test/java/com/mapbox/android/core/metrics/AbstractCompositeMetricsTest.java new file mode 100644 index 000000000..eec397e6f --- /dev/null +++ b/libcore/src/test/java/com/mapbox/android/core/metrics/AbstractCompositeMetricsTest.java @@ -0,0 +1,66 @@ +package com.mapbox.android.core.metrics; + +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AbstractCompositeMetricsTest { + private TestCompositeMetrics metrics; + + @Before + public void setUp() { + metrics = new TestCompositeMetrics(TimeUnit.HOURS.toMillis(1)); + } + + @Test + public void addLongSpanMetric() { + metrics.add("test", 100L); + metrics.add("test", 10L); + Metrics firstMetric = metrics.getMetrics("test"); + Metrics secondMetric = metrics.getMetrics("test"); + assertThat(firstMetric.getValue()).isEqualTo(110L); + assertThat(secondMetric).isNull(); + } + + @Test + public void addMultipleMetrics() { + metrics.add("test", 100L); + metrics.add("foo", 10L); + Metrics test = metrics.getMetrics("test"); + Metrics foo = metrics.getMetrics("foo"); + assertThat(test.getValue()).isEqualTo(100L); + assertThat(foo.getValue()).isEqualTo(10L); + } + + @Test + public void addRemoveMetric() { + metrics.add("test", 100L); + metrics.getMetrics("test"); + metrics.add("test", 10L); + assertThat(metrics.getMetrics("test").getValue()).isEqualTo(10L); + } + + @Test + public void getEmptyMetric() { + assertThat(metrics.getMetrics("test")).isNull(); + } + + @Test + public void getMetricsEmptyString() { + assertThat(metrics.getMetrics("")).isNull(); + } + + private static final class TestCompositeMetrics extends AbstractCompositeMetrics { + TestCompositeMetrics(long maxLength) { + super(maxLength); + } + + @Override + protected Metrics nextMetrics(long start, long end) { + return new MetricsImpl(start, end); + } + } +} \ No newline at end of file diff --git a/libcore/src/test/java/com/mapbox/android/core/metrics/MetricsImplTest.java b/libcore/src/test/java/com/mapbox/android/core/metrics/MetricsImplTest.java new file mode 100644 index 000000000..3e01d766e --- /dev/null +++ b/libcore/src/test/java/com/mapbox/android/core/metrics/MetricsImplTest.java @@ -0,0 +1,59 @@ +package com.mapbox.android.core.metrics; + +import android.os.SystemClock; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MetricsImplTest { + + @Test + public void wrongSpan() { + long startTime = SystemClock.elapsedRealtime(); + long endTime = startTime + TimeUnit.HOURS.toMillis(24); + Metrics metrics = new MetricsImpl(endTime, startTime); + assertThat(metrics.getStart()).isEqualTo(startTime); + assertThat(metrics.getEnd()).isEqualTo(endTime); + } + + @Test + public void add() { + Metrics metrics = getMetrics(); + metrics.add(100L); + assertThat(metrics.getValue()).isEqualTo(100L); + } + + @Test + public void subtract() { + Metrics metrics = getMetrics(); + metrics.add(-100L); + assertThat(metrics.getValue()).isEqualTo(-100L); + } + + @Test + public void getValue() { + Metrics metrics = getMetrics(); + assertThat(metrics.getValue()).isEqualTo(0L); + } + + @Test + public void getStart() { + long startTime = SystemClock.elapsedRealtime(); + Metrics metrics = new MetricsImpl(startTime, startTime + TimeUnit.HOURS.toMillis(24)); + assertThat(metrics.getStart()).isEqualTo(startTime); + } + + @Test + public void getEnd() { + long endTime = SystemClock.elapsedRealtime(); + Metrics metrics = new MetricsImpl(0, endTime); + assertThat(metrics.getStart()).isEqualTo(endTime); + } + + private static Metrics getMetrics() { + long startTime = SystemClock.elapsedRealtime(); + return new MetricsImpl(startTime, startTime + TimeUnit.HOURS.toMillis(24)); + } +} \ No newline at end of file diff --git a/libtelemetry/src/main/java/com/mapbox/android/telemetry/TelemetryClient.java b/libtelemetry/src/main/java/com/mapbox/android/telemetry/TelemetryClient.java index 7cce84d6b..fa20b9c7d 100644 --- a/libtelemetry/src/main/java/com/mapbox/android/telemetry/TelemetryClient.java +++ b/libtelemetry/src/main/java/com/mapbox/android/telemetry/TelemetryClient.java @@ -150,7 +150,7 @@ private void sendBatch(List batch, Callback callback, boolean serializeNu .post(body) .build(); - OkHttpClient client = setting.getClient(certificateBlacklist); + OkHttpClient client = setting.getClient(certificateBlacklist, batch.size()); client.newCall(request).enqueue(callback); } diff --git a/libtelemetry/src/main/java/com/mapbox/android/telemetry/TelemetryClientBuild.java b/libtelemetry/src/main/java/com/mapbox/android/telemetry/TelemetryClientBuild.java deleted file mode 100644 index b521a2a39..000000000 --- a/libtelemetry/src/main/java/com/mapbox/android/telemetry/TelemetryClientBuild.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.mapbox.android.telemetry; - -interface TelemetryClientBuild { - - TelemetryClient build(ServerInformation serverInformation); -} diff --git a/libtelemetry/src/main/java/com/mapbox/android/telemetry/TelemetryClientFactory.java b/libtelemetry/src/main/java/com/mapbox/android/telemetry/TelemetryClientFactory.java index 0a97457b3..2c70cc22a 100644 --- a/libtelemetry/src/main/java/com/mapbox/android/telemetry/TelemetryClientFactory.java +++ b/libtelemetry/src/main/java/com/mapbox/android/telemetry/TelemetryClientFactory.java @@ -1,13 +1,9 @@ package com.mapbox.android.telemetry; - import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import java.util.HashMap; -import java.util.Map; - class TelemetryClientFactory { private static final String LOG_TAG = "TelemetryClientFactory"; private static final String RETRIEVING_APP_META_DATA_ERROR_MESSAGE = "Failed when retrieving app meta-data: %s"; @@ -15,29 +11,6 @@ class TelemetryClientFactory { private final String userAgent; private final Logger logger; private final CertificateBlacklist certificateBlacklist; - private final Map BUILD_TELEMETRY_CLIENT = new HashMap() { - { - put(Environment.CHINA, new TelemetryClientBuild() { - @Override - public TelemetryClient build(ServerInformation serverInformation) { - return buildTelemetryClient(Environment.CHINA, certificateBlacklist); - } - }); - put(Environment.STAGING, new TelemetryClientBuild() { - @Override - public TelemetryClient build(ServerInformation serverInformation) { - return buildTelemetryClientCustom(serverInformation, certificateBlacklist); - } - }); - put(Environment.COM, new TelemetryClientBuild() { - @Override - public TelemetryClient build(ServerInformation serverInformation) { - return buildTelemetryClient(Environment.COM, certificateBlacklist); - } - }); - } - }; TelemetryClientFactory(String accessToken, String userAgent, Logger logger, CertificateBlacklist certificateBlacklist) { @@ -48,44 +21,47 @@ public TelemetryClient build(ServerInformation serverInformation) { } TelemetryClient obtainTelemetryClient(Context context) { - EnvironmentChain environmentChain = new EnvironmentChain(); - EnvironmentResolver setupChain = environmentChain.setup(); - ServerInformation serverInformation; try { ApplicationInfo appInformation = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); if (appInformation != null && appInformation.metaData != null) { - serverInformation = setupChain.obtainServerInformation(appInformation.metaData); - return BUILD_TELEMETRY_CLIENT.get(serverInformation.getEnvironment()).build(serverInformation); + EnvironmentChain environmentChain = new EnvironmentChain(); + return buildClientFrom(environmentChain.setup().obtainServerInformation(appInformation.metaData), context); } } catch (Exception exception) { logger.error(LOG_TAG, String.format(RETRIEVING_APP_META_DATA_ERROR_MESSAGE, exception.getMessage())); } - return buildTelemetryClient(Environment.COM, certificateBlacklist); + return buildTelemetryClient(Environment.COM, certificateBlacklist, context); } - private TelemetryClient buildTelemetryClient(Environment environment, CertificateBlacklist certificateBlacklist) { - TelemetryClientSettings telemetryClientSettings = new TelemetryClientSettings.Builder() - .environment(environment) - .build(); - TelemetryClient telemetryClient = new TelemetryClient(accessToken, userAgent, telemetryClientSettings, logger, - certificateBlacklist); - - return telemetryClient; + private TelemetryClient buildTelemetryClient(Environment environment, + CertificateBlacklist certificateBlacklist, + Context context) { + return new TelemetryClient(accessToken, userAgent, + new TelemetryClientSettings.Builder(context) + .environment(environment) + .build(), + logger, certificateBlacklist); } private TelemetryClient buildTelemetryClientCustom(ServerInformation serverInformation, - CertificateBlacklist certificateBlacklist) { - Environment environment = serverInformation.getEnvironment(); - String hostname = serverInformation.getHostname(); - String accessToken = serverInformation.getAccessToken(); - TelemetryClientSettings telemetryClientSettings = new TelemetryClientSettings.Builder() - .environment(environment) - .baseUrl(TelemetryClientSettings.configureUrlHostname(hostname)) + CertificateBlacklist certificateBlacklist, + Context context) { + TelemetryClientSettings telemetryClientSettings = new TelemetryClientSettings.Builder(context) + .environment(serverInformation.getEnvironment()) + .baseUrl(TelemetryClientSettings.configureUrlHostname(serverInformation.getHostname())) .build(); - TelemetryClient telemetryClient = new TelemetryClient(accessToken, userAgent, telemetryClientSettings, logger, + return new TelemetryClient(serverInformation.getAccessToken(), userAgent, telemetryClientSettings, logger, certificateBlacklist); + } - return telemetryClient; + private TelemetryClient buildClientFrom(ServerInformation serverInformation, Context context) { + Environment environment = serverInformation.getEnvironment(); + switch (environment) { + case STAGING: + return buildTelemetryClientCustom(serverInformation, certificateBlacklist, context); + default: + return buildTelemetryClient(environment, certificateBlacklist, context); + } } } diff --git a/libtelemetry/src/main/java/com/mapbox/android/telemetry/TelemetryClientSettings.java b/libtelemetry/src/main/java/com/mapbox/android/telemetry/TelemetryClientSettings.java index 7a383c76e..81097733e 100644 --- a/libtelemetry/src/main/java/com/mapbox/android/telemetry/TelemetryClientSettings.java +++ b/libtelemetry/src/main/java/com/mapbox/android/telemetry/TelemetryClientSettings.java @@ -3,11 +3,11 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; - import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; +import android.content.Context; import okhttp3.ConnectionSpec; import okhttp3.HttpUrl; import okhttp3.Interceptor; @@ -25,6 +25,7 @@ class TelemetryClientSettings { } }; private static final String HTTPS_SCHEME = "https"; + private final Context context; private Environment environment; private final OkHttpClient client; private final HttpUrl baseUrl; @@ -34,6 +35,7 @@ class TelemetryClientSettings { private boolean debugLoggingEnabled; TelemetryClientSettings(Builder builder) { + this.context = builder.context; this.environment = builder.environment; this.client = builder.client; this.baseUrl = builder.baseUrl; @@ -47,8 +49,14 @@ Environment getEnvironment() { return environment; } - OkHttpClient getClient(CertificateBlacklist certificateBlacklist) { - return configureHttpClient(certificateBlacklist, new GzipRequestInterceptor()); + OkHttpClient getClient(CertificateBlacklist certificateBlacklist, int eventCount) { + // Order in which interceptors are added matter! + Interceptor[] interceptors = { + new GzipRequestInterceptor() }; + // TODO: add network interceptors in the following order + // new NetworkUsageInterceptor(new NetworkUsageMetricsCollector(context, metrics)), + // new NetworkErrorInterceptor(metrics, eventCount) }; + return configureHttpClient(certificateBlacklist, interceptors); } OkHttpClient getAttachmentClient(CertificateBlacklist certificateBlacklist) { @@ -64,7 +72,7 @@ boolean isDebugLoggingEnabled() { } Builder toBuilder() { - return new Builder() + return new Builder(context) .environment(environment) .client(client) .baseUrl(baseUrl) @@ -81,6 +89,7 @@ static HttpUrl configureUrlHostname(String eventsHost) { } static final class Builder { + Context context; Environment environment = Environment.COM; OkHttpClient client = new OkHttpClient(); HttpUrl baseUrl = null; @@ -89,7 +98,8 @@ static final class Builder { HostnameVerifier hostnameVerifier = null; boolean debugLoggingEnabled = false; - Builder() { + Builder(Context context) { + this.context = context; } Builder environment(Environment environment) { @@ -141,15 +151,17 @@ TelemetryClientSettings build() { } private OkHttpClient configureHttpClient(CertificateBlacklist certificateBlacklist, - Interceptor interceptor) { + Interceptor[] interceptors) { CertificatePinnerFactory factory = new CertificatePinnerFactory(); OkHttpClient.Builder builder = client.newBuilder() .retryOnConnectionFailure(true) .certificatePinner(factory.provideCertificatePinnerFor(environment, certificateBlacklist)) .connectionSpecs(Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS)); - if (interceptor != null) { - builder.addInterceptor(interceptor); + if (interceptors != null) { + for (Interceptor interceptor: interceptors) { + builder.addInterceptor(interceptor); + } } if (isSocketFactoryUnset(sslSocketFactory, x509TrustManager)) { diff --git a/libtelemetry/src/main/java/com/mapbox/android/telemetry/metrics/TelemetryMetrics.java b/libtelemetry/src/main/java/com/mapbox/android/telemetry/metrics/TelemetryMetrics.java new file mode 100644 index 000000000..a861abf27 --- /dev/null +++ b/libtelemetry/src/main/java/com/mapbox/android/telemetry/metrics/TelemetryMetrics.java @@ -0,0 +1,47 @@ +package com.mapbox.android.telemetry.metrics; + +import android.support.annotation.IntRange; +import android.support.annotation.VisibleForTesting; +import com.mapbox.android.core.metrics.AbstractCompositeMetrics; +import com.mapbox.android.core.metrics.Metrics; +import com.mapbox.android.core.metrics.MetricsImpl; + +import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.ConnectivityManager.TYPE_VPN; + +public class TelemetryMetrics extends AbstractCompositeMetrics { + public static final String EVENTS_TOTAL = "eventCountTotal"; + public static final String EVENTS_FAILED = "eventCountFailed"; + + @VisibleForTesting + static final String MOBILE_BYTES_TX = "cellDataSent"; + static final String WIFI_BYTES_TX = "wifiDataSent"; + static final String MOBILE_BYTES_RX = "cellDataReceived"; + static final String WIFI_BYTES_RX = "wifiDataReceived"; + + public TelemetryMetrics(long maxLength) { + super(maxLength); + } + + public void addRxBytesForType(@IntRange(from = TYPE_MOBILE, to = TYPE_VPN) int networkType, long bytes) { + if (isValidNetworkType(networkType)) { + add(networkType == TYPE_WIFI ? WIFI_BYTES_RX : MOBILE_BYTES_RX, bytes); + } + } + + public void addTxBytesForType(@IntRange(from = TYPE_MOBILE, to = TYPE_VPN) int networkType, long bytes) { + if (isValidNetworkType(networkType)) { + add(networkType == TYPE_WIFI ? WIFI_BYTES_TX : MOBILE_BYTES_TX, bytes); + } + } + + @Override + protected Metrics nextMetrics(long start, long end) { + return new MetricsImpl(start, end); + } + + private static boolean isValidNetworkType(int type) { + return type >= TYPE_MOBILE && type <= TYPE_VPN; + } +} diff --git a/libtelemetry/src/main/java/com/mapbox/android/telemetry/metrics/TelemetryMetricsClient.java b/libtelemetry/src/main/java/com/mapbox/android/telemetry/metrics/TelemetryMetricsClient.java new file mode 100644 index 000000000..408281536 --- /dev/null +++ b/libtelemetry/src/main/java/com/mapbox/android/telemetry/metrics/TelemetryMetricsClient.java @@ -0,0 +1,54 @@ +package com.mapbox.android.telemetry.metrics; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.VisibleForTesting; + +import java.util.concurrent.TimeUnit; + +public class TelemetryMetricsClient { + private static final String TELEMETRY_METRICS_USER_AGENT = "mapbox-android-metrics"; + private static TelemetryMetricsClient telemetryMetricsClient; + private static final Object lock = new Object(); + private final TelemetryMetrics telemetryMetrics; + + @VisibleForTesting + TelemetryMetricsClient(@NonNull TelemetryMetrics telemetryMetrics) { + this.telemetryMetrics = telemetryMetrics; + } + + public static TelemetryMetricsClient install(@NonNull Context context) { + Context applicationContext; + if (context.getApplicationContext() == null) { + // In shared processes content providers getApplicationContext() can return null. + applicationContext = context; + } else { + applicationContext = context.getApplicationContext(); + } + + // TODO: fetch metrics state file + TelemetryMetrics metrics = new TelemetryMetrics(TimeUnit.HOURS.toMillis(24)); + synchronized (lock) { + if (telemetryMetricsClient == null) { + telemetryMetricsClient = new TelemetryMetricsClient(metrics); + } + } + return telemetryMetricsClient; + } + + @NonNull + public static TelemetryMetricsClient getInstance() { + synchronized (lock) { + if (telemetryMetricsClient != null) { + return telemetryMetricsClient; + } else { + throw new IllegalStateException("TelemetryMetricsClient is not installed."); + } + } + } + + @NonNull + public TelemetryMetrics getMetrics() { + return telemetryMetrics; + } +} diff --git a/libtelemetry/src/main/java/com/mapbox/android/telemetry/metrics/network/NetworkErrorInterceptor.java b/libtelemetry/src/main/java/com/mapbox/android/telemetry/metrics/network/NetworkErrorInterceptor.java new file mode 100644 index 000000000..16d088ca1 --- /dev/null +++ b/libtelemetry/src/main/java/com/mapbox/android/telemetry/metrics/network/NetworkErrorInterceptor.java @@ -0,0 +1,30 @@ +package com.mapbox.android.telemetry.metrics.network; + +import com.mapbox.android.telemetry.metrics.TelemetryMetrics; +import okhttp3.Interceptor; +import okhttp3.Response; + +import java.io.IOException; + +import static com.mapbox.android.telemetry.metrics.TelemetryMetrics.EVENTS_FAILED; +import static com.mapbox.android.telemetry.metrics.TelemetryMetrics.EVENTS_TOTAL; + +public class NetworkErrorInterceptor implements Interceptor { + private final TelemetryMetrics metrics; + private final int eventCount; + + public NetworkErrorInterceptor(TelemetryMetrics metrics, int eventCount) { + this.metrics = metrics; + this.eventCount = eventCount; + } + + @Override + public Response intercept(Chain chain) throws IOException { + Response response = chain.proceed(chain.request()); + metrics.add(EVENTS_TOTAL, eventCount); + if (!response.isSuccessful()) { + metrics.add(EVENTS_FAILED, eventCount); + } + return response; + } +} diff --git a/libtelemetry/src/main/java/com/mapbox/android/telemetry/metrics/network/NetworkUsageInterceptor.java b/libtelemetry/src/main/java/com/mapbox/android/telemetry/metrics/network/NetworkUsageInterceptor.java new file mode 100644 index 000000000..3dbfe180a --- /dev/null +++ b/libtelemetry/src/main/java/com/mapbox/android/telemetry/metrics/network/NetworkUsageInterceptor.java @@ -0,0 +1,42 @@ +package com.mapbox.android.telemetry.metrics.network; + +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +import java.io.IOException; + +public class NetworkUsageInterceptor implements Interceptor { + private final NetworkUsageMetricsCollector metricsCollector; + + public NetworkUsageInterceptor(NetworkUsageMetricsCollector metricsCollector) { + this.metricsCollector = metricsCollector; + } + + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + RequestBody requestBody = request.body(); + if (requestBody == null) { + return chain.proceed(request); + } + + Response response; + try { + response = chain.proceed(request); + } catch (IOException ioe) { + throw ioe; + } + + metricsCollector.addTxBytes(requestBody.contentLength()); + ResponseBody responseBody = response.body(); + if (responseBody == null) { + return response; + } + + metricsCollector.addRxBytes(responseBody.contentLength()); + return response; + } +} diff --git a/libtelemetry/src/main/java/com/mapbox/android/telemetry/metrics/network/NetworkUsageMetricsCollector.java b/libtelemetry/src/main/java/com/mapbox/android/telemetry/metrics/network/NetworkUsageMetricsCollector.java new file mode 100644 index 000000000..bb1864f6d --- /dev/null +++ b/libtelemetry/src/main/java/com/mapbox/android/telemetry/metrics/network/NetworkUsageMetricsCollector.java @@ -0,0 +1,30 @@ +package com.mapbox.android.telemetry.metrics.network; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import com.mapbox.android.telemetry.metrics.TelemetryMetrics; + +public class NetworkUsageMetricsCollector { + private static final int TYPE_NONE = -1; + private final ConnectivityManager connectivityManager; + private final TelemetryMetrics metrics; + + public NetworkUsageMetricsCollector(Context context, TelemetryMetrics metrics) { + this.connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + this.metrics = metrics; + } + + void addRxBytes(long bytes) { + metrics.addRxBytesForType(getActiveNetworkType(), bytes); + } + + void addTxBytes(long bytes) { + metrics.addTxBytesForType(getActiveNetworkType(), bytes); + } + + private int getActiveNetworkType() { + NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo(); + return activeNetwork == null ? TYPE_NONE : activeNetwork.getType(); + } +} diff --git a/libtelemetry/src/test/java/com/mapbox/android/telemetry/ConfigurationClientTest.java b/libtelemetry/src/test/java/com/mapbox/android/telemetry/ConfigurationClientTest.java index fe753ff64..0dd1f26a9 100644 --- a/libtelemetry/src/test/java/com/mapbox/android/telemetry/ConfigurationClientTest.java +++ b/libtelemetry/src/test/java/com/mapbox/android/telemetry/ConfigurationClientTest.java @@ -63,7 +63,7 @@ public void setUp() throws Exception { TelemetryClientSettings settings = provideDefaultTelemetryClientSettings(); CertificateBlacklist mockedBlacklist = mock(CertificateBlacklist.class); - OkHttpClient client = settings.getClient(mockedBlacklist); + OkHttpClient client = settings.getClient(mockedBlacklist, 0); Context mockedContext = getConfigContext(); File mockedFile = mock(File.class); @@ -129,7 +129,7 @@ public void nullHandler() throws Exception { private TelemetryClientSettings provideDefaultTelemetryClientSettings() { HttpUrl localUrl = obtainBaseEndpointUrl(); - return new TelemetryClientSettings.Builder() + return new TelemetryClientSettings.Builder(mock(Context.class)) .baseUrl(localUrl) .sslSocketFactory(clientCertificates.sslSocketFactory()) .x509TrustManager(clientCertificates.trustManager()) diff --git a/libtelemetry/src/test/java/com/mapbox/android/telemetry/MockWebServerTest.java b/libtelemetry/src/test/java/com/mapbox/android/telemetry/MockWebServerTest.java index 51d23cb69..e0cec2565 100644 --- a/libtelemetry/src/test/java/com/mapbox/android/telemetry/MockWebServerTest.java +++ b/libtelemetry/src/test/java/com/mapbox/android/telemetry/MockWebServerTest.java @@ -147,8 +147,8 @@ String obtainContentFromFile(String fileName) throws IOException { return stringBuilder.toString(); } - TelemetryClient obtainATelemetryClient(String accessToken, String userAgent) { - TelemetryClientSettings telemetryClientSettings = provideDefaultTelemetryClientSettings(); + TelemetryClient obtainATelemetryClient(String accessToken, String userAgent, Context context) { + TelemetryClientSettings telemetryClientSettings = provideDefaultTelemetryClientSettings(context); Logger mockedLogger = mock(Logger.class); CertificateBlacklist mockedBlacklist = mock(CertificateBlacklist.class); return new TelemetryClient(accessToken, userAgent, telemetryClientSettings, mockedLogger, mockedBlacklist); @@ -188,10 +188,10 @@ private Buffer gunzip(Buffer gzipped) throws IOException { return result; } - TelemetryClientSettings provideDefaultTelemetryClientSettings() { + TelemetryClientSettings provideDefaultTelemetryClientSettings(Context context) { HttpUrl localUrl = obtainBaseEndpointUrl(); - return new TelemetryClientSettings.Builder() + return new TelemetryClientSettings.Builder(context) .baseUrl(localUrl) .sslSocketFactory(clientCertificates.sslSocketFactory()) .x509TrustManager(clientCertificates.trustManager()) diff --git a/libtelemetry/src/test/java/com/mapbox/android/telemetry/TelemetryClientAppUserTurnstileEventTest.java b/libtelemetry/src/test/java/com/mapbox/android/telemetry/TelemetryClientAppUserTurnstileEventTest.java index f741aa80b..f905d4a89 100644 --- a/libtelemetry/src/test/java/com/mapbox/android/telemetry/TelemetryClientAppUserTurnstileEventTest.java +++ b/libtelemetry/src/test/java/com/mapbox/android/telemetry/TelemetryClientAppUserTurnstileEventTest.java @@ -11,16 +11,16 @@ import okhttp3.Callback; -import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; public class TelemetryClientAppUserTurnstileEventTest extends MockWebServerTest { @Test public void sendsTheCorrectBodyPostingAppUserTurnstileEvent() throws Exception { - Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); + Context mockedContext = TelemetryClientTest.getMockedContext(); MapboxTelemetry.applicationContext = mockedContext; - TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent"); + TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent", + mockedContext); Event anAppUserTurnstile = new AppUserTurnstile("anySdkIdentifier", "anySdkVersion", false); List theAppUserTurnstile = obtainEvents(anAppUserTurnstile); Callback mockedCallback = mock(Callback.class); diff --git a/libtelemetry/src/test/java/com/mapbox/android/telemetry/TelemetryClientLocationEventTest.java b/libtelemetry/src/test/java/com/mapbox/android/telemetry/TelemetryClientLocationEventTest.java index 373d4a8b6..edf6e405c 100644 --- a/libtelemetry/src/test/java/com/mapbox/android/telemetry/TelemetryClientLocationEventTest.java +++ b/libtelemetry/src/test/java/com/mapbox/android/telemetry/TelemetryClientLocationEventTest.java @@ -20,12 +20,13 @@ public class TelemetryClientLocationEventTest extends MockWebServerTest { @Test public void sendsTheCorrectBodyPostingLocationEvent() throws Exception { - Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); + Context mockedContext = TelemetryClientTest.getMockedContext(); MapboxTelemetry.applicationContext = mockedContext; ActivityManager mockedActivityManager = mock(ActivityManager.class, RETURNS_DEEP_STUBS); when(mockedContext.getSystemService(Context.ACTIVITY_SERVICE)).thenReturn(mockedActivityManager); - TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent"); + TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent", + mockedContext); double aLatitude = 40.416775; double aLongitude = -3.703790; Event aLocationEvent = new LocationEvent("aSessionId", aLatitude, aLongitude, ""); diff --git a/libtelemetry/src/test/java/com/mapbox/android/telemetry/TelemetryClientTest.java b/libtelemetry/src/test/java/com/mapbox/android/telemetry/TelemetryClientTest.java index 66a3c6e12..c5792393d 100644 --- a/libtelemetry/src/test/java/com/mapbox/android/telemetry/TelemetryClientTest.java +++ b/libtelemetry/src/test/java/com/mapbox/android/telemetry/TelemetryClientTest.java @@ -2,6 +2,8 @@ import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import com.google.gson.Gson; import org.junit.Test; @@ -24,23 +26,26 @@ import okhttp3.OkHttpClient; import okhttp3.Response; +import static android.net.ConnectivityManager.TYPE_WIFI; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class TelemetryClientTest extends MockWebServerTest { @Test public void sendsContentTypeHeader() throws Exception { - Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); - MapboxTelemetry.applicationContext = mockedContext; - TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent"); + MapboxTelemetry.applicationContext = getMockedContext(); + TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent", + MapboxTelemetry.applicationContext); List mockedEvent = obtainAnEvent(); Callback mockedCallback = mock(Callback.class); enqueueMockResponse(); @@ -52,9 +57,9 @@ public void sendsContentTypeHeader() throws Exception { @Test public void sendsContentEncodingHeader() throws Exception { - Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); - MapboxTelemetry.applicationContext = mockedContext; - TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent"); + MapboxTelemetry.applicationContext = getMockedContext(); + TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent", + MapboxTelemetry.applicationContext); List mockedEvent = obtainAnEvent(); Callback mockedCallback = mock(Callback.class); enqueueMockResponse(); @@ -66,9 +71,9 @@ public void sendsContentEncodingHeader() throws Exception { @Test public void sendsPostEventRequestWithTheCorrectAccessTokenParameter() throws Exception { - Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); - MapboxTelemetry.applicationContext = mockedContext; - TelemetryClient telemetryClient = obtainATelemetryClient("theAccessToken", "anyUserAgent"); + MapboxTelemetry.applicationContext = getMockedContext(); + TelemetryClient telemetryClient = obtainATelemetryClient("theAccessToken", "anyUserAgent", + MapboxTelemetry.applicationContext); List mockedEvent = obtainAnEvent(); Callback mockedCallback = mock(Callback.class); enqueueMockResponse(); @@ -80,9 +85,9 @@ public void sendsPostEventRequestWithTheCorrectAccessTokenParameter() throws Exc @Test public void sendsUserAgentHeader() throws Exception { - Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); - MapboxTelemetry.applicationContext = mockedContext; - TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "theUserAgent"); + MapboxTelemetry.applicationContext = getMockedContext(); + TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "theUserAgent", + MapboxTelemetry.applicationContext); List mockedEvent = obtainAnEvent(); Callback mockedCallback = mock(Callback.class); enqueueMockResponse(); @@ -94,9 +99,9 @@ public void sendsUserAgentHeader() throws Exception { @Test public void sendsPostEventRequestToTheCorrectEndpoint() throws Exception { - Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); - MapboxTelemetry.applicationContext = mockedContext; - TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent"); + MapboxTelemetry.applicationContext = getMockedContext(); + TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent", + MapboxTelemetry.applicationContext); List mockedEvent = obtainAnEvent(); Callback mockedCallback = mock(Callback.class); enqueueMockResponse(); @@ -108,9 +113,9 @@ public void sendsPostEventRequestToTheCorrectEndpoint() throws Exception { @Test public void sendsTheCorrectBodyPostingAnEvent() throws Exception { - Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); - MapboxTelemetry.applicationContext = mockedContext; - TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent"); + MapboxTelemetry.applicationContext = getMockedContext(); + TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent", + MapboxTelemetry.applicationContext); List theEvent = obtainAnEvent(); Callback mockedCallback = mock(Callback.class); enqueueMockResponse(); @@ -124,9 +129,9 @@ public void sendsTheCorrectBodyPostingAnEvent() throws Exception { @Test public void receivesNoBodyPostingAnEventSuccessfully() throws Exception { - Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); - MapboxTelemetry.applicationContext = mockedContext; - TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent"); + MapboxTelemetry.applicationContext = getMockedContext(); + TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent", + MapboxTelemetry.applicationContext); List theEvent = obtainAnEvent(); Callback mockedCallback = mock(Callback.class); enqueueMockNoResponse(204); @@ -138,9 +143,9 @@ public void receivesNoBodyPostingAnEventSuccessfully() throws Exception { @Test public void parsesUnauthorizedRequestResponseProperlyPostingAnEvent() throws Exception { - Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); - MapboxTelemetry.applicationContext = mockedContext; - TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent"); + MapboxTelemetry.applicationContext = getMockedContext(); + TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent", + MapboxTelemetry.applicationContext); List theEvent = obtainAnEvent(); final CountDownLatch latch = new CountDownLatch(1); final AtomicReference bodyRef = new AtomicReference<>(); @@ -158,9 +163,9 @@ public void parsesUnauthorizedRequestResponseProperlyPostingAnEvent() throws Exc @Test public void parsesInvalidMessageBodyResponseProperlyPostingAnEvent() throws Exception { - Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); - MapboxTelemetry.applicationContext = mockedContext; - TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent"); + MapboxTelemetry.applicationContext = getMockedContext();; + TelemetryClient telemetryClient = obtainATelemetryClient("anyAccessToken", "anyUserAgent", + MapboxTelemetry.applicationContext); List theEvent = obtainAnEvent(); final CountDownLatch latch = new CountDownLatch(1); final AtomicReference bodyRef = new AtomicReference<>(); @@ -178,13 +183,13 @@ public void parsesInvalidMessageBodyResponseProperlyPostingAnEvent() throws Exce @Test public void checksRequestTimeoutFailure() throws Exception { - Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); + Context mockedContext = getMockedContext(); MapboxTelemetry.applicationContext = mockedContext; OkHttpClient localOkHttpClientWithShortTimeout = new OkHttpClient.Builder() .readTimeout(100, TimeUnit.MILLISECONDS) .build(); HttpUrl localUrl = obtainBaseEndpointUrl(); - TelemetryClientSettings telemetryClientSettings = new TelemetryClientSettings.Builder() + TelemetryClientSettings telemetryClientSettings = new TelemetryClientSettings.Builder(mockedContext) .client(localOkHttpClientWithShortTimeout) .baseUrl(localUrl) .sslSocketFactory(clientCertificates.sslSocketFactory()) @@ -213,9 +218,9 @@ public boolean verify(String hostname, SSLSession session) { @Test public void checksDebugLoggingEnabledBatch() throws Exception { - Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); + Context mockedContext = getMockedContext(); MapboxTelemetry.applicationContext = mockedContext; - TelemetryClientSettings clientSettings = provideDefaultTelemetryClientSettings(); + TelemetryClientSettings clientSettings = provideDefaultTelemetryClientSettings(mockedContext); Logger mockedLogger = mock(Logger.class); List mockedEvent = obtainAnEvent(); Callback mockedCallback = mock(Callback.class); @@ -232,9 +237,9 @@ public void checksDebugLoggingEnabledBatch() throws Exception { @Test public void checksDebugLoggingEnabledAttachment() throws Exception { - Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); + Context mockedContext = getMockedContext(); MapboxTelemetry.applicationContext = mockedContext; - TelemetryClientSettings clientSettings = provideDefaultTelemetryClientSettings(); + TelemetryClientSettings clientSettings = provideDefaultTelemetryClientSettings(mockedContext); Logger mockedLogger = mock(Logger.class); TelemetryClient telemetryClient = new TelemetryClient("anyAccessToken", "anyUserAgent", clientSettings, @@ -281,4 +286,14 @@ private void assertTelemetryResponseEquals(String responseBody, String expectedM assertEquals(expectedTelemetryResponse, actualTelemetryResponse); } + + static Context getMockedContext() { + Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); + ConnectivityManager mockedCm = mock(ConnectivityManager.class, RETURNS_DEEP_STUBS); + NetworkInfo mockedNetworkInfo = mock(NetworkInfo.class, RETURNS_DEEP_STUBS); + when(mockedNetworkInfo.getType()).thenReturn(TYPE_WIFI); + when(mockedCm.getActiveNetworkInfo()).thenReturn(mockedNetworkInfo); + when(mockedContext.getSystemService(anyString())).thenReturn(mockedCm); + return mockedContext; + } } \ No newline at end of file diff --git a/libtelemetry/src/test/java/com/mapbox/android/telemetry/metrics/TelemetryMetricsTest.java b/libtelemetry/src/test/java/com/mapbox/android/telemetry/metrics/TelemetryMetricsTest.java new file mode 100644 index 000000000..c31156927 --- /dev/null +++ b/libtelemetry/src/test/java/com/mapbox/android/telemetry/metrics/TelemetryMetricsTest.java @@ -0,0 +1,57 @@ +package com.mapbox.android.telemetry.metrics; + +import android.net.ConnectivityManager; +import com.mapbox.android.core.metrics.Metrics; +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TelemetryMetricsTest { + private TelemetryMetrics telemetryMetrics; + + @Before + public void setUp() { + telemetryMetrics = new TelemetryMetrics(TimeUnit.MINUTES.toMillis(10)); + } + + @Test + public void addRxBytesForMobileType() { + telemetryMetrics.addRxBytesForType(ConnectivityManager.TYPE_MOBILE, 10L); + Metrics metrics = telemetryMetrics.getMetrics(TelemetryMetrics.MOBILE_BYTES_RX); + assertThat(metrics.getValue()).isEqualTo(10L); + } + + @Test + public void addRxBytesForWifiType() { + telemetryMetrics.addRxBytesForType(ConnectivityManager.TYPE_WIFI, 10L); + Metrics metrics = telemetryMetrics.getMetrics(TelemetryMetrics.WIFI_BYTES_RX); + assertThat(metrics.getValue()).isEqualTo(10L); + } + + @Test + public void addTxBytesForWifiType() { + telemetryMetrics.addTxBytesForType(ConnectivityManager.TYPE_WIFI, 10L); + Metrics metrics = telemetryMetrics.getMetrics(TelemetryMetrics.WIFI_BYTES_TX); + assertThat(metrics.getValue()).isEqualTo(10L); + } + + @Test + public void addTxBytesForMobileType() { + telemetryMetrics.addTxBytesForType(ConnectivityManager.TYPE_MOBILE, 10L); + Metrics metrics = telemetryMetrics.getMetrics(TelemetryMetrics.MOBILE_BYTES_TX); + assertThat(metrics.getValue()).isEqualTo(10L); + } + + @Test + public void addBytesForUnsupportedType() { + telemetryMetrics.addTxBytesForType(1000, 10L); + telemetryMetrics.addRxBytesForType(1000, 10L); + assertThat(telemetryMetrics.getMetrics(TelemetryMetrics.MOBILE_BYTES_TX)).isNull(); + assertThat(telemetryMetrics.getMetrics(TelemetryMetrics.WIFI_BYTES_TX)).isNull(); + assertThat(telemetryMetrics.getMetrics(TelemetryMetrics.MOBILE_BYTES_RX)).isNull(); + assertThat(telemetryMetrics.getMetrics(TelemetryMetrics.WIFI_BYTES_RX)).isNull(); + } +} \ No newline at end of file diff --git a/libtelemetry/src/test/java/com/mapbox/android/telemetry/metrics/network/NetworkUsageMetricsCollectorTest.java b/libtelemetry/src/test/java/com/mapbox/android/telemetry/metrics/network/NetworkUsageMetricsCollectorTest.java new file mode 100644 index 000000000..4425553fc --- /dev/null +++ b/libtelemetry/src/test/java/com/mapbox/android/telemetry/metrics/network/NetworkUsageMetricsCollectorTest.java @@ -0,0 +1,56 @@ +package com.mapbox.android.telemetry.metrics.network; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import com.mapbox.android.telemetry.metrics.TelemetryMetrics; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static android.net.ConnectivityManager.TYPE_WIFI; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; + +@RunWith(MockitoJUnitRunner.class) +public class NetworkUsageMetricsCollectorTest { + + @Mock + private TelemetryMetrics metrics; + + private NetworkUsageMetricsCollector networkUsageMetricsCollector; + + @Before + public void setUp() { + networkUsageMetricsCollector = new NetworkUsageMetricsCollector(getMockedContext(TYPE_WIFI), metrics); + } + + @Test + public void addRxBytes() { + networkUsageMetricsCollector.addRxBytes(30); + verify(metrics).addRxBytesForType(anyInt(), anyLong()); + } + + @Test + public void addTxBytes() { + networkUsageMetricsCollector.addTxBytes(30); + verify(metrics).addTxBytesForType(anyInt(), anyLong()); + } + + private static Context getMockedContext(int networkType) { + Context mockedContext = mock(Context.class, RETURNS_DEEP_STUBS); + ConnectivityManager mockedCm = mock(ConnectivityManager.class, RETURNS_DEEP_STUBS); + NetworkInfo mockedNetworkInfo = mock(NetworkInfo.class, RETURNS_DEEP_STUBS); + when(mockedNetworkInfo.getType()).thenReturn(networkType); + when(mockedCm.getActiveNetworkInfo()).thenReturn(mockedNetworkInfo); + when(mockedContext.getSystemService(anyString())).thenReturn(mockedCm); + return mockedContext; + } +} \ No newline at end of file