Skip to content

Commit

Permalink
Release 3.3.0 (#519)
Browse files Browse the repository at this point in the history
  • Loading branch information
gthea authored Jul 18, 2023
2 parents 9494c3b + 337688c commit dee2432
Show file tree
Hide file tree
Showing 76 changed files with 3,157 additions and 412 deletions.
1 change: 1 addition & 0 deletions .github/workflows/instrumented.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ jobs:
uses: reactivecircus/[email protected]
with:
api-level: ${{ matrix.api-level }}
avd-name: macOS-avd-arm64-v8a-29
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
Expand Down
4 changes: 4 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
3.3.0 (Jul 18, 2023)
- Improved streaming architecture implementation to apply feature flag updates from the notification received which is now enhanced, improving efficiency and reliability of the whole update system.
- Added logic to do a full check of feature flags immediately when the app comes back to foreground, limited to once per minute.

3.2.2 (Jun 7, 2023)
- Refactored cipher creation to avoid NPE scenarios.

Expand Down
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ apply plugin: 'signing'
apply plugin: 'kotlin-android'

ext {
splitVersion = '3.2.2'
splitVersion = '3.3.0'
}

android {
Expand Down Expand Up @@ -98,8 +98,8 @@ dependencies {
def lifecycleVersion = '2.5.1'
def annotationVersion = '1.2.0'
def gsonVersion = '2.9.1'
def guavaVersion = '31.1-android'
def snakeYamlVersion = '1.32'
def guavaVersion = '32.0.0-android'
def snakeYamlVersion = '2.0'
def jetBrainsAnnotationsVersion = '22.0.0'
def okHttpVersion = '3.12.13'
def playServicesVersion = '17.6.0'
Expand Down
32 changes: 32 additions & 0 deletions split-proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,35 @@
-dontwarn java.beans.IntrospectionException
-dontwarn java.beans.Introspector
-dontwarn java.beans.PropertyDescriptor

##---------------Begin: proguard configuration for Gson ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { <fields>; }

# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
@com.google.gson.annotations.SerializedName <fields>;
}

# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
##---------------End: proguard configuration for Gson ----------
50 changes: 48 additions & 2 deletions src/androidTest/java/helper/IntegrationHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,16 +182,31 @@ public static SplitClientConfig lowRefreshRateConfig() {
}

public static SplitClientConfig lowRefreshRateConfig(boolean streamingEnabled) {
return lowRefreshRateConfig(streamingEnabled, false);
return lowRefreshRateConfig(streamingEnabled, false, true, 60L, 2L);
}

public static SplitClientConfig lowRefreshRateConfig(boolean streamingEnabled, boolean telemetryEnabled) {
return lowRefreshRateConfig(streamingEnabled, telemetryEnabled, true, 60L, 2L);
}

public static SplitClientConfig syncDisabledConfig() {
return lowRefreshRateConfig(true, false, false, 60L, 2L);
}

public static SplitClientConfig customSseConnectionDelayConfig(boolean streamingEnabled, long delay, long disconnectionDelay) {
return lowRefreshRateConfig(streamingEnabled, false, true, delay, disconnectionDelay);
}

public static SplitClientConfig lowRefreshRateConfig(boolean streamingEnabled, boolean telemetryEnabled, boolean syncEnabled, long delay, long sseDisconnectionDelay) {
TestableSplitConfigBuilder builder = new TestableSplitConfigBuilder()
.ready(30000)
.featuresRefreshRate(3)
.segmentsRefreshRate(3)
.impressionsRefreshRate(3)
.impressionsChunkSize(999999)
.syncEnabled(syncEnabled)
.defaultSSEConnectionDelayInSecs(delay)
.sseDisconnectionDelayInSecs(sseDisconnectionDelay)
.streamingEnabled(streamingEnabled)
.shouldRecordTelemetry(telemetryEnabled)
.enableDebug()
Expand All @@ -200,10 +215,14 @@ public static SplitClientConfig lowRefreshRateConfig(boolean streamingEnabled, b
}

public static String streamingEnabledToken() {
return streamingEnabledToken(0);
}

public static String streamingEnabledToken(int delay) {
// This token expires in 2040
return "{" +
" \"pushEnabled\": true," +
" \"connDelay\": 0," +
" \"connDelay\": " + delay + "," +
" \"token\": \"eyJhbGciOiJIUzI1NiIsImtpZCI6IjVZOU05US45QnJtR0EiLCJ0eXAiOiJKV1QifQ.eyJ4LWFibHktY2FwYWJpbGl0eSI6IntcIk16TTVOamMwT0RjeU5nPT1fTVRFeE16Z3dOamd4X01UY3dOVEkyTVRNME1nPT1fbXlTZWdtZW50c1wiOltcInN1YnNjcmliZVwiXSxcIk16TTVOamMwT0RjeU5nPT1fTVRFeE16Z3dOamd4X3NwbGl0c1wiOltcInN1YnNjcmliZVwiXSxcImNvbnRyb2xfcHJpXCI6W1wic3Vic2NyaWJlXCIsXCJjaGFubmVsLW1ldGFkYXRhOnB1Ymxpc2hlcnNcIl0sXCJjb250cm9sX3NlY1wiOltcInN1YnNjcmliZVwiLFwiY2hhbm5lbC1tZXRhZGF0YTpwdWJsaXNoZXJzXCJdfSIsIngtYWJseS1jbGllbnRJZCI6ImNsaWVudElkIiwiZXhwIjoyMjA4OTg4ODAwLCJpYXQiOjE1ODc0MDQzODh9.LcKAXnkr-CiYVxZ7l38w9i98Y-BMAv9JlGP2i92nVQY\"" +
"}";

Expand All @@ -217,6 +236,33 @@ public static String streamingEnabledV1Token() {
return "{\"connDelay\":0,\"pushEnabled\":true,\"token\":\"eyJhbGciOiJIUzI1NiIsImtpZCI6IjVZOU05US5pSGZUUmciLCJ0eXAiOiJKV1QifQ.eyJ4LWFibHktY2FwYWJpbGl0eSI6IntcIk56TTJNREk1TXpjMF9NVGd5TlRnMU1UZ3dOZz09X01Ua3pOamd3TURFNE1BPT1fbXlTZWdtZW50c1wiOltcInN1YnNjcmliZVwiXSxcIk56TTJNREk1TXpjMF9NVGd5TlRnMU1UZ3dOZz09X01qWXhNRE0yTkRjd09RPT1fbXlTZWdtZW50c1wiOltcInN1YnNjcmliZVwiXSxcIk56TTJNREk1TXpjMF9NVGd5TlRnMU1UZ3dOZz09X3NwbGl0c1wiOltcInN1YnNjcmliZVwiXSxcImNvbnRyb2xfcHJpXCI6W1wic3Vic2NyaWJlXCIsXCJjaGFubmVsLW1ldGFkYXRhOnB1Ymxpc2hlcnNcIl0sXCJjb250cm9sX3NlY1wiOltcInN1YnNjcmliZVwiLFwiY2hhbm5lbC1tZXRhZGF0YTpwdWJsaXNoZXJzXCJdfSIsIngtYWJseS1jbGllbnRJZCI6ImNsaWVudElkIiwiZXhwIjoxNjQ4NjU2MjU4LCJpYXQiOjE2NDg2NTI2NTh9.MWwudv3kafKr-gVeqt-ClLAkCngZsDhdWx-dwqM9rxs\"}";
}

public static String splitChangeV2CompressionType2() {
return splitChangeV2("9999999999999",
"1000",
"2",
"eJzMk99u2kwQxV8lOtdryQZj8N6hD5QPlThSTVNVEUKDPYZt1jZar1OlyO9emf8lVFWv2ss5zJyd82O8hTWUZSqZvW04opwhUVdsIKBSSKR+10vS1HWW7pIdz2NyBjRwHS8IXEopTLgbQqDYT+ZUm3LxlV4J4mg81LpMyKqygPRc94YeM6eQTtjphp4fegLVXvD6Qdjt9wPXF6gs2bqCxPC/2eRpDIEXpXXblpGuWCDljGptZ4bJ5lxYSJRZBoFkTcWKozpfsoH0goHfCXpB6PfcngDpVQnZEUjKIlOr2uwWqiC3zU5L1aF+3p7LFhUkPv8/mY2nk3gGgZxssmZzb8p6A9n25ktVtA9iGI3ODXunQ3HDp+AVWT6F+rZWlrWq7MN+YkSWWvuTDvkMSnNV7J6oTdl6qKTEvGnmjcCGjL2IYC/ovPYgUKnvvPtbmrmApiVryLM7p2jE++AfH6fTx09/HvuF32LWnNjStM0Xh3c8ukZcsZlEi3h8/zCObsBpJ0acqYLTmFdtqitK1V6NzrfpdPBbLmVx4uK26e27izpDu/r5yf/16AXun2Cr4u6w591xw7+LfDidLj6Mv8TXwP8xbofv/c7UmtHMmx8BAAD//0fclvU=");
}

public static String splitChangeV2CompressionType1() {
return splitChangeV2("9999999999999",
"1000",
"1",
"H4sIAAAAAAAA/8yT327aTBDFXyU612vJxoTgvUMfKB8qcaSapqoihAZ7DNusvWi9TpUiv3tl/pdQVb1qL+cwc3bOj/EGzlKeq3T6tuaYCoZEXbGFgMogkXXDIM0y31v4C/aCgMnrU9/3gl7Pp4yilMMIAuVusqDamvlXeiWIg/FAa5OSU6aEDHz/ip4wZ5Be1AmjoBsFAtVOCO56UXh31/O7ApUjV1eQGPw3HT+NIPCitG7bctIVC2ScU63d1DK5gksHCZPnEEhXVC45rosFW8ig1++GYej3g85tJEB6aSA7Aqkpc7Ws7XahCnLTbLVM7evnzalsUUHi8//j6WgyTqYQKMilK7b31tRryLa3WKiyfRCDeHhq2Dntiys+JS/J8THUt5VyrFXlHnYTQ3LU2h91yGdQVqhy+0RtTeuhUoNZ08wagTVZdxbBndF5vYVApb7z9m9pZgKaFqwhT+6coRHvg398nEweP/157Bd+S1hz6oxtm88O73B0jbhgM47nyej+YRRfgdNODDlXJWcJL9tUF5SqnRqfbtPr4LdcTHnk4rfp3buLOkG7+Pmp++vRM9w/wVblzX7Pm8OGfxf5YDKZfxh9SS6B/2Pc9t/7ja01o5k1PwIAAP//uTipVskEAAA=");
}

public static String splitChangeV2CompressionType0() {
return splitChangeV2("9999999999999",
"1000",
"0",
"eyJ0cmFmZmljVHlwZU5hbWUiOiJ1c2VyIiwiaWQiOiJkNDMxY2RkMC1iMGJlLTExZWEtOGE4MC0xNjYwYWRhOWNlMzkiLCJuYW1lIjoibWF1cm9famF2YSIsInRyYWZmaWNBbGxvY2F0aW9uIjoxMDAsInRyYWZmaWNBbGxvY2F0aW9uU2VlZCI6LTkyMzkxNDkxLCJzZWVkIjotMTc2OTM3NzYwNCwic3RhdHVzIjoiQUNUSVZFIiwia2lsbGVkIjpmYWxzZSwiZGVmYXVsdFRyZWF0bWVudCI6Im9mZiIsImNoYW5nZU51bWJlciI6MTY4NDMyOTg1NDM4NSwiYWxnbyI6MiwiY29uZmlndXJhdGlvbnMiOnt9LCJjb25kaXRpb25zIjpbeyJjb25kaXRpb25UeXBlIjoiV0hJVEVMSVNUIiwibWF0Y2hlckdyb3VwIjp7ImNvbWJpbmVyIjoiQU5EIiwibWF0Y2hlcnMiOlt7Im1hdGNoZXJUeXBlIjoiV0hJVEVMSVNUIiwibmVnYXRlIjpmYWxzZSwid2hpdGVsaXN0TWF0Y2hlckRhdGEiOnsid2hpdGVsaXN0IjpbImFkbWluIiwibWF1cm8iLCJuaWNvIl19fV19LCJwYXJ0aXRpb25zIjpbeyJ0cmVhdG1lbnQiOiJvZmYiLCJzaXplIjoxMDB9XSwibGFiZWwiOiJ3aGl0ZWxpc3RlZCJ9LHsiY29uZGl0aW9uVHlwZSI6IlJPTExPVVQiLCJtYXRjaGVyR3JvdXAiOnsiY29tYmluZXIiOiJBTkQiLCJtYXRjaGVycyI6W3sia2V5U2VsZWN0b3IiOnsidHJhZmZpY1R5cGUiOiJ1c2VyIn0sIm1hdGNoZXJUeXBlIjoiSU5fU0VHTUVOVCIsIm5lZ2F0ZSI6ZmFsc2UsInVzZXJEZWZpbmVkU2VnbWVudE1hdGNoZXJEYXRhIjp7InNlZ21lbnROYW1lIjoibWF1ci0yIn19XX0sInBhcnRpdGlvbnMiOlt7InRyZWF0bWVudCI6Im9uIiwic2l6ZSI6MH0seyJ0cmVhdG1lbnQiOiJvZmYiLCJzaXplIjoxMDB9LHsidHJlYXRtZW50IjoiVjQiLCJzaXplIjowfSx7InRyZWF0bWVudCI6InY1Iiwic2l6ZSI6MH1dLCJsYWJlbCI6ImluIHNlZ21lbnQgbWF1ci0yIn0seyJjb25kaXRpb25UeXBlIjoiUk9MTE9VVCIsIm1hdGNoZXJHcm91cCI6eyJjb21iaW5lciI6IkFORCIsIm1hdGNoZXJzIjpbeyJrZXlTZWxlY3RvciI6eyJ0cmFmZmljVHlwZSI6InVzZXIifSwibWF0Y2hlclR5cGUiOiJBTExfS0VZUyIsIm5lZ2F0ZSI6ZmFsc2V9XX0sInBhcnRpdGlvbnMiOlt7InRyZWF0bWVudCI6Im9uIiwic2l6ZSI6MH0seyJ0cmVhdG1lbnQiOiJvZmYiLCJzaXplIjoxMDB9LHsidHJlYXRtZW50IjoiVjQiLCJzaXplIjowfSx7InRyZWF0bWVudCI6InY1Iiwic2l6ZSI6MH1dLCJsYWJlbCI6ImRlZmF1bHQgcnVsZSJ9XX0=");
}

private static String splitChangeV2(String changeNumber, String previousChangeNumber, String compressionType, String compressedPayload) {
return "id: vQQ61wzBRO:0:0\n" +
"event: message\n" +
"data: {\"id\":\"m2T85LA4fQ:0:0\",\"clientId\":\"pri:NzIyNjY1MzI4\",\"timestamp\":"+System.currentTimeMillis()+",\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":"+changeNumber+",\\\"pcn\\\":"+previousChangeNumber+",\\\"c\\\":"+compressionType+",\\\"d\\\":\\\""+compressedPayload+"\\\"}\"}\n";
}

/**
* Builds a dispatcher with the given responses.
*
Expand Down
19 changes: 17 additions & 2 deletions src/androidTest/java/helper/TestableSplitConfigBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import io.split.android.client.SyncConfig;
import io.split.android.client.impressions.ImpressionListener;
import io.split.android.client.network.DevelopmentSslConfig;
import io.split.android.client.service.ServiceConstants;
import io.split.android.client.service.impressions.ImpressionsMode;
import io.split.android.client.shared.UserConsent;
import io.split.android.client.utils.logger.Logger;
Expand Down Expand Up @@ -56,6 +57,8 @@ public class TestableSplitConfigBuilder {
private int mMtkRefreshRate = 1800;
private UserConsent mUserConsent = UserConsent.GRANTED;
private boolean mEncryptionEnabled;
private long mDefaultSSEConnectionDelayInSecs = ServiceConstants.DEFAULT_SSE_CONNECTION_DELAY_SECS;
private long mSSEDisconnectionDelayInSecs = 60L;

public TestableSplitConfigBuilder() {
mServiceEndpoints = ServiceEndpoints.builder().build();
Expand Down Expand Up @@ -102,7 +105,7 @@ public TestableSplitConfigBuilder ready(int ready) {
}

public TestableSplitConfigBuilder enableDebug() {
this.mLogLevel = SplitLogLevel.DEBUG;
this.mLogLevel = SplitLogLevel.VERBOSE;
return this;
}

Expand Down Expand Up @@ -236,6 +239,16 @@ public TestableSplitConfigBuilder encryptionEnabled(boolean enabled) {
return this;
}

public TestableSplitConfigBuilder defaultSSEConnectionDelayInSecs(long seconds) {
this.mDefaultSSEConnectionDelayInSecs = seconds;
return this;
}

public TestableSplitConfigBuilder sseDisconnectionDelayInSecs(long seconds) {
this.mSSEDisconnectionDelayInSecs = seconds;
return this;
}

public SplitClientConfig build() {
Constructor constructor = SplitClientConfig.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
Expand Down Expand Up @@ -285,7 +298,9 @@ public SplitClientConfig build() {
mMtkPerPush,
mMtkRefreshRate,
mUserConsent,
mEncryptionEnabled);
mEncryptionEnabled,
mDefaultSSEConnectionDelayInSecs,
mSSEDisconnectionDelayInSecs);
return config;
} catch (Exception e) {
Logger.e("Error creating Testable Split client builder: "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import io.split.android.client.storage.db.MySegmentDao;
import io.split.android.client.storage.db.MySegmentEntity;
import io.split.android.client.storage.db.SplitRoomDatabase;
import io.split.android.client.telemetry.storage.InMemoryTelemetryStorage;
import io.split.android.client.utils.logger.Logger;
import tests.integration.shared.TestingData;
import tests.integration.shared.TestingHelper;
Expand All @@ -61,6 +62,7 @@ public class MySegmentsChangeV2MultiClientTest {
SplitClient mClient;
SynchronizerSpyImpl mSynchronizerSpy;
SplitRoomDatabase mDb;
private InMemoryTelemetryStorage mTelemetryStorage;

@Before
public void setup() {
Expand All @@ -71,6 +73,7 @@ public void setup() {
mMySegmentsSyncLatch2 = new CountDownLatch(1);
mMySegmentsUpdateLatch2 = new CountDownLatch(1);
mDb = DatabaseHelper.getTestDatabase(mContext);
mTelemetryStorage = new InMemoryTelemetryStorage();
}

@Test
Expand Down Expand Up @@ -102,7 +105,8 @@ public void onInvalidated(@NonNull Set<String> tables) {

mFactory = IntegrationHelper.buildFactory(
mApiKey, new Key(userKey),
config, mContext, httpClientMock, mDb, mSynchronizerSpy);
config, mContext, httpClientMock, mDb, mSynchronizerSpy,
null, null, mTelemetryStorage);

mClient = mFactory.client();
SplitClient client2 = mFactory.client(new Key(userKey2));
Expand Down Expand Up @@ -166,8 +170,8 @@ mApiKey, new Key(userKey),
Assert.assertTrue(mySegmentEntity.getSegmentList().contains("new_segment_added"));
Assert.assertFalse(mySegmentEntity.getSegmentList().contains("segment1"));

Assert.assertEquals(4, mTelemetryStorage.popUpdatesFromSSE().getMySegments());
Assert.assertEquals("new_segment_added", mySegmentEntity2.getSegmentList());

}

@After
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import io.split.android.client.network.HttpMethod;
import io.split.android.client.storage.db.MySegmentEntity;
import io.split.android.client.storage.db.SplitRoomDatabase;
import io.split.android.client.telemetry.storage.InMemoryTelemetryStorage;
import io.split.android.client.telemetry.storage.TelemetryStorage;
import io.split.android.client.utils.logger.Logger;
import fake.HttpStreamResponseMock;
import tests.integration.shared.TestingHelper;
Expand All @@ -57,6 +59,7 @@ public class MySegmentsChangeV2Test {
SplitFactory mFactory;
SplitClient mClient;
SynchronizerSpyImpl mSynchronizerSpy;
private TelemetryStorage mTelemetryStorage;

@Before
public void setup() {
Expand All @@ -68,6 +71,7 @@ public void setup() {
mApiKey = apiKeyAndDb.first;
mMySegmentsSyncLatch2 = new CountDownLatch(1);
mMySegmentsUpdateLatch2 = new CountDownLatch(1);
mTelemetryStorage = new InMemoryTelemetryStorage();
}

@Test
Expand All @@ -88,7 +92,7 @@ public void mySegmentsUpdate() throws IOException, InterruptedException {

mFactory = IntegrationHelper.buildFactory(
mApiKey, new Key(userKey),
config, mContext, httpClientMock, db, mSynchronizerSpy);
config, mContext, httpClientMock, db, mSynchronizerSpy, null, null, mTelemetryStorage);

mClient = mFactory.client();

Expand Down Expand Up @@ -125,6 +129,7 @@ mApiKey, new Key(userKey),

Assert.assertTrue(mySegmentEntity.getSegmentList().contains("new_segment_added"));
Assert.assertFalse(mySegmentEntity.getSegmentList().contains("segment1"));
Assert.assertEquals(2, mTelemetryStorage.popUpdatesFromSSE().getMySegments());
mFactory.destroy();
}

Expand Down
Loading

0 comments on commit dee2432

Please sign in to comment.