Migrate from `Object[]` parameters to `Stream<Arguments>`

This commit is contained in:
Chris Eager 2022-01-03 13:57:16 -08:00 committed by Chris Eager
parent f45a1c232f
commit bb27dd0c3b
6 changed files with 440 additions and 393 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -20,112 +20,121 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
class ExperimentTest {
private Timer matchTimer;
private Timer errorTimer;
private Timer matchTimer;
private Timer errorTimer;
private Experiment experiment;
private Experiment experiment;
@BeforeEach
void setUp() {
matchTimer = mock(Timer.class);
errorTimer = mock(Timer.class);
@BeforeEach
void setUp() {
matchTimer = mock(Timer.class);
errorTimer = mock(Timer.class);
experiment = new Experiment("test", matchTimer, errorTimer, mock(Timer.class), mock(Timer.class), mock(Timer.class));
}
experiment = new Experiment("test", matchTimer, errorTimer, mock(Timer.class), mock(Timer.class),
mock(Timer.class));
}
@Test
void compareFutureResult() {
experiment.compareFutureResult(12, CompletableFuture.completedFuture(12));
verify(matchTimer).record(anyLong(), eq(TimeUnit.NANOSECONDS));
}
@Test
void compareFutureResult() {
experiment.compareFutureResult(12, CompletableFuture.completedFuture(12));
verify(matchTimer).record(anyLong(), eq(TimeUnit.NANOSECONDS));
}
@Test
void compareFutureResultError() {
experiment.compareFutureResult(12, CompletableFuture.failedFuture(new RuntimeException("OH NO")));
verify(errorTimer).record(anyLong(), eq(TimeUnit.NANOSECONDS));
}
@Test
void compareFutureResultError() {
experiment.compareFutureResult(12, CompletableFuture.failedFuture(new RuntimeException("OH NO")));
verify(errorTimer).record(anyLong(), eq(TimeUnit.NANOSECONDS));
}
@Test
void compareSupplierResultMatch() {
experiment.compareSupplierResult(12, () -> 12);
verify(matchTimer).record(anyLong(), eq(TimeUnit.NANOSECONDS));
}
@Test
void compareSupplierResultMatch() {
experiment.compareSupplierResult(12, () -> 12);
verify(matchTimer).record(anyLong(), eq(TimeUnit.NANOSECONDS));
}
@Test
void compareSupplierResultError() {
experiment.compareSupplierResult(12, () -> { throw new RuntimeException("OH NO"); });
verify(errorTimer).record(anyLong(), eq(TimeUnit.NANOSECONDS));
}
@Test
void compareSupplierResultError() {
experiment.compareSupplierResult(12, () -> {
throw new RuntimeException("OH NO");
});
verify(errorTimer).record(anyLong(), eq(TimeUnit.NANOSECONDS));
}
@Test
void compareSupplierResultAsyncMatch() throws InterruptedException {
final ExecutorService experimentExecutor = Executors.newSingleThreadExecutor();
@Test
void compareSupplierResultAsyncMatch() throws InterruptedException {
final ExecutorService experimentExecutor = Executors.newSingleThreadExecutor();
experiment.compareSupplierResultAsync(12, () -> 12, experimentExecutor);
experimentExecutor.shutdown();
experimentExecutor.awaitTermination(1, TimeUnit.SECONDS);
experiment.compareSupplierResultAsync(12, () -> 12, experimentExecutor);
experimentExecutor.shutdown();
experimentExecutor.awaitTermination(1, TimeUnit.SECONDS);
verify(matchTimer).record(anyLong(), eq(TimeUnit.NANOSECONDS));
}
verify(matchTimer).record(anyLong(), eq(TimeUnit.NANOSECONDS));
}
@Test
void compareSupplierResultAsyncError() throws InterruptedException {
final ExecutorService experimentExecutor = Executors.newSingleThreadExecutor();
@Test
void compareSupplierResultAsyncError() throws InterruptedException {
final ExecutorService experimentExecutor = Executors.newSingleThreadExecutor();
experiment.compareSupplierResultAsync(12, () -> { throw new RuntimeException("OH NO"); }, experimentExecutor);
experimentExecutor.shutdown();
experimentExecutor.awaitTermination(1, TimeUnit.SECONDS);
experiment.compareSupplierResultAsync(12, () -> {
throw new RuntimeException("OH NO");
}, experimentExecutor);
experimentExecutor.shutdown();
experimentExecutor.awaitTermination(1, TimeUnit.SECONDS);
verify(errorTimer).record(anyLong(), eq(TimeUnit.NANOSECONDS));
}
verify(errorTimer).record(anyLong(), eq(TimeUnit.NANOSECONDS));
}
@Test
void compareSupplierResultAsyncRejection() {
final ExecutorService executorService = mock(ExecutorService.class);
doThrow(new RejectedExecutionException()).when(executorService).execute(any(Runnable.class));
@Test
void compareSupplierResultAsyncRejection() {
final ExecutorService executorService = mock(ExecutorService.class);
doThrow(new RejectedExecutionException()).when(executorService).execute(any(Runnable.class));
experiment.compareSupplierResultAsync(12, () -> 12, executorService);
verify(errorTimer).record(anyLong(), eq(TimeUnit.NANOSECONDS));
}
experiment.compareSupplierResultAsync(12, () -> 12, executorService);
verify(errorTimer).record(anyLong(), eq(TimeUnit.NANOSECONDS));
}
@ParameterizedTest
@MethodSource("argumentsForTestRecordResult")
public void testRecordResult(final Object expected, final Object actual, final Experiment experiment, final Timer expectedTimer) {
reset(expectedTimer);
@ParameterizedTest
@MethodSource
public void testRecordResult(final Object expected, final Object actual, final Experiment experiment,
final Timer expectedTimer) {
reset(expectedTimer);
final long durationNanos = 123;
final long durationNanos = 123;
experiment.recordResult(expected, actual, durationNanos);
verify(expectedTimer).record(durationNanos, TimeUnit.NANOSECONDS);
}
experiment.recordResult(expected, actual, durationNanos);
verify(expectedTimer).record(durationNanos, TimeUnit.NANOSECONDS);
}
@SuppressWarnings("unused")
private static Object[] argumentsForTestRecordResult() {
// Hack: parameters are set before the @Before method gets called
final Timer matchTimer = mock(Timer.class);
final Timer errorTimer = mock(Timer.class);
final Timer bothPresentMismatchTimer = mock(Timer.class);
final Timer controlNullMismatchTimer = mock(Timer.class);
final Timer experimentNullMismatchTimer = mock(Timer.class);
@SuppressWarnings("unused")
private static Stream<Arguments> testRecordResult() {
// Hack: parameters are set before the @Before method gets called
final Timer matchTimer = mock(Timer.class);
final Timer errorTimer = mock(Timer.class);
final Timer bothPresentMismatchTimer = mock(Timer.class);
final Timer controlNullMismatchTimer = mock(Timer.class);
final Timer experimentNullMismatchTimer = mock(Timer.class);
final Experiment experiment = new Experiment("test", matchTimer, errorTimer, bothPresentMismatchTimer, controlNullMismatchTimer, experimentNullMismatchTimer);
final Experiment experiment = new Experiment("test", matchTimer, errorTimer, bothPresentMismatchTimer,
controlNullMismatchTimer, experimentNullMismatchTimer);
return new Object[] {
new Object[] { 12, 12, experiment, matchTimer },
new Object[] { null, 12, experiment, controlNullMismatchTimer },
new Object[] { 12, null, experiment, experimentNullMismatchTimer },
new Object[] { 12, 17, experiment, bothPresentMismatchTimer },
new Object[] { Optional.of(12), Optional.of(12), experiment, matchTimer },
new Object[] { Optional.empty(), Optional.of(12), experiment, controlNullMismatchTimer },
new Object[] { Optional.of(12), Optional.empty(), experiment, experimentNullMismatchTimer },
new Object[] { Optional.of(12), Optional.of(17), experiment, bothPresentMismatchTimer }
};
}
return Stream.of(
Arguments.of(12, 12, experiment, matchTimer),
Arguments.of(null, 12, experiment, controlNullMismatchTimer),
Arguments.of(12, null, experiment, experimentNullMismatchTimer),
Arguments.of(12, 17, experiment, bothPresentMismatchTimer),
Arguments.of(Optional.of(12), Optional.of(12), experiment, matchTimer),
Arguments.of(Optional.empty(), Optional.of(12), experiment, controlNullMismatchTimer),
Arguments.of(Optional.of(12), Optional.empty(), experiment, experimentNullMismatchTimer),
Arguments.of(Optional.of(12), Optional.of(17), experiment, bothPresentMismatchTimer)
);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -8,81 +8,84 @@ package org.whispersystems.textsecuregcm.metrics;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
class OperatingSystemMemoryGaugeTest {
private static final String MEMINFO =
"""
MemTotal: 16052208 kB
MemFree: 4568468 kB
MemAvailable: 7702848 kB
Buffers: 636372 kB
Cached: 5019116 kB
SwapCached: 6692 kB
Active: 7746436 kB
Inactive: 2729876 kB
Active(anon): 5580980 kB
Inactive(anon): 1648108 kB
Active(file): 2165456 kB
Inactive(file): 1081768 kB
Unevictable: 443948 kB
Mlocked: 4924 kB
SwapTotal: 1003516 kB
SwapFree: 935932 kB
Dirty: 28308 kB
Writeback: 0 kB
AnonPages: 5258396 kB
Mapped: 1530740 kB
Shmem: 2419340 kB
KReclaimable: 229392 kB
Slab: 408156 kB
SReclaimable: 229392 kB
SUnreclaim: 178764 kB
KernelStack: 17360 kB
PageTables: 50436 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 9029620 kB
Committed_AS: 16681884 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 41944 kB
VmallocChunk: 0 kB
Percpu: 4240 kB
HardwareCorrupted: 0 kB
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
FileHugePages: 0 kB
FilePmdMapped: 0 kB
CmaTotal: 0 kB
CmaFree: 0 kB
HugePages_Total: 0
HugePages_Free: 7
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
DirectMap4k: 481804 kB
DirectMap2M: 14901248 kB
DirectMap1G: 2097152 kB
""";
private static final String MEMINFO =
"""
MemTotal: 16052208 kB
MemFree: 4568468 kB
MemAvailable: 7702848 kB
Buffers: 636372 kB
Cached: 5019116 kB
SwapCached: 6692 kB
Active: 7746436 kB
Inactive: 2729876 kB
Active(anon): 5580980 kB
Inactive(anon): 1648108 kB
Active(file): 2165456 kB
Inactive(file): 1081768 kB
Unevictable: 443948 kB
Mlocked: 4924 kB
SwapTotal: 1003516 kB
SwapFree: 935932 kB
Dirty: 28308 kB
Writeback: 0 kB
AnonPages: 5258396 kB
Mapped: 1530740 kB
Shmem: 2419340 kB
KReclaimable: 229392 kB
Slab: 408156 kB
SReclaimable: 229392 kB
SUnreclaim: 178764 kB
KernelStack: 17360 kB
PageTables: 50436 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 9029620 kB
Committed_AS: 16681884 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 41944 kB
VmallocChunk: 0 kB
Percpu: 4240 kB
HardwareCorrupted: 0 kB
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
FileHugePages: 0 kB
FilePmdMapped: 0 kB
CmaTotal: 0 kB
CmaFree: 0 kB
HugePages_Total: 0
HugePages_Free: 7
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
DirectMap4k: 481804 kB
DirectMap2M: 14901248 kB
DirectMap1G: 2097152 kB
""";
@ParameterizedTest
@MethodSource("argumentsForTestGetValue")
public void testGetValue(final String metricName, final long expectedValue) {
assertEquals(expectedValue, new OperatingSystemMemoryGauge(metricName).getValue(MEMINFO.lines()));
}
@ParameterizedTest
@MethodSource
void testGetValue(final String metricName, final long expectedValue) {
assertEquals(expectedValue, new OperatingSystemMemoryGauge(metricName).getValue(MEMINFO.lines()));
}
private static Object[] argumentsForTestGetValue() {
return new Object[] {
new Object[] { "MemTotal", 16052208L },
new Object[] { "Active(anon)", 5580980L },
new Object[] { "Committed_AS", 16681884L },
new Object[] { "HugePages_Free", 7L },
new Object[] { "NonsenseMetric", 0L }
};
}
@SuppressWarnings("unused")
private static Stream<Arguments> testGetValue() {
return Stream.of(
Arguments.of("MemTotal", 16052208L),
Arguments.of("Active(anon)", 5580980L),
Arguments.of("Committed_AS", 16681884L),
Arguments.of("HugePages_Free", 7L),
Arguments.of("NonsenseMetric", 0L)
);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -11,81 +11,85 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import io.micrometer.core.instrument.Tag;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
class UserAgentTagUtilTest {
@ParameterizedTest
@MethodSource("argumentsForTestGetUserAgentTags")
public void testGetUserAgentTags(final String userAgent, final List<Tag> expectedTags) {
assertEquals(new HashSet<>(expectedTags),
new HashSet<>(UserAgentTagUtil.getUserAgentTags(userAgent)));
@ParameterizedTest
@MethodSource
public void testGetUserAgentTags(final String userAgent, final List<Tag> expectedTags) {
assertEquals(new HashSet<>(expectedTags),
new HashSet<>(UserAgentTagUtil.getUserAgentTags(userAgent)));
}
private static List<Tag> platformVersionTags(String platform, String version) {
return List.of(Tag.of(UserAgentTagUtil.PLATFORM_TAG, platform), Tag.of(UserAgentTagUtil.VERSION_TAG, version));
}
@SuppressWarnings("unused")
private static Stream<Arguments> testGetUserAgentTags() {
return Stream.of(
Arguments.of("This is obviously not a reasonable User-Agent string.", UserAgentTagUtil.UNRECOGNIZED_TAGS),
Arguments.of(null, UserAgentTagUtil.UNRECOGNIZED_TAGS),
Arguments.of("Signal-Android 4.53.7 (Android 8.1)", platformVersionTags("android", "4.53.7")),
Arguments.of("Signal Desktop 1.2.3", platformVersionTags("desktop", "1.2.3")),
Arguments.of("Signal/3.9.0 (iPhone; iOS 12.2; Scale/3.00)", platformVersionTags("ios", "3.9.0")),
Arguments.of("Signal-Android 1.2.3 (Android 8.1)", UserAgentTagUtil.UNRECOGNIZED_TAGS),
Arguments.of("Signal Desktop 3.9.0", platformVersionTags("desktop", "3.9.0")),
Arguments.of("Signal/4.53.7 (iPhone; iOS 12.2; Scale/3.00)", platformVersionTags("ios", "4.53.7")),
Arguments.of("Signal-Android 4.68.3 (Android 9)", platformVersionTags("android", "4.68.3")),
Arguments.of("Signal-Android 1.2.3 (Android 4.3)", UserAgentTagUtil.UNRECOGNIZED_TAGS),
Arguments.of("Signal-Android 4.68.3.0-bobsbootlegclient", UserAgentTagUtil.UNRECOGNIZED_TAGS),
Arguments.of("Signal Desktop 1.22.45-foo-0", UserAgentTagUtil.UNRECOGNIZED_TAGS),
Arguments.of("Signal Desktop 1.34.5-beta.1-fakeclientemporium", UserAgentTagUtil.UNRECOGNIZED_TAGS),
Arguments.of("Signal Desktop 1.32.0-beta.3", UserAgentTagUtil.UNRECOGNIZED_TAGS)
);
}
@Test
void testGetUserAgentTagsFlooded() {
for (int i = 0; i < UserAgentTagUtil.MAX_VERSIONS; i++) {
UserAgentTagUtil.getUserAgentTags(String.format("Signal-Android 4.0.%d (Android 8.1)", i));
}
private static List<Tag> platformVersionTags(String platform, String version) {
return List.of(Tag.of(UserAgentTagUtil.PLATFORM_TAG, platform), Tag.of(UserAgentTagUtil.VERSION_TAG, version));
}
assertEquals(UserAgentTagUtil.OVERFLOW_TAGS,
UserAgentTagUtil.getUserAgentTags("Signal-Android 4.1.0 (Android 8.1)"));
@SuppressWarnings("unused")
private static Object[] argumentsForTestGetUserAgentTags() {
return new Object[] {
new Object[] { "This is obviously not a reasonable User-Agent string.", UserAgentTagUtil.UNRECOGNIZED_TAGS },
new Object[] { null, UserAgentTagUtil.UNRECOGNIZED_TAGS },
new Object[] { "Signal-Android 4.53.7 (Android 8.1)", platformVersionTags("android", "4.53.7") },
new Object[] { "Signal Desktop 1.2.3", platformVersionTags("desktop", "1.2.3") },
new Object[] { "Signal/3.9.0 (iPhone; iOS 12.2; Scale/3.00)", platformVersionTags("ios", "3.9.0") },
new Object[] { "Signal-Android 1.2.3 (Android 8.1)", UserAgentTagUtil.UNRECOGNIZED_TAGS },
new Object[] { "Signal Desktop 3.9.0", platformVersionTags("desktop", "3.9.0") },
new Object[] { "Signal/4.53.7 (iPhone; iOS 12.2; Scale/3.00)", platformVersionTags("ios", "4.53.7") },
new Object[] { "Signal-Android 4.68.3 (Android 9)", platformVersionTags("android", "4.68.3") },
new Object[] { "Signal-Android 1.2.3 (Android 4.3)", UserAgentTagUtil.UNRECOGNIZED_TAGS },
new Object[] { "Signal-Android 4.68.3.0-bobsbootlegclient", UserAgentTagUtil.UNRECOGNIZED_TAGS },
new Object[] { "Signal Desktop 1.22.45-foo-0", UserAgentTagUtil.UNRECOGNIZED_TAGS },
new Object[] { "Signal Desktop 1.34.5-beta.1-fakeclientemporium", UserAgentTagUtil.UNRECOGNIZED_TAGS },
new Object[] { "Signal Desktop 1.32.0-beta.3", UserAgentTagUtil.UNRECOGNIZED_TAGS },
};
}
final List<Tag> tags = UserAgentTagUtil.getUserAgentTags("Signal-Android 4.0.0 (Android 8.1)");
@Test
void testGetUserAgentTagsFlooded() {
for (int i = 0; i < UserAgentTagUtil.MAX_VERSIONS; i++) {
UserAgentTagUtil.getUserAgentTags(String.format("Signal-Android 4.0.%d (Android 8.1)", i));
}
assertEquals(2, tags.size());
assertTrue(tags.contains(Tag.of(UserAgentTagUtil.PLATFORM_TAG, "android")));
assertTrue(tags.contains(Tag.of(UserAgentTagUtil.VERSION_TAG, "4.0.0")));
}
assertEquals(UserAgentTagUtil.OVERFLOW_TAGS,
UserAgentTagUtil.getUserAgentTags("Signal-Android 4.1.0 (Android 8.1)"));
@ParameterizedTest
@MethodSource("argumentsForTestGetPlatformTag")
public void testGetPlatformTag(final String userAgent, final Tag expectedTag) {
assertEquals(expectedTag, UserAgentTagUtil.getPlatformTag(userAgent));
}
final List<Tag> tags = UserAgentTagUtil.getUserAgentTags("Signal-Android 4.0.0 (Android 8.1)");
assertEquals(2, tags.size());
assertTrue(tags.contains(Tag.of(UserAgentTagUtil.PLATFORM_TAG, "android")));
assertTrue(tags.contains(Tag.of(UserAgentTagUtil.VERSION_TAG, "4.0.0")));
}
@ParameterizedTest
@MethodSource("argumentsForTestGetPlatformTag")
public void testGetPlatformTag(final String userAgent, final Tag expectedTag) {
assertEquals(expectedTag, UserAgentTagUtil.getPlatformTag(userAgent));
}
private static Object[] argumentsForTestGetPlatformTag() {
return new Object[] {
new Object[] { "This is obviously not a reasonable User-Agent string.", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "unrecognized") },
new Object[] { null, Tag.of(UserAgentTagUtil.PLATFORM_TAG, "unrecognized") },
new Object[] { "Signal-Android 4.53.7 (Android 8.1)", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "android") },
new Object[] { "Signal Desktop 1.2.3", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "desktop") },
new Object[] { "Signal/3.9.0 (iPhone; iOS 12.2; Scale/3.00)", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "ios") },
new Object[] { "Signal-Android 1.2.3 (Android 8.1)", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "android") },
new Object[] { "Signal Desktop 3.9.0", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "desktop") },
new Object[] { "Signal/4.53.7 (iPhone; iOS 12.2; Scale/3.00)", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "ios") },
new Object[] { "Signal-Android 4.68.3 (Android 9)", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "android") },
new Object[] { "Signal-Android 1.2.3 (Android 4.3)", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "android") },
new Object[] { "Signal-Android 4.68.3.0-bobsbootlegclient", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "android") },
new Object[] { "Signal Desktop 1.22.45-foo-0", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "desktop") },
new Object[] { "Signal Desktop 1.34.5-beta.1-fakeclientemporium", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "desktop") },
new Object[] { "Signal Desktop 1.32.0-beta.3", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "desktop") },
};
}
private static Stream<Arguments> argumentsForTestGetPlatformTag() {
return Stream.of(
Arguments.of("This is obviously not a reasonable User-Agent string.",
Tag.of(UserAgentTagUtil.PLATFORM_TAG, "unrecognized")),
Arguments.of(null, Tag.of(UserAgentTagUtil.PLATFORM_TAG, "unrecognized")),
Arguments.of("Signal-Android 4.53.7 (Android 8.1)", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "android")),
Arguments.of("Signal Desktop 1.2.3", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "desktop")),
Arguments.of("Signal/3.9.0 (iPhone; iOS 12.2; Scale/3.00)", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "ios")),
Arguments.of("Signal-Android 1.2.3 (Android 8.1)", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "android")),
Arguments.of("Signal Desktop 3.9.0", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "desktop")),
Arguments.of("Signal/4.53.7 (iPhone; iOS 12.2; Scale/3.00)", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "ios")),
Arguments.of("Signal-Android 4.68.3 (Android 9)", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "android")),
Arguments.of("Signal-Android 1.2.3 (Android 4.3)", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "android")),
Arguments.of("Signal-Android 4.68.3.0-bobsbootlegclient", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "android")),
Arguments.of("Signal Desktop 1.22.45-foo-0", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "desktop")),
Arguments.of("Signal Desktop 1.34.5-beta.1-fakeclientemporium",
Tag.of(UserAgentTagUtil.PLATFORM_TAG, "desktop")),
Arguments.of("Signal Desktop 1.32.0-beta.3", Tag.of(UserAgentTagUtil.PLATFORM_TAG, "desktop"))
);
}
}

View File

@ -1,3 +1,8 @@
/*
* Copyright 2021-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.sms;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
@ -6,7 +11,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
@ -16,11 +21,13 @@ import java.util.Collections;
import java.util.List;
import java.util.Locale.LanguageRange;
import java.util.Optional;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.whispersystems.textsecuregcm.configuration.TwilioConfiguration;
import org.whispersystems.textsecuregcm.http.FaultTolerantHttpClient;
@ -89,7 +96,7 @@ class TwilioVerifySenderTest {
}
@ParameterizedTest
@MethodSource("argumentsForDeliverSmsVerificationWithVerify")
@MethodSource
void deliverSmsVerificationWithVerify(@Nullable final String client, @Nullable final String languageRange,
final boolean expectAppHash, @Nullable final String expectedLocale) throws Exception {
@ -114,17 +121,18 @@ class TwilioVerifySenderTest {
)));
}
private static Object[] argumentsForDeliverSmsVerificationWithVerify() {
return new Object[][]{
@SuppressWarnings("unused")
private static Stream<Arguments> deliverSmsVerificationWithVerify() {
return Stream.of(
// client, languageRange, expectAppHash, expectedLocale
{"ios", "fr-CA, en", false, "fr"},
{"android-2021-03", "zh-HK, it", true, "zh-HK"},
{null, null, false, null}
};
Arguments.of("ios", "fr-CA, en", false, "fr"),
Arguments.of("android-2021-03", "zh-HK, it", true, "zh-HK"),
Arguments.of(null, null, false, null)
);
}
@ParameterizedTest
@MethodSource("argumentsForDeliverVoxVerificationWithVerify")
@MethodSource
void deliverVoxVerificationWithVerify(@Nullable final String languageRange,
@Nullable final String expectedLocale) throws Exception {
@ -147,14 +155,15 @@ class TwilioVerifySenderTest {
+ "&CustomCode=123456")));
}
private static Object[] argumentsForDeliverVoxVerificationWithVerify() {
return new Object[][]{
@SuppressWarnings("unused")
private static Stream<Arguments> deliverVoxVerificationWithVerify() {
return Stream.of(
// languageRange, expectedLocale
{"fr-CA, en", "fr"},
{"zh-HK, it", "zh-HK"},
{"en-CAA, en", "en"},
{null, null}
};
Arguments.of("fr-CA, en", "fr"),
Arguments.of("zh-HK, it", "zh-HK"),
Arguments.of("en-CAA, en", "en"),
Arguments.of(null, null)
);
}
@Test
@ -203,16 +212,17 @@ class TwilioVerifySenderTest {
wireMock.stubFor(post(urlEqualTo("/v2/Services/" + VERIFY_SERVICE_SID + "/Verifications/" + VERIFICATION_SID))
.withBasicAuth(ACCOUNT_ID, ACCOUNT_TOKEN)
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{\"status\": \"approved\", \"sid\": \"" + VERIFICATION_SID + "\"}")));
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{\"status\": \"approved\", \"sid\": \"" + VERIFICATION_SID + "\"}")));
final Boolean success = sender.reportVerificationSucceeded(VERIFICATION_SID).get();
assertThat(success).isTrue();
wireMock.verify(1, postRequestedFor(urlEqualTo("/v2/Services/" + VERIFY_SERVICE_SID + "/Verifications/" + VERIFICATION_SID))
.withHeader("Content-Type", equalTo("application/x-www-form-urlencoded"))
.withRequestBody(equalTo("Status=approved")));
wireMock.verify(1,
postRequestedFor(urlEqualTo("/v2/Services/" + VERIFY_SERVICE_SID + "/Verifications/" + VERIFICATION_SID))
.withHeader("Content-Type", equalTo("application/x-www-form-urlencoded"))
.withRequestBody(equalTo("Status=approved")));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -9,113 +9,119 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import java.time.Duration;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.whispersystems.textsecuregcm.entities.SignedPreKey;
class DeviceTest {
@ParameterizedTest
@MethodSource("argumentsForTestIsEnabled")
void testIsEnabled(final boolean master, final boolean fetchesMessages, final String apnId, final String gcmId, final SignedPreKey signedPreKey, final Duration timeSinceLastSeen, final boolean expectEnabled) {
final long lastSeen = System.currentTimeMillis() - timeSinceLastSeen.toMillis();
final Device device = new Device(master ? 1 : 2, "test", "auth-token", "salt", gcmId, apnId, null, fetchesMessages, 1, signedPreKey, lastSeen, lastSeen, "user-agent", 0, null);
@ParameterizedTest
@MethodSource
void testIsEnabled(final boolean master, final boolean fetchesMessages, final String apnId, final String gcmId,
final SignedPreKey signedPreKey, final Duration timeSinceLastSeen, final boolean expectEnabled) {
final long lastSeen = System.currentTimeMillis() - timeSinceLastSeen.toMillis();
final Device device = new Device(master ? 1 : 2, "test", "auth-token", "salt", gcmId, apnId, null, fetchesMessages,
1, signedPreKey, lastSeen, lastSeen, "user-agent", 0, null);
assertEquals(expectEnabled, device.isEnabled());
}
assertEquals(expectEnabled, device.isEnabled());
}
private static Object[] argumentsForTestIsEnabled() {
return new Object[] {
// master fetchesMessages apnId gcmId signedPreKey lastSeen expectEnabled
new Object[] { true, false, null, null, null, Duration.ofDays(60), false },
new Object[] { true, false, null, null, null, Duration.ofDays(1), false },
new Object[] { true, false, null, null, mock(SignedPreKey.class), Duration.ofDays(60), false },
new Object[] { true, false, null, null, mock(SignedPreKey.class), Duration.ofDays(1), false },
new Object[] { true, false, null, "gcm-id", null, Duration.ofDays(60), false },
new Object[] { true, false, null, "gcm-id", null, Duration.ofDays(1), false },
new Object[] { true, false, null, "gcm-id", mock(SignedPreKey.class), Duration.ofDays(60), true },
new Object[] { true, false, null, "gcm-id", mock(SignedPreKey.class), Duration.ofDays(1), true },
new Object[] { true, false, "apn-id", null, null, Duration.ofDays(60), false },
new Object[] { true, false, "apn-id", null, null, Duration.ofDays(1), false },
new Object[] { true, false, "apn-id", null, mock(SignedPreKey.class), Duration.ofDays(60), true },
new Object[] { true, false, "apn-id", null, mock(SignedPreKey.class), Duration.ofDays(1), true },
new Object[] { true, true, null, null, null, Duration.ofDays(60), false },
new Object[] { true, true, null, null, null, Duration.ofDays(1), false },
new Object[] { true, true, null, null, mock(SignedPreKey.class), Duration.ofDays(60), true },
new Object[] { true, true, null, null, mock(SignedPreKey.class), Duration.ofDays(1), true },
new Object[] { false, false, null, null, null, Duration.ofDays(60), false },
new Object[] { false, false, null, null, null, Duration.ofDays(1), false },
new Object[] { false, false, null, null, mock(SignedPreKey.class), Duration.ofDays(60), false },
new Object[] { false, false, null, null, mock(SignedPreKey.class), Duration.ofDays(1), false },
new Object[] { false, false, null, "gcm-id", null, Duration.ofDays(60), false },
new Object[] { false, false, null, "gcm-id", null, Duration.ofDays(1), false },
new Object[] { false, false, null, "gcm-id", mock(SignedPreKey.class), Duration.ofDays(60), false },
new Object[] { false, false, null, "gcm-id", mock(SignedPreKey.class), Duration.ofDays(1), true },
new Object[] { false, false, "apn-id", null, null, Duration.ofDays(60), false },
new Object[] { false, false, "apn-id", null, null, Duration.ofDays(1), false },
new Object[] { false, false, "apn-id", null, mock(SignedPreKey.class), Duration.ofDays(60), false },
new Object[] { false, false, "apn-id", null, mock(SignedPreKey.class), Duration.ofDays(1), true },
new Object[] { false, true, null, null, null, Duration.ofDays(60), false },
new Object[] { false, true, null, null, null, Duration.ofDays(1), false },
new Object[] { false, true, null, null, mock(SignedPreKey.class), Duration.ofDays(60), false },
new Object[] { false, true, null, null, mock(SignedPreKey.class), Duration.ofDays(1), true }
};
}
private static Stream<Arguments> testIsEnabled() {
return Stream.of(
// master fetchesMessages apnId gcmId signedPreKey lastSeen expectEnabled
Arguments.of(true, false, null, null, null, Duration.ofDays(60), false),
Arguments.of(true, false, null, null, null, Duration.ofDays(1), false),
Arguments.of(true, false, null, null, mock(SignedPreKey.class), Duration.ofDays(60), false),
Arguments.of(true, false, null, null, mock(SignedPreKey.class), Duration.ofDays(1), false),
Arguments.of(true, false, null, "gcm-id", null, Duration.ofDays(60), false),
Arguments.of(true, false, null, "gcm-id", null, Duration.ofDays(1), false),
Arguments.of(true, false, null, "gcm-id", mock(SignedPreKey.class), Duration.ofDays(60), true),
Arguments.of(true, false, null, "gcm-id", mock(SignedPreKey.class), Duration.ofDays(1), true),
Arguments.of(true, false, "apn-id", null, null, Duration.ofDays(60), false),
Arguments.of(true, false, "apn-id", null, null, Duration.ofDays(1), false),
Arguments.of(true, false, "apn-id", null, mock(SignedPreKey.class), Duration.ofDays(60), true),
Arguments.of(true, false, "apn-id", null, mock(SignedPreKey.class), Duration.ofDays(1), true),
Arguments.of(true, true, null, null, null, Duration.ofDays(60), false),
Arguments.of(true, true, null, null, null, Duration.ofDays(1), false),
Arguments.of(true, true, null, null, mock(SignedPreKey.class), Duration.ofDays(60), true),
Arguments.of(true, true, null, null, mock(SignedPreKey.class), Duration.ofDays(1), true),
Arguments.of(false, false, null, null, null, Duration.ofDays(60), false),
Arguments.of(false, false, null, null, null, Duration.ofDays(1), false),
Arguments.of(false, false, null, null, mock(SignedPreKey.class), Duration.ofDays(60), false),
Arguments.of(false, false, null, null, mock(SignedPreKey.class), Duration.ofDays(1), false),
Arguments.of(false, false, null, "gcm-id", null, Duration.ofDays(60), false),
Arguments.of(false, false, null, "gcm-id", null, Duration.ofDays(1), false),
Arguments.of(false, false, null, "gcm-id", mock(SignedPreKey.class), Duration.ofDays(60), false),
Arguments.of(false, false, null, "gcm-id", mock(SignedPreKey.class), Duration.ofDays(1), true),
Arguments.of(false, false, "apn-id", null, null, Duration.ofDays(60), false),
Arguments.of(false, false, "apn-id", null, null, Duration.ofDays(1), false),
Arguments.of(false, false, "apn-id", null, mock(SignedPreKey.class), Duration.ofDays(60), false),
Arguments.of(false, false, "apn-id", null, mock(SignedPreKey.class), Duration.ofDays(1), true),
Arguments.of(false, true, null, null, null, Duration.ofDays(60), false),
Arguments.of(false, true, null, null, null, Duration.ofDays(1), false),
Arguments.of(false, true, null, null, mock(SignedPreKey.class), Duration.ofDays(60), false),
Arguments.of(false, true, null, null, mock(SignedPreKey.class), Duration.ofDays(1), true)
);
}
@ParameterizedTest
@MethodSource("argumentsForTestIsGroupsV2Supported")
void testIsGroupsV2Supported(final boolean master, final String apnId, final boolean gv2Capability, final boolean gv2_2Capability, final boolean gv2_3Capability, final boolean expectGv2Supported) {
final Device.DeviceCapabilities capabilities = new Device.DeviceCapabilities(gv2Capability, gv2_2Capability, gv2_3Capability, false, false, false,
false, false, false);
final Device device = new Device(master ? 1 : 2, "test", "auth-token", "salt",
null, apnId, null, false, 1, null, 0, 0, "user-agent", 0, capabilities);
@ParameterizedTest
@MethodSource("argumentsForTestIsGroupsV2Supported")
void testIsGroupsV2Supported(final boolean master, final String apnId, final boolean gv2Capability,
final boolean gv2_2Capability, final boolean gv2_3Capability, final boolean expectGv2Supported) {
final Device.DeviceCapabilities capabilities = new Device.DeviceCapabilities(gv2Capability, gv2_2Capability,
gv2_3Capability, false, false, false,
false, false, false);
final Device device = new Device(master ? 1 : 2, "test", "auth-token", "salt",
null, apnId, null, false, 1, null, 0, 0, "user-agent", 0, capabilities);
assertEquals(expectGv2Supported, device.isGroupsV2Supported());
}
assertEquals(expectGv2Supported, device.isGroupsV2Supported());
}
private static Object[] argumentsForTestIsGroupsV2Supported() {
return new Object[] {
// master apnId gv2 gv2-2 gv2-3 capable
private static Stream<Arguments> argumentsForTestIsGroupsV2Supported() {
return Stream.of(
// master apnId gv2 gv2-2 gv2-3 capable
// Android master
new Object[] { true, null, false, false, false, false },
new Object[] { true, null, true, false, false, false },
new Object[] { true, null, false, true, false, false },
new Object[] { true, null, true, true, false, false },
new Object[] { true, null, false, false, true, true },
new Object[] { true, null, true, false, true, true },
new Object[] { true, null, false, true, true, true },
new Object[] { true, null, true, true, true, true },
// Android master
Arguments.of(true, null, false, false, false, false),
Arguments.of(true, null, true, false, false, false),
Arguments.of(true, null, false, true, false, false),
Arguments.of(true, null, true, true, false, false),
Arguments.of(true, null, false, false, true, true),
Arguments.of(true, null, true, false, true, true),
Arguments.of(true, null, false, true, true, true),
Arguments.of(true, null, true, true, true, true),
// iOs master
new Object[] { true, "apn-id", false, false, false, false },
new Object[] { true, "apn-id", true, false, false, false },
new Object[] { true, "apn-id", false, true, false, true },
new Object[] { true, "apn-id", true, true, false, true },
new Object[] { true, "apn-id", false, false, true, true },
new Object[] { true, "apn-id", true, false, true, true },
new Object[] { true, "apn-id", false, true, true, true },
new Object[] { true, "apn-id", true, true, true, true },
// iOS master
Arguments.of(true, "apn-id", false, false, false, false),
Arguments.of(true, "apn-id", true, false, false, false),
Arguments.of(true, "apn-id", false, true, false, true),
Arguments.of(true, "apn-id", true, true, false, true),
Arguments.of(true, "apn-id", false, false, true, true),
Arguments.of(true, "apn-id", true, false, true, true),
Arguments.of(true, "apn-id", false, true, true, true),
Arguments.of(true, "apn-id", true, true, true, true),
// iOs linked
new Object[] { false, "apn-id", false, false, false, false },
new Object[] { false, "apn-id", true, false, false, false },
new Object[] { false, "apn-id", false, true, false, true },
new Object[] { false, "apn-id", true, true, false, true },
new Object[] { false, "apn-id", false, false, true, true },
new Object[] { false, "apn-id", true, false, true, true },
new Object[] { false, "apn-id", false, true, true, true },
new Object[] { false, "apn-id", true, true, true, true },
// iOS linked
Arguments.of(false, "apn-id", false, false, false, false),
Arguments.of(false, "apn-id", true, false, false, false),
Arguments.of(false, "apn-id", false, true, false, true),
Arguments.of(false, "apn-id", true, true, false, true),
Arguments.of(false, "apn-id", false, false, true, true),
Arguments.of(false, "apn-id", true, false, true, true),
Arguments.of(false, "apn-id", false, true, true, true),
Arguments.of(false, "apn-id", true, true, true, true),
// desktop linked
new Object[] { false, null, false, false, false, false },
new Object[] { false, null, true, false, false, false },
new Object[] { false, null, false, true, false, false },
new Object[] { false, null, true, true, false, false },
new Object[] { false, null, false, false, true, true },
new Object[] { false, null, true, false, true, true },
new Object[] { false, null, false, true, true, true },
new Object[] { false, null, true, true, true, true }
};
}
// desktop linked
Arguments.of(false, null, false, false, false, false),
Arguments.of(false, null, true, false, false, false),
Arguments.of(false, null, false, true, false, false),
Arguments.of(false, null, true, true, false, false),
Arguments.of(false, null, false, false, true, true),
Arguments.of(false, null, true, false, true, true),
Arguments.of(false, null, false, true, true, true),
Arguments.of(false, null, true, true, true, true)
);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -9,73 +9,88 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.vdurmont.semver4j.Semver;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
class UserAgentUtilTest {
@ParameterizedTest
@MethodSource("argumentsForTestParseUserAgentString")
void testParseUserAgentString(final String userAgentString, final UserAgent expectedUserAgent) throws UnrecognizedUserAgentException {
assertEquals(expectedUserAgent, UserAgentUtil.parseUserAgentString(userAgentString));
}
@ParameterizedTest
@MethodSource
void testParseUserAgentString(final String userAgentString, final UserAgent expectedUserAgent)
throws UnrecognizedUserAgentException {
assertEquals(expectedUserAgent, UserAgentUtil.parseUserAgentString(userAgentString));
}
private static Object[] argumentsForTestParseUserAgentString() {
return new Object[] {
new Object[] { "Signal-Android/4.68.3 Android/25", new UserAgent(ClientPlatform.ANDROID, new Semver("4.68.3"), "Android/25") },
new Object[] { "Signal-Android 4.53.7 (Android 8.1)", new UserAgent(ClientPlatform.ANDROID, new Semver("4.53.7"), "(Android 8.1)") },
};
}
@SuppressWarnings("unused")
private static Stream<Arguments> testParseUserAgentString() {
return Stream.of(
Arguments.of("Signal-Android/4.68.3 Android/25",
new UserAgent(ClientPlatform.ANDROID, new Semver("4.68.3"), "Android/25")),
Arguments.of("Signal-Android 4.53.7 (Android 8.1)",
new UserAgent(ClientPlatform.ANDROID, new Semver("4.53.7"), "(Android 8.1)"))
);
}
@ParameterizedTest
@MethodSource("argumentsForTestParseBogusUserAgentString")
void testParseBogusUserAgentString(final String userAgentString) {
assertThrows(UnrecognizedUserAgentException.class, () -> UserAgentUtil.parseUserAgentString(userAgentString));
}
@ParameterizedTest
@MethodSource
void testParseBogusUserAgentString(final String userAgentString) {
assertThrows(UnrecognizedUserAgentException.class, () -> UserAgentUtil.parseUserAgentString(userAgentString));
}
private static Object[] argumentsForTestParseBogusUserAgentString() {
return new Object[] {
null,
"This is obviously not a reasonable User-Agent string.",
"Signal-Android/4.6-8.3.unreasonableversionstring-17"
};
}
@SuppressWarnings("unused")
private static Stream<String> testParseBogusUserAgentString() {
return Stream.of(
null,
"This is obviously not a reasonable User-Agent string.",
"Signal-Android/4.6-8.3.unreasonableversionstring-17"
);
}
@ParameterizedTest
@MethodSource("argumentsForTestParseStandardUserAgentString")
void testParseStandardUserAgentString(final String userAgentString, final UserAgent expectedUserAgent) {
assertEquals(expectedUserAgent, UserAgentUtil.parseStandardUserAgentString(userAgentString));
}
@ParameterizedTest
@MethodSource("argumentsForTestParseStandardUserAgentString")
void testParseStandardUserAgentString(final String userAgentString, final UserAgent expectedUserAgent) {
assertEquals(expectedUserAgent, UserAgentUtil.parseStandardUserAgentString(userAgentString));
}
private static Object[] argumentsForTestParseStandardUserAgentString() {
return new Object[] {
new Object[] { "This is obviously not a reasonable User-Agent string.", null },
new Object[] { "Signal-Android/4.68.3 Android/25", new UserAgent(ClientPlatform.ANDROID, new Semver("4.68.3"), "Android/25") },
new Object[] { "Signal-Android/4.68.3", new UserAgent(ClientPlatform.ANDROID, new Semver("4.68.3")) },
new Object[] { "Signal-Desktop/1.2.3 Linux", new UserAgent(ClientPlatform.DESKTOP, new Semver("1.2.3"), "Linux") },
new Object[] { "Signal-Desktop/1.2.3 macOS", new UserAgent(ClientPlatform.DESKTOP, new Semver("1.2.3"), "macOS") },
new Object[] { "Signal-Desktop/1.2.3 Windows", new UserAgent(ClientPlatform.DESKTOP, new Semver("1.2.3"), "Windows") },
new Object[] { "Signal-Desktop/1.2.3", new UserAgent(ClientPlatform.DESKTOP, new Semver("1.2.3")) },
new Object[] { "Signal-Desktop/1.32.0-beta.3", new UserAgent(ClientPlatform.DESKTOP, new Semver("1.32.0-beta.3")) },
new Object[] { "Signal-iOS/3.9.0 (iPhone; iOS 12.2; Scale/3.00)", new UserAgent(ClientPlatform.IOS, new Semver("3.9.0"), "(iPhone; iOS 12.2; Scale/3.00)") },
new Object[] { "Signal-iOS/3.9.0 iOS/14.2", new UserAgent(ClientPlatform.IOS, new Semver("3.9.0"), "iOS/14.2") },
new Object[] { "Signal-iOS/3.9.0", new UserAgent(ClientPlatform.IOS, new Semver("3.9.0")) }
};
}
private static Stream<Arguments> argumentsForTestParseStandardUserAgentString() {
return Stream.of(
Arguments.of("This is obviously not a reasonable User-Agent string.", null),
Arguments.of("Signal-Android/4.68.3 Android/25",
new UserAgent(ClientPlatform.ANDROID, new Semver("4.68.3"), "Android/25")),
Arguments.of("Signal-Android/4.68.3", new UserAgent(ClientPlatform.ANDROID, new Semver("4.68.3"))),
Arguments.of("Signal-Desktop/1.2.3 Linux", new UserAgent(ClientPlatform.DESKTOP, new Semver("1.2.3"), "Linux")),
Arguments.of("Signal-Desktop/1.2.3 macOS", new UserAgent(ClientPlatform.DESKTOP, new Semver("1.2.3"), "macOS")),
Arguments.of("Signal-Desktop/1.2.3 Windows",
new UserAgent(ClientPlatform.DESKTOP, new Semver("1.2.3"), "Windows")),
Arguments.of("Signal-Desktop/1.2.3", new UserAgent(ClientPlatform.DESKTOP, new Semver("1.2.3"))),
Arguments.of("Signal-Desktop/1.32.0-beta.3",
new UserAgent(ClientPlatform.DESKTOP, new Semver("1.32.0-beta.3"))),
Arguments.of("Signal-iOS/3.9.0 (iPhone; iOS 12.2; Scale/3.00)",
new UserAgent(ClientPlatform.IOS, new Semver("3.9.0"), "(iPhone; iOS 12.2; Scale/3.00)")),
Arguments.of("Signal-iOS/3.9.0 iOS/14.2", new UserAgent(ClientPlatform.IOS, new Semver("3.9.0"), "iOS/14.2")),
Arguments.of("Signal-iOS/3.9.0", new UserAgent(ClientPlatform.IOS, new Semver("3.9.0")))
);
}
@ParameterizedTest
@MethodSource("argumentsForTestParseLegacyUserAgentString")
void testParseLegacyUserAgentString(final String userAgentString, final UserAgent expectedUserAgent) {
assertEquals(expectedUserAgent, UserAgentUtil.parseLegacyUserAgentString(userAgentString));
}
@ParameterizedTest
@MethodSource
void testParseLegacyUserAgentString(final String userAgentString, final UserAgent expectedUserAgent) {
assertEquals(expectedUserAgent, UserAgentUtil.parseLegacyUserAgentString(userAgentString));
}
private static Object[] argumentsForTestParseLegacyUserAgentString() {
return new Object[] {
new Object[] { "This is obviously not a reasonable User-Agent string.", null },
new Object[] { "Signal-Android 4.53.7 (Android 8.1)", new UserAgent(ClientPlatform.ANDROID, new Semver("4.53.7"), "(Android 8.1)") },
new Object[] { "Signal Desktop 1.2.3", new UserAgent(ClientPlatform.DESKTOP, new Semver("1.2.3")) },
new Object[] { "Signal Desktop 1.32.0-beta.3", new UserAgent(ClientPlatform.DESKTOP, new Semver("1.32.0-beta.3")) },
new Object[] { "Signal/3.9.0 (iPhone; iOS 12.2; Scale/3.00)", new UserAgent(ClientPlatform.IOS, new Semver("3.9.0"), "(iPhone; iOS 12.2; Scale/3.00)") }
};
}
@SuppressWarnings("unused")
private static Stream<Arguments> testParseLegacyUserAgentString() {
return Stream.of(
Arguments.of("This is obviously not a reasonable User-Agent string.", null),
Arguments.of("Signal-Android 4.53.7 (Android 8.1)",
new UserAgent(ClientPlatform.ANDROID, new Semver("4.53.7"), "(Android 8.1)")),
Arguments.of("Signal Desktop 1.2.3", new UserAgent(ClientPlatform.DESKTOP, new Semver("1.2.3"))),
Arguments.of("Signal Desktop 1.32.0-beta.3",
new UserAgent(ClientPlatform.DESKTOP, new Semver("1.32.0-beta.3"))),
Arguments.of("Signal/3.9.0 (iPhone; iOS 12.2; Scale/3.00)",
new UserAgent(ClientPlatform.IOS, new Semver("3.9.0"), "(iPhone; iOS 12.2; Scale/3.00)"))
);
}
}