From e162ce59995fb0b666ec58e3f8c6a023c02a04f6 Mon Sep 17 00:00:00 2001 From: Jae Lim Date: Wed, 24 Oct 2018 16:06:44 -0700 Subject: [PATCH 01/68] Remove DatabaseStorage inner class from StorageHelper class --- .../DatabasePersistenceAndroidTest.java | 31 ++- .../storage/StorageHelperAndroidTest.java | 157 ++++++------ .../persistence/DatabasePersistence.java | 25 +- .../utils/storage/DatabaseManager.java | 22 +- .../utils/storage/StorageHelper.java | 224 ------------------ .../appcenter/AbstractAppCenterTest.java | 12 +- .../persistence/DatabasePersistenceTest.java | 50 ++-- 7 files changed, 152 insertions(+), 369 deletions(-) diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index b689cd7a88..d9ae10b322 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -25,7 +25,6 @@ import com.microsoft.appcenter.utils.crypto.CryptoUtils; import com.microsoft.appcenter.utils.storage.DatabaseManager; import com.microsoft.appcenter.utils.storage.StorageHelper; -import com.microsoft.appcenter.utils.storage.StorageHelper.DatabaseStorage.DatabaseScanner; import org.json.JSONException; import org.junit.Before; @@ -470,9 +469,9 @@ public void deleteLogs() throws PersistenceException { persistence.deleteLogs("", id); /* Access DatabaseStorage directly to verify the deletions. */ - DatabaseScanner scanner1 = persistence.mDatabaseStorage.getScanner(DatabasePersistence.COLUMN_GROUP, "test-p1"); - DatabaseScanner scanner2 = persistence.mDatabaseStorage.getScanner(DatabasePersistence.COLUMN_GROUP, "test-p2"); - DatabaseScanner scanner3 = persistence.mDatabaseStorage.getScanner(DatabasePersistence.COLUMN_GROUP, "test-p3"); + DatabaseManager.Scanner scanner1 = persistence.mDatabaseManager.getScanner(DatabasePersistence.COLUMN_GROUP, "test-p1", null, null, false); + DatabaseManager.Scanner scanner2 = persistence.mDatabaseManager.getScanner(DatabasePersistence.COLUMN_GROUP, "test-p2", null, null, false); + DatabaseManager.Scanner scanner3 = persistence.mDatabaseManager.getScanner(DatabasePersistence.COLUMN_GROUP, "test-p3", null, null, false); //noinspection TryFinallyCanBeTryWithResources try { @@ -493,7 +492,7 @@ public void deleteLogs() throws PersistenceException { persistence.deleteLogs("test-p1", id); /* Access DatabaseStorage directly to verify the deletions. */ - DatabaseScanner scanner4 = persistence.mDatabaseStorage.getScanner(DatabasePersistence.COLUMN_GROUP, "test-p1"); + DatabaseManager.Scanner scanner4 = persistence.mDatabaseManager.getScanner(DatabasePersistence.COLUMN_GROUP, "test-p1", null, null, false); //noinspection TryFinallyCanBeTryWithResources try { @@ -559,7 +558,7 @@ public void deleteLogsForGroup() throws PersistenceException { assertEquals(1, pendingGroups.get("test-p2" + id2).size()); assertEquals(1, pendingGroups.size()); assertEquals(0, outputLogs.size()); - assertEquals(1, persistence.mDatabaseStorage.size()); + assertEquals(1, persistence.mDatabaseManager.getRowCount()); /* Verify one log still persists in the database. */ persistence.clearPendingLogState(); @@ -611,7 +610,7 @@ public void getLogs() throws PersistenceException { assertEquals(10, persistence.countLogs("test")); /* Clear. Nothing to get after. */ - persistence.mDatabaseStorage.clear(); + persistence.mDatabaseManager.clear(); List outputLogs = new ArrayList<>(); assertNull(persistence.getLogs("test", Collections.emptyList(), sizeForGetLogs, outputLogs)); assertTrue(outputLogs.isEmpty()); @@ -754,7 +753,7 @@ public void getLogsException() throws PersistenceException, JSONException { List outputLogs = new ArrayList<>(); persistence.getLogs("test", Collections.emptyList(), 10, outputLogs); assertEquals(numberOfLogs / 2, outputLogs.size()); - assertEquals(2, persistence.mDatabaseStorage.size()); + assertEquals(2, persistence.mDatabaseManager.getRowCount()); } finally { persistence.close(); } @@ -768,7 +767,7 @@ public void upgradeFromVersion1to3() throws PersistenceException, JSONException oldSchema.remove(DatabasePersistence.COLUMN_TARGET_TOKEN); oldSchema.remove(DatabasePersistence.COLUMN_DATA_TYPE); oldSchema.remove(DatabasePersistence.COLUMN_TARGET_KEY); - StorageHelper.DatabaseStorage databaseStorage = StorageHelper.DatabaseStorage.getDatabaseStorage(DatabasePersistence.DATABASE, DatabasePersistence.TABLE, 1, oldSchema, new DatabaseManager.Listener() { + DatabaseManager databaseManager = new DatabaseManager(sContext, DatabasePersistence.DATABASE, DatabasePersistence.TABLE, 1, oldSchema, new DatabaseManager.Listener() { @Override public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @@ -792,9 +791,9 @@ public void onError(String operation, RuntimeException e) { ContentValues contentValues = new ContentValues(); contentValues.put(DatabasePersistence.COLUMN_GROUP, "test"); contentValues.put(DatabasePersistence.COLUMN_LOG, logSerializer.serializeLog(oldLog)); - databaseStorage.put(contentValues); + databaseManager.put(contentValues); } finally { - databaseStorage.close(); + databaseManager.close(); } /* Upgrade. */ @@ -838,7 +837,7 @@ public void onError(String operation, RuntimeException e) { assertEquals(commonSchemaLog, outputLogs.get(0)); /* Verify target token is encrypted. */ - ContentValues values = persistence.mDatabaseStorage.get(DatabasePersistence.COLUMN_GROUP, "test/one"); + ContentValues values = persistence.mDatabaseManager.get(DatabasePersistence.COLUMN_GROUP, "test/one"); String token = values.getAsString(DatabasePersistence.COLUMN_TARGET_TOKEN); assertNotNull(token); assertNotEquals("test-guid", token); @@ -858,7 +857,7 @@ public void upgradeFromVersion2to3() throws PersistenceException, JSONException /* Initialize database persistence with old schema. */ ContentValues oldSchema = new ContentValues(DatabasePersistence.SCHEMA); oldSchema.remove(DatabasePersistence.COLUMN_TARGET_KEY); - StorageHelper.DatabaseStorage databaseStorage = StorageHelper.DatabaseStorage.getDatabaseStorage(DatabasePersistence.DATABASE, DatabasePersistence.TABLE, DatabasePersistence.VERSION_TYPE_API_KEY, oldSchema, new DatabaseManager.Listener() { + DatabaseManager databaseManager = new DatabaseManager(sContext, DatabasePersistence.DATABASE, DatabasePersistence.TABLE, DatabasePersistence.VERSION_TYPE_API_KEY, oldSchema, new DatabaseManager.Listener() { @Override public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @@ -883,9 +882,9 @@ public void onError(String operation, RuntimeException e) { contentValues.put(DatabasePersistence.COLUMN_GROUP, "test"); contentValues.put(DatabasePersistence.COLUMN_LOG, logSerializer.serializeLog(oldLog)); contentValues.put(DatabasePersistence.COLUMN_DATA_TYPE, MOCK_LOG_TYPE); - databaseStorage.put(contentValues); + databaseManager.put(contentValues); } finally { - databaseStorage.close(); + databaseManager.close(); } /* Upgrade. */ @@ -929,7 +928,7 @@ public void onError(String operation, RuntimeException e) { assertEquals(commonSchemaLog, outputLogs.get(0)); /* Verify target token is encrypted. */ - ContentValues values = persistence.mDatabaseStorage.get(DatabasePersistence.COLUMN_GROUP, "test/one"); + ContentValues values = persistence.mDatabaseManager.get(DatabasePersistence.COLUMN_GROUP, "test/one"); String token = values.getAsString(DatabasePersistence.COLUMN_TARGET_TOKEN); assertNotNull(token); assertNotEquals("test-guid", token); diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java index d80ac724fd..dcedcee8ea 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java @@ -33,7 +33,6 @@ import java.util.concurrent.TimeUnit; import static com.microsoft.appcenter.utils.storage.DatabaseManager.ALLOWED_SIZE_MULTIPLE; -import static com.microsoft.appcenter.utils.storage.StorageHelper.DatabaseStorage; import static com.microsoft.appcenter.utils.storage.StorageHelper.InternalStorage; import static com.microsoft.appcenter.utils.storage.StorageHelper.PreferencesStorage; import static org.junit.Assert.assertArrayEquals; @@ -234,94 +233,94 @@ public static void assertContentValuesEquals(ContentValues expected, ContentValu } @SuppressWarnings("SpellCheckingInspection") - private static void runDatabaseStorageTest(DatabaseStorage databaseStorage, boolean imdbTest) { + private static void runDatabaseManagerTest(DatabaseManager databaseManager, boolean imdbTest) { ContentValues value1 = generateContentValues(); ContentValues value2 = generateContentValues(); ContentValues value3 = generateContentValues(); /* Put. */ - Long value1Id = databaseStorage.put(value1); + Long value1Id = databaseManager.put(value1); assertNotNull(value1Id); /* Get for in-memory database test. */ if (imdbTest) { /* Try with invalid key. */ - ContentValues valueFromDatabase = databaseStorage.get("COL_STRINGX", value1.getAsString("COL_STRING")); + ContentValues valueFromDatabase = databaseManager.get("COL_STRINGX", value1.getAsString("COL_STRING")); assertNull(valueFromDatabase); /* Try with valid key. */ - valueFromDatabase = databaseStorage.get("COL_STRING", value1.getAsString("COL_STRING")); + valueFromDatabase = databaseManager.get("COL_STRING", value1.getAsString("COL_STRING")); assertContentValuesEquals(value1, valueFromDatabase); - valueFromDatabase = databaseStorage.get("COL_STRING", value1.getAsString("COL_STRING") + "X"); + valueFromDatabase = databaseManager.get("COL_STRING", value1.getAsString("COL_STRING") + "X"); assertNull(valueFromDatabase); } /* Put another. */ - Long value2Id = databaseStorage.put(value2); + Long value2Id = databaseManager.put(value2); assertNotNull(value2Id); /* Generate an ID that is neither value1Id nor value2Id. */ /* Get. */ - ContentValues value1FromDatabase = databaseStorage.get(value1Id); + ContentValues value1FromDatabase = databaseManager.get(value1Id); assertContentValuesEquals(value1, value1FromDatabase); - ContentValues value2FromDatabase = databaseStorage.get(DatabaseManager.PRIMARY_KEY, value2Id); + ContentValues value2FromDatabase = databaseManager.get(DatabaseManager.PRIMARY_KEY, value2Id); assertContentValuesEquals(value2, value2FromDatabase); //noinspection ResourceType - ContentValues nullValueFromDatabase = databaseStorage.get(-1); + ContentValues nullValueFromDatabase = databaseManager.get(-1); assertNull(nullValueFromDatabase); /* Count with scanner. */ - DatabaseStorage.DatabaseScanner scanner = databaseStorage.getScanner(); + DatabaseManager.Scanner scanner = databaseManager.getScanner(null, null, null, null, false); assertEquals(2, scanner.getCount()); assertEquals(2, scanner.getCount()); - DatabaseStorage.DatabaseScanner scanner1 = databaseStorage.getScanner("COL_STRING", value1.getAsString("COL_STRING")); + DatabaseManager.Scanner scanner1 = databaseManager.getScanner("COL_STRING", value1.getAsString("COL_STRING"), null, null, false); assertEquals(1, scanner1.getCount()); Iterator iterator = scanner1.iterator(); assertContentValuesEquals(value1, iterator.next()); assertFalse(iterator.hasNext()); /* Null value matching. */ - assertEquals(0, databaseStorage.getScanner("COL_STRING", null).getCount()); - assertEquals(2, databaseStorage.getScanner("COL_STRING_NULL", null).getCount()); + assertEquals(0, databaseManager.getScanner("COL_STRING", null, null, null, false).getCount()); + assertEquals(2, databaseManager.getScanner("COL_STRING_NULL", null, null, null, false).getCount()); /* Test null value filter does not exclude anything, so returns the 2 logs. */ - scanner = databaseStorage.getScanner(null, null, "COL_STRING", null, false); + scanner = databaseManager.getScanner(null, null, "COL_STRING", null, false); assertEquals(2, scanner.getCount()); /* Test filtering only with the second key parameter to get only the second log. */ - scanner = databaseStorage.getScanner(null, null, "COL_STRING", Collections.singletonList(value1.getAsString("COL_STRING")), false); + scanner = databaseManager.getScanner(null, null, "COL_STRING", Collections.singletonList(value1.getAsString("COL_STRING")), false); assertEquals(1, scanner.getCount()); assertContentValuesEquals(value2, scanner.iterator().next()); /* Delete. */ - databaseStorage.delete(value1Id); - assertNull(databaseStorage.get(value1Id)); - assertEquals(1, databaseStorage.size()); - assertEquals(1, databaseStorage.getScanner().getCount()); + databaseManager.delete(value1Id); + assertNull(databaseManager.get(value1Id)); + assertEquals(1, databaseManager.getRowCount()); + assertEquals(1, databaseManager.getScanner(null, null, null, null, false).getCount()); /* Put logs to delete multiple IDs. */ ContentValues value4 = generateContentValues(); ContentValues value5 = generateContentValues(); - Long value4Id = databaseStorage.put(value4); - Long value5Id = databaseStorage.put(value5); + Long value4Id = databaseManager.put(value4); + Long value5Id = databaseManager.put(value5); assertNotNull(value4Id); assertNotNull(value5Id); /* Delete multiple logs. */ - databaseStorage.delete(Arrays.asList(value4Id, value5Id)); - assertNull(databaseStorage.get(value4Id)); - assertNull(databaseStorage.get(value5Id)); - assertEquals(1, databaseStorage.size()); + databaseManager.delete(Arrays.asList(value4Id, value5Id)); + assertNull(databaseManager.get(value4Id)); + assertNull(databaseManager.get(value5Id)); + assertEquals(1, databaseManager.getRowCount()); /* Put logs to delete with condition. */ ContentValues value6 = generateContentValues(); ContentValues value7 = generateContentValues(); value6.put("COL_STRING", value2.getAsString("COL_STRING")); value7.put("COL_STRING", value2.getAsString("COL_STRING") + "A"); - Long value6Id = databaseStorage.put(value6); - Long value7Id = databaseStorage.put(value7); + Long value6Id = databaseManager.put(value6); + Long value7Id = databaseManager.put(value7); assertNotNull(value6Id); assertNotNull(value7Id); @@ -329,19 +328,19 @@ private static void runDatabaseStorageTest(DatabaseStorage databaseStorage, bool if (imdbTest) { /* Try with invalid key. */ - databaseStorage.delete("COL_STRINGX", value2.getAsString("COL_STRING")); - assertEquals(3, databaseStorage.size()); + databaseManager.delete("COL_STRINGX", value2.getAsString("COL_STRING")); + assertEquals(3, databaseManager.getRowCount()); } /* Delete logs with condition. */ - databaseStorage.delete("COL_STRING", value2.getAsString("COL_STRING")); - assertEquals(1, databaseStorage.size()); - ContentValues value7FromDatabase = databaseStorage.get(value7Id); + databaseManager.delete("COL_STRING", value2.getAsString("COL_STRING")); + assertEquals(1, databaseManager.getRowCount()); + ContentValues value7FromDatabase = databaseManager.get(value7Id); assertContentValuesEquals(value7, value7FromDatabase); /* Clear. */ - databaseStorage.clear(); - assertEquals(0, databaseStorage.size()); + databaseManager.clear(); + assertEquals(0, databaseManager.getRowCount()); } @Test @@ -519,10 +518,10 @@ public void internalStorageForBytes() throws IOException { @Test public void databaseStorage() { - Log.i(TAG, "Testing Database Storage"); + Log.i(TAG, "Testing Database Manager"); /* Get instance to access database. */ - DatabaseStorage databaseStorage = DatabaseStorage.getDatabaseStorage("test-databaseStorage", "databaseStorage", 1, mSchema, new DatabaseManager.Listener() { + DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseStorage", "databaseStorage", 1, mSchema, new DatabaseManager.Listener() { @Override public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @@ -537,12 +536,12 @@ public void onError(String operation, RuntimeException e) { //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) try { - runDatabaseStorageTest(databaseStorage, false); + runDatabaseManagerTest(databaseManager, false); } finally { /* Close. */ //noinspection ThrowFromFinallyBlock - databaseStorage.close(); + databaseManager.close(); } } @@ -559,7 +558,7 @@ public void databaseStorageUpgradeNotHandled() { oldVersionValue.put("COL_STRING", "Hello World"); /* Get instance to access database. */ - DatabaseStorage databaseStorage = DatabaseStorage.getDatabaseStorage("test-databaseStorageUpgrade", "databaseStorageUpgrade", 1, schema, new DatabaseManager.Listener() { + DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseStorageUpgrade", "databaseStorageUpgrade", 1, schema, new DatabaseManager.Listener() { @Override public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @@ -574,22 +573,22 @@ public void onError(String operation, RuntimeException e) { try { /* Database will always create a column for identifiers so default length of all tables is 1. */ - assertEquals(2, databaseStorage.getColumnNames().length); - long id = databaseStorage.put(oldVersionValue); + assertEquals(2, databaseManager.getColumnNames().length); + long id = databaseManager.put(oldVersionValue); /* Put data. */ - ContentValues actual = databaseStorage.get(id); + ContentValues actual = databaseManager.get(id); actual.remove("oid"); assertEquals(oldVersionValue, actual); - assertEquals(1, databaseStorage.size()); + assertEquals(1, databaseManager.getRowCount()); } finally { /* Close. */ - databaseStorage.close(); + databaseManager.close(); } /* Get instance to access database with a newer schema without handling upgrade. */ - databaseStorage = DatabaseStorage.getDatabaseStorage("test-databaseStorageUpgrade", "databaseStorageUpgrade", 2, mSchema, new DatabaseManager.Listener() { + databaseManager = new DatabaseManager(sContext, "test-databaseStorageUpgrade", "databaseStorageUpgrade", 2, mSchema, new DatabaseManager.Listener() { @Override public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @@ -604,12 +603,12 @@ public void onError(String operation, RuntimeException e) { /* Verify data deleted since no handled upgrade. */ try { - assertEquals(11, databaseStorage.getColumnNames().length); - assertEquals(0, databaseStorage.size()); + assertEquals(11, databaseManager.getColumnNames().length); + assertEquals(0, databaseManager.getRowCount()); } finally { /* Close. */ - databaseStorage.close(); + databaseManager.close(); } } @@ -626,7 +625,7 @@ public void databaseStorageUpgradeHandled() { oldVersionValue.put("COL_STRING", "Hello World"); /* Get instance to access database. */ - DatabaseStorage databaseStorage = DatabaseStorage.getDatabaseStorage("test-databaseStorageUpgrade", "databaseStorageUpgrade", 1, schema, new DatabaseManager.Listener() { + DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseStorageUpgrade", "databaseStorageUpgrade", 1, schema, new DatabaseManager.Listener() { @Override public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @@ -642,22 +641,22 @@ public void onError(String operation, RuntimeException e) { /* Put data. */ long id; try { - id = databaseStorage.put(oldVersionValue); - ContentValues actual = databaseStorage.get(id); + id = databaseManager.put(oldVersionValue); + ContentValues actual = databaseManager.get(id); actual.remove("oid"); assertEquals(oldVersionValue, actual); - assertEquals(1, databaseStorage.size()); + assertEquals(1, databaseManager.getRowCount()); } finally { /* Close. */ - databaseStorage.close(); + databaseManager.close(); } /* Upgrade schema. */ schema.put("COL_INT", 1); /* Get instance to access database with a newer schema without handling upgrade. */ - databaseStorage = DatabaseStorage.getDatabaseStorage("test-databaseStorageUpgrade", "databaseStorageUpgrade", 2, schema, new DatabaseManager.Listener() { + databaseManager = new DatabaseManager(sContext, "test-databaseStorageUpgrade", "databaseStorageUpgrade", 2, schema, new DatabaseManager.Listener() { @Override public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @@ -673,24 +672,24 @@ public void onError(String operation, RuntimeException e) { try { /* Verify data still there. */ - ContentValues actual = databaseStorage.get(id); + ContentValues actual = databaseManager.get(id); actual.remove("oid"); assertEquals(oldVersionValue, actual); - assertEquals(1, databaseStorage.size()); + assertEquals(1, databaseManager.getRowCount()); /* Put new data. */ ContentValues data = new ContentValues(); data.put("COL_STRING", "Hello World"); data.put("COL_INT", 2); - id = databaseStorage.put(data); - actual = databaseStorage.get(id); + id = databaseManager.put(data); + actual = databaseManager.get(id); actual.remove("oid"); assertEquals(data, actual); - assertEquals(2, databaseStorage.size()); + assertEquals(2, databaseManager.getRowCount()); } finally { /* Close. */ - databaseStorage.close(); + databaseManager.close(); } } @@ -699,7 +698,7 @@ public void databaseStorageScannerRemove() { Log.i(TAG, "Testing Database Storage Exceptions"); /* Get instance to access database. */ - DatabaseStorage databaseStorage = DatabaseStorage.getDatabaseStorage("test-databaseStorageScannerRemove", "databaseStorageScannerRemove", 1, mSchema, new DatabaseManager.Listener() { + DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseStorageScannerRemove", "databaseStorageScannerRemove", 1, mSchema, new DatabaseManager.Listener() { @Override public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @@ -714,12 +713,12 @@ public void onError(String operation, RuntimeException e) { //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) try { - databaseStorage.getScanner().iterator().remove(); + databaseManager.getScanner(null, null, null, null, false).iterator().remove(); } finally { /* Close. */ //noinspection ThrowFromFinallyBlock - databaseStorage.close(); + databaseManager.close(); } } @@ -728,7 +727,7 @@ public void databaseStorageScannerNext() { Log.i(TAG, "Testing Database Storage Exceptions"); /* Get instance to access database. */ - DatabaseStorage databaseStorage = DatabaseStorage.getDatabaseStorage("test-databaseStorageScannerNext", "databaseStorageScannerNext", 1, mSchema, new DatabaseManager.Listener() { + DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseStorageScannerNext", "databaseStorageScannerNext", 1, mSchema, new DatabaseManager.Listener() { @Override public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @@ -743,12 +742,12 @@ public void onError(String operation, RuntimeException e) { //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) try { - databaseStorage.getScanner().iterator().next(); + databaseManager.getScanner(null, null, null, null, false).iterator().next(); } finally { /* Close. */ //noinspection ThrowFromFinallyBlock - databaseStorage.close(); + databaseManager.close(); } } @@ -761,7 +760,7 @@ public void databaseStorageInMemoryDB() { Log.i(TAG, "Testing Database Storage switch over to in-memory database"); /* Get instance to access database. */ - DatabaseStorage databaseStorage = DatabaseStorage.getDatabaseStorage("test-databaseStorageInMemoryDB", "test.databaseStorageInMemoryDB", 1, mSchema, new DatabaseManager.Listener() { + DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseStorageInMemoryDB", "test.databaseStorageInMemoryDB", 1, mSchema, new DatabaseManager.Listener() { @Override public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @@ -770,17 +769,19 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @Override public void onError(String operation, RuntimeException e) { + /* Do not handle any errors. This is simulating errors so this is expected. */ } }); //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) try { - runDatabaseStorageTest(databaseStorage, true); + runDatabaseManagerTest(databaseManager, true); } finally { + /* Close. */ //noinspection ThrowFromFinallyBlock - databaseStorage.close(); + databaseManager.close(); } } @@ -789,7 +790,7 @@ public void setMaximumSize() { Log.i(TAG, "Testing Database Storage set maximum size"); /* Get instance to access database. */ - DatabaseStorage databaseStorage = DatabaseStorage.getDatabaseStorage("test-setMaximumSize", "test.setMaximumSize", 1, mSchema, new DatabaseManager.Listener() { + DatabaseManager databaseManager = new DatabaseManager(sContext, "test-setMaximumSize", "test.setMaximumSize", 1, mSchema, new DatabaseManager.Listener() { @Override public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @@ -807,24 +808,24 @@ public void onError(String operation, RuntimeException e) { try { /* Test to change to an exact size as its multiple of 4KB. */ - assertTrue(databaseStorage.setMaxStorageSize(MAX_SIZE_IN_BYTES)); - assertEquals(MAX_SIZE_IN_BYTES, databaseStorage.getMaxSize()); + assertTrue(databaseManager.setMaxSize(MAX_SIZE_IN_BYTES)); + assertEquals(MAX_SIZE_IN_BYTES, databaseManager.getMaxSize()); /* Test inexact value, it will use next multiple of 4KB. */ long desiredSize = MAX_SIZE_IN_BYTES * 2 + 1; - assertTrue(databaseStorage.setMaxStorageSize(desiredSize)); - assertEquals(desiredSize - 1 + ALLOWED_SIZE_MULTIPLE, databaseStorage.getMaxSize()); + assertTrue(databaseManager.setMaxSize(desiredSize)); + assertEquals(desiredSize - 1 + ALLOWED_SIZE_MULTIPLE, databaseManager.getMaxSize()); /* Try to set to a very small value. */ - assertFalse(databaseStorage.setMaxStorageSize(2)); + assertFalse(databaseManager.setMaxSize(2)); /* Test the side effect is that we shrunk to the minimum size that is possible. */ - assertEquals(MAX_SIZE_IN_BYTES, databaseStorage.getMaxSize()); + assertEquals(MAX_SIZE_IN_BYTES, databaseManager.getMaxSize()); } finally { /* Close. */ //noinspection ThrowFromFinallyBlock - databaseStorage.close(); + databaseManager.close(); } } diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index 7205255028..3ff294c283 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -33,7 +33,6 @@ import java.util.TreeMap; import static com.microsoft.appcenter.AppCenter.LOG_TAG; -import static com.microsoft.appcenter.utils.storage.StorageHelper.DatabaseStorage; public class DatabasePersistence extends Persistence { @@ -117,10 +116,10 @@ public class DatabasePersistence extends Persistence { private final Context mContext; /** - * Database storage instance to access Persistence database. + * Database manager instance to access Persistence database. */ @VisibleForTesting - final DatabaseStorage mDatabaseStorage; + final DatabaseManager mDatabaseManager; /** * Pending log groups. Key is a UUID and value is a list of database identifiers. @@ -160,7 +159,7 @@ public DatabasePersistence(Context context) { mContext = context; mPendingDbIdentifiersGroups = new HashMap<>(); mPendingDbIdentifiers = new HashSet<>(); - mDatabaseStorage = DatabaseStorage.getDatabaseStorage(DATABASE, TABLE, version, schema, new DatabaseManager.Listener() { + mDatabaseManager = new DatabaseManager(context, DATABASE, TABLE, version, schema, new DatabaseManager.Listener() { @Override public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @@ -187,7 +186,7 @@ public void onError(String operation, RuntimeException e) { @Override public boolean setMaxStorageSize(long maxStorageSizeInBytes) { - return mDatabaseStorage.setMaxStorageSize(maxStorageSizeInBytes); + return mDatabaseManager.setMaxSize(maxStorageSizeInBytes); } /** @@ -232,7 +231,7 @@ public long putLog(@NonNull String group, @NonNull Log log) throws PersistenceEx targetToken = null; } contentValues = getContentValues(group, isLargePayload ? null : payload, targetToken, log.getType(), targetKey); - long databaseId = mDatabaseStorage.put(contentValues); + long databaseId = mDatabaseManager.put(contentValues); AppCenterLog.debug(LOG_TAG, "Stored a log to the Persistence database for log type " + log.getType() + " with databaseId=" + databaseId); if (isLargePayload) { AppCenterLog.debug(LOG_TAG, "Payload is larger than what SQLite supports, storing payload in a separate file."); @@ -246,7 +245,7 @@ public long putLog(@NonNull String group, @NonNull Log log) throws PersistenceEx } catch (IOException e) { /* Remove database entry if we cannot save payload as a file. */ - mDatabaseStorage.delete(databaseId); + mDatabaseManager.delete(databaseId); throw e; } AppCenterLog.debug(LOG_TAG, "Payload written to " + payloadFile); @@ -274,7 +273,7 @@ File getLargePayloadFile(File directory, long databaseId) { private void deleteLog(File groupLargePayloadDirectory, long id) { //noinspection ResultOfMethodCallIgnored SQLite delete does not have return type either. getLargePayloadFile(groupLargePayloadDirectory, id).delete(); - mDatabaseStorage.delete(id); + mDatabaseManager.delete(id); } @Override @@ -316,7 +315,7 @@ public void deleteLogs(String group) { directory.delete(); /* Delete from database. */ - mDatabaseStorage.delete(COLUMN_GROUP, group); + mDatabaseManager.delete(COLUMN_GROUP, group); /* Delete from pending state. */ for (Iterator iterator = mPendingDbIdentifiersGroups.keySet().iterator(); iterator.hasNext(); ) { @@ -331,7 +330,7 @@ public void deleteLogs(String group) { public int countLogs(@NonNull String group) { /* Query database and get scanner. */ - DatabaseStorage.DatabaseScanner scanner = mDatabaseStorage.getScanner(COLUMN_GROUP, group, null, null, true); + DatabaseManager.Scanner scanner = mDatabaseManager.getScanner(COLUMN_GROUP, group, null, null, true); int count = scanner.getCount(); scanner.close(); return count; @@ -345,7 +344,7 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT AppCenterLog.debug(LOG_TAG, "Trying to get " + limit + " logs from the Persistence database for " + group); /* Query database and get scanner. */ - DatabaseStorage.DatabaseScanner scanner = mDatabaseStorage.getScanner(COLUMN_GROUP, group, COLUMN_TARGET_KEY, pausedTargetKeys, false); + DatabaseManager.Scanner scanner = mDatabaseManager.getScanner(COLUMN_GROUP, group, COLUMN_TARGET_KEY, pausedTargetKeys, false); /* Add logs to output parameter after deserialization if logs are not already sent. */ int count = 0; @@ -364,7 +363,7 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT */ if (dbIdentifier == null) { AppCenterLog.error(LOG_TAG, "Empty database record, probably content was larger than 2MB, need to delete as it's now corrupted."); - DatabaseStorage.DatabaseScanner idScanner = mDatabaseStorage.getScanner(COLUMN_GROUP, group, COLUMN_TARGET_KEY, pausedTargetKeys, true); + DatabaseManager.Scanner idScanner = mDatabaseManager.getScanner(COLUMN_GROUP, group, COLUMN_TARGET_KEY, pausedTargetKeys, true); for (ContentValues idValues : idScanner) { Long invalidId = idValues.getAsLong(DatabaseManager.PRIMARY_KEY); if (!mPendingDbIdentifiers.contains(invalidId) && !candidates.containsKey(invalidId)) { @@ -473,6 +472,6 @@ public void clearPendingLogState() { @Override public void close() { - mDatabaseStorage.close(); + mDatabaseManager.close(); } } \ No newline at end of file diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index da46b89f1a..11abee38ea 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -101,8 +101,8 @@ public class DatabaseManager implements Closeable { * @param schema The schema. * @param listener The error listener. */ - DatabaseManager(Context context, String database, String table, int version, - ContentValues schema, Listener listener) { + public DatabaseManager(Context context, String database, String table, int version, + ContentValues schema, Listener listener) { mContext = context; mDatabase = database; mTable = table; @@ -361,7 +361,7 @@ else if (PRIMARY_KEY.equals(key)) { * This flag is ignored if using in memory database. * @return A scanner to iterate all values. */ - Scanner getScanner(String key1, Object value1, String key2, Collection value2Filter, boolean idOnly) { + public Scanner getScanner(String key1, Object value1, String key2, Collection value2Filter, boolean idOnly) { return new Scanner(key1, value1, key2, value2Filter, idOnly); } @@ -412,7 +412,7 @@ public void close() { * * @return The number of records in the table. */ - final long getRowCount() { + public final long getRowCount() { /* Try SQLite. */ if (mIMDB == null) { @@ -542,13 +542,23 @@ void setSQLiteOpenHelper(@NonNull SQLiteOpenHelper helper) { mSQLiteOpenHelper = helper; } + /** + * Gets an array of column names in the table. + * + * @return An array of column names. + */ + @VisibleForTesting + String[] getColumnNames() { + return getCursor(null, null, null, null, false).getColumnNames(); + } + /** * Set maximum SQLite database size. * * @param maxStorageSizeInBytes Maximum SQLite database size. * @return true if database size was set, otherwise false. */ - boolean setMaxSize(long maxStorageSizeInBytes) { + public boolean setMaxSize(long maxStorageSizeInBytes) { SQLiteDatabase db = getDatabase(); long newMaxSize = db.setMaximumSize(maxStorageSizeInBytes); @@ -608,7 +618,7 @@ public interface Listener { /** * Scanner specification. */ - class Scanner implements Iterable, Closeable { + public class Scanner implements Iterable, Closeable { /** * First filter key. diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/StorageHelper.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/StorageHelper.java index 5efa53b244..fa4784ef14 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/StorageHelper.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/StorageHelper.java @@ -1,13 +1,10 @@ package com.microsoft.appcenter.utils.storage; import android.annotation.SuppressLint; -import android.content.ContentValues; import android.content.Context; import android.content.SharedPreferences; -import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import com.microsoft.appcenter.AppCenter; @@ -15,7 +12,6 @@ import java.io.BufferedReader; import java.io.BufferedWriter; -import java.io.Closeable; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; @@ -27,9 +23,6 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; import java.util.Set; /** @@ -542,221 +535,4 @@ public static void mkdir(@NonNull String path) { new File(path).mkdirs(); } } - - /** - * DatabaseStorage Helper class - */ - public static class DatabaseStorage implements Closeable { - - /** - * DatabaseManager instance. - */ - private final DatabaseManager mDatabaseManager; - - /** - * Private constructor. - * - * @param databaseManager An instance of {@code DatabaseManager}. - */ - private DatabaseStorage(@NonNull DatabaseManager databaseManager) { - mDatabaseManager = databaseManager; - } - - /** - * Get a new instance of {@code DatabaseManager}. - * - * @param database The database name. - * @param table The table name. - * @param version The version. - * @param schema The schema of the database. If the database has more than one table, - * it should contain schemas for all the tables. - * @param listener The database listener. - * @return database storage. - */ - @SuppressWarnings("WeakerAccess") - public static DatabaseStorage getDatabaseStorage(@NonNull String database, - @NonNull String table, - @IntRange(from = 1) int version, - @NonNull ContentValues schema, - @NonNull DatabaseManager.Listener listener) { - return new DatabaseStorage(new DatabaseManager(sContext, database, table, version, schema, listener)); - } - - /** - * Set maximum SQLite database size. - * - * @param maxStorageSizeInBytes Maximum SQLite database size. - * @return true if database size was set, otherwise false. - */ - public boolean setMaxStorageSize(long maxStorageSizeInBytes) { - return mDatabaseManager.setMaxSize(maxStorageSizeInBytes); - } - - /** - * Store an entry in a table. - * - * @param values The entry to be stored. - * @return The identifier of the created database entry. - */ - public long put(@NonNull ContentValues values) { - return mDatabaseManager.put(values); - } - - /** - * Delete an entry in a table. - * - * @param id The identifier for the entry to be deleted. - */ - public void delete(@IntRange(from = 0) long id) { - mDatabaseManager.delete(id); - } - - /** - * Deletes the entries by the identifier from the database. - * - * @param idList The list of database identifiers. - */ - public void delete(@NonNull List idList) { - mDatabaseManager.delete(idList); - } - - /** - * Deletes the entries that matches key == value. - * - * @param key The optional key for query. - * @param value The optional value for query. - */ - public void delete(@Nullable String key, @Nullable Object value) { - mDatabaseManager.delete(key, value); - } - - /** - * Gets the entry by the identifier. - * - * @param id The database identifier. - * @return An entry for the identifier or null if not found. - */ - public ContentValues get(@IntRange(from = 0) long id) { - return mDatabaseManager.get(id); - } - - /** - * Gets the entry that matches key == value. - * - * @param key The optional key for query. - * @param value The optional value for query. - * @return A matching entry. - */ - public ContentValues get(@Nullable String key, @Nullable Object value) { - return mDatabaseManager.get(key, value); - } - - /** - * Gets a scanner to iterate all values. - * - * @return A scanner to iterate all values. - */ - @SuppressWarnings("WeakerAccess") - public DatabaseScanner getScanner() { - return getScanner(null, null); - } - - /** - * Gets a scanner to iterate all values those match key == value. - * - * @param key The optional key for query. - * @param value The optional value for query. - * @return A scanner to iterate all values. - */ - public DatabaseScanner getScanner(@Nullable String key, @Nullable Object value) { - return getScanner(key, value, null, null, false); - } - - /** - * Gets a scanner to iterate all values those match key == value, but records contain - * only identifiers. - * - * @param key The optional key1 for query. - * @param value The optional value1 for query. - * @param key2 The optional key2 for query. - * @param value2Filter The optional values to exclude from query that matches key2. - * @param idOnly True to return only identifiers, false to return all fields. - * This flag is ignored if using in memory database. - * @return A scanner to iterate all values (records contain only identifiers). - */ - public DatabaseScanner getScanner(@Nullable String key, @Nullable Object value, String key2, Collection value2Filter, boolean idOnly) { - return new DatabaseScanner(mDatabaseManager.getScanner(key, value, key2, value2Filter, idOnly)); - } - - /** - * Clears the table in the database. - */ - public void clear() { - mDatabaseManager.clear(); - } - - /** - * Closes database and cleans up in-memory database. - */ - @Override - public void close() { - mDatabaseManager.close(); - } - - /** - * Gets the count of records in the table. - * - * @return The number of records in the table. - */ - public long size() { - return mDatabaseManager.getRowCount(); - } - - /** - * Gets the maximum size of the database. - * - * @return The maximum size of database in bytes. - */ - @SuppressWarnings("unused") - public long getMaxSize() { - return mDatabaseManager.getMaxSize(); - } - - /** - * Gets an array of column names in the table. - * - * @return An array of column names. - */ - @VisibleForTesting - String[] getColumnNames() { - return mDatabaseManager.getCursor(null, null, null, null, false).getColumnNames(); - } - - /** - * Database scanner to iterate over values. - */ - public static class DatabaseScanner implements Iterable, Closeable { - - private final DatabaseManager.Scanner mScanner; - - private DatabaseScanner(DatabaseManager.Scanner scanner) { - mScanner = scanner; - } - - @Override - public void close() { - mScanner.close(); - } - - @NonNull - @Override - public Iterator iterator() { - return mScanner.iterator(); - } - - public int getCount() { - return mScanner.getCount(); - } - } - } } \ No newline at end of file diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java index 33532484a9..a80766eec6 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java @@ -40,7 +40,7 @@ import static com.microsoft.appcenter.AppCenter.TRANSMISSION_TARGET_TOKEN_KEY; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyCollectionOf; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; @@ -61,7 +61,6 @@ StorageHelper.class, StorageHelper.PreferencesStorage.class, IdHelper.class, - StorageHelper.DatabaseStorage.class, DeviceInfoHelper.class, Thread.class, ShutdownHelper.class, @@ -122,7 +121,6 @@ public void setUp() throws Exception { mockStatic(StorageHelper.class); mockStatic(StorageHelper.PreferencesStorage.class); mockStatic(IdHelper.class); - mockStatic(StorageHelper.DatabaseStorage.class); mockStatic(Thread.class); mockStatic(ShutdownHelper.class); mockStatic(DeviceInfoHelper.class); @@ -164,10 +162,10 @@ public Void answer(InvocationOnMock invocation) { StorageHelper.PreferencesStorage.putBoolean(anyString(), anyBoolean()); /* Mock empty database. */ - StorageHelper.DatabaseStorage databaseStorage = mock(StorageHelper.DatabaseStorage.class); - when(StorageHelper.DatabaseStorage.getDatabaseStorage(anyString(), anyString(), anyInt(), any(ContentValues.class), any(DatabaseManager.Listener.class))).thenReturn(databaseStorage); - StorageHelper.DatabaseStorage.DatabaseScanner databaseScanner = mock(StorageHelper.DatabaseStorage.DatabaseScanner.class); - when(databaseStorage.getScanner(anyString(), anyObject())).thenReturn(databaseScanner); + DatabaseManager databaseManager = mock(DatabaseManager.class); + whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); + DatabaseManager.Scanner databaseScanner = mock(DatabaseManager.Scanner.class); + when(databaseManager.getScanner(anyString(), anyObject(), anyString(), anyCollectionOf(String.class), anyBoolean())).thenReturn(databaseScanner); when(databaseScanner.iterator()).thenReturn(mDataBaseScannerIterator); /* Mock network state helper. */ diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java index 2b5fce2b74..f15840c9a8 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java @@ -9,7 +9,6 @@ import com.microsoft.appcenter.ingestion.models.json.LogSerializer; import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.storage.DatabaseManager; -import com.microsoft.appcenter.utils.storage.StorageHelper; import org.json.JSONException; import org.junit.Rule; @@ -30,9 +29,10 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyCollectionOf; -import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; @@ -42,9 +42,10 @@ import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.whenNew; @SuppressWarnings("unused") -@PrepareForTest({AppCenterLog.class, StorageHelper.DatabaseStorage.class}) +@PrepareForTest({AppCenterLog.class, DatabaseManager.class, DatabasePersistence.class}) public class DatabasePersistenceTest { @Rule @@ -76,10 +77,10 @@ public void databaseOperationException() throws Persistence.PersistenceException } @Test - public void clearPendingLogState() throws JSONException { + public void clearPendingLogState() throws Exception { /* groupCount should be <= 9. */ - final int groupCount = 4; + final int groupCount = 1; final int logCount = 10; /* Mock logs. */ @@ -96,14 +97,13 @@ public void clearPendingLogState() throws JSONException { } /* Mock instances. */ - mockStatic(StorageHelper.DatabaseStorage.class); - StorageHelper.DatabaseStorage mockDatabaseStorage = mock(StorageHelper.DatabaseStorage.class); - when(StorageHelper.DatabaseStorage.getDatabaseStorage(anyString(), anyString(), anyInt(), any(ContentValues.class), any(DatabaseManager.Listener.class))).thenReturn(mockDatabaseStorage); + DatabaseManager mockDatabaseManager = mock(DatabaseManager.class); + whenNew(DatabaseManager.class).withAnyArguments().thenReturn(mockDatabaseManager); for (int i = 0; i < groupCount; i++) { - StorageHelper.DatabaseStorage.DatabaseScanner mockDatabaseScanner = mock(StorageHelper.DatabaseStorage.DatabaseScanner.class); + DatabaseManager.Scanner mockDatabaseScanner = mock(DatabaseManager.Scanner.class); when(mockDatabaseScanner.iterator()).thenReturn(list.get(i).iterator()); - when(mockDatabaseStorage.getScanner(COLUMN_GROUP, String.valueOf(i), COLUMN_TARGET_KEY, Collections.emptyList(), false)).thenReturn(mockDatabaseScanner); + when(mockDatabaseManager.getScanner(COLUMN_GROUP, String.valueOf(i), COLUMN_TARGET_KEY, Collections.emptyList(), false)).thenReturn(mockDatabaseScanner); } LogSerializer mockLogSerializer = mock(LogSerializer.class); @@ -129,13 +129,12 @@ public void clearPendingLogState() throws JSONException { } @Test - public void getLogsWithCorruption() throws JSONException { + public void getLogsWithCorruption() throws Exception { /* Mock instances. */ int logCount = 3; - mockStatic(StorageHelper.DatabaseStorage.class); - StorageHelper.DatabaseStorage databaseStorage = mock(StorageHelper.DatabaseStorage.class); - when(StorageHelper.DatabaseStorage.getDatabaseStorage(anyString(), anyString(), anyInt(), any(ContentValues.class), any(DatabaseManager.Listener.class))).thenReturn(databaseStorage); + DatabaseManager databaseManager = mock(DatabaseManager.class); + whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); /* Make 3 logs, the second one will be corrupted. */ Collection fieldValues = new ArrayList<>(logCount); @@ -161,8 +160,8 @@ public void getLogsWithCorruption() throws JSONException { } /* Mock log sequence retrieved from scanner. */ - StorageHelper.DatabaseStorage.DatabaseScanner databaseScanner = mock(StorageHelper.DatabaseStorage.DatabaseScanner.class); - when(databaseStorage.getScanner(anyString(), anyString(), anyString(), anyCollectionOf(String.class), eq(false))).thenReturn(databaseScanner); + DatabaseManager.Scanner databaseScanner = mock(DatabaseManager.Scanner.class); + when(databaseManager.getScanner(anyString(), anyString(), anyString(), anyCollectionOf(String.class), eq(false))).thenReturn(databaseScanner); when(databaseScanner.iterator()).thenReturn(fieldValues.iterator()); /* Mock second scanner with identifiers only. */ @@ -172,8 +171,8 @@ public void getLogsWithCorruption() throws JSONException { when(contentValues.getAsLong(DatabaseManager.PRIMARY_KEY)).thenReturn(i); idValues.add(contentValues); } - StorageHelper.DatabaseStorage.DatabaseScanner idDatabaseScanner = mock(StorageHelper.DatabaseStorage.DatabaseScanner.class); - when(databaseStorage.getScanner(anyString(), anyString(), anyString(), anyCollectionOf(String.class), eq(true))).thenReturn(idDatabaseScanner); + DatabaseManager.Scanner idDatabaseScanner = mock(DatabaseManager.Scanner.class); + when(databaseManager.getScanner(anyString(), anyObject(), anyString(), anyCollectionOf(String.class), eq(true))).thenReturn(idDatabaseScanner); when(idDatabaseScanner.iterator()).thenReturn(idValues.iterator()); /* Mock serializer and eventually the database. */ @@ -200,7 +199,7 @@ public Log answer(InvocationOnMock invocation) { assertEquals("last", outLogs.get(1).getType()); /* Verify we detected and deleted the corrupted log, the second one. */ - verify(databaseStorage).delete(1); + verify(databaseManager).delete(1); /* Verify next call is empty logs as they are pending. */ outLogs = new ArrayList<>(); @@ -260,17 +259,18 @@ public Log answer(InvocationOnMock invocation) { assertEquals("true last", outLogs.get(0).getType()); /* Verify that the only log we deleted in the entire test was the one from previous test (id=1). */ - verify(databaseStorage).delete(anyLong()); + verify(databaseManager).delete(anyLong()); } @Test - public void checkSetStorageSizeForwarding() { + public void checkSetStorageSizeForwarding() throws Exception { /* The real Android test for checking size is in StorageHelperAndroidTest. */ - mockStatic(StorageHelper.DatabaseStorage.class); - StorageHelper.DatabaseStorage databaseStorage = mock(StorageHelper.DatabaseStorage.class); - when(StorageHelper.DatabaseStorage.getDatabaseStorage(anyString(), anyString(), anyInt(), any(ContentValues.class), any(DatabaseManager.Listener.class))).thenReturn(databaseStorage); - when(databaseStorage.setMaxStorageSize(anyLong())).thenReturn(true).thenReturn(false); + DatabaseManager databaseManager = mock(DatabaseManager.class); + whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); + DatabaseManager.Scanner databaseScanner = mock(DatabaseManager.Scanner.class); + when(databaseManager.getScanner(anyString(), anyObject(), anyString(), anyCollectionOf(String.class), anyBoolean())).thenReturn(databaseScanner); + when(databaseManager.setMaxSize(anyLong())).thenReturn(true).thenReturn(false); /* Just checks calls are forwarded to the low level database layer. */ DatabasePersistence persistence = new DatabasePersistence(mock(Context.class)); From fd6e9e4070f0d5da77e4efef0e543844163aace8 Mon Sep 17 00:00:00 2001 From: Jae Lim Date: Thu, 25 Oct 2018 10:09:46 -0700 Subject: [PATCH 02/68] Remove in-memory database --- .../DatabasePersistenceAndroidTest.java | 10 - .../storage/StorageHelperAndroidTest.java | 102 +---- .../persistence/DatabasePersistence.java | 8 +- .../utils/storage/DatabaseManager.java | 400 +++++------------- .../persistence/DatabasePersistenceTest.java | 4 +- .../utils/storage/DatabaseManagerTest.java | 299 ++++++------- 6 files changed, 231 insertions(+), 592 deletions(-) diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index d9ae10b322..003ad5dde8 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -773,11 +773,6 @@ public void upgradeFromVersion1to3() throws PersistenceException, JSONException public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { return false; } - - @Override - public void onError(String operation, RuntimeException e) { - throw e; - } }); /* Init log serializer. */ @@ -863,11 +858,6 @@ public void upgradeFromVersion2to3() throws PersistenceException, JSONException public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { return false; } - - @Override - public void onError(String operation, RuntimeException e) { - throw e; - } }); /* Init log serializer. */ diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java index dcedcee8ea..fa50aa3486 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java @@ -136,7 +136,6 @@ public boolean accept(File dir, String filename) { sContext.deleteDatabase("test-databaseStorageUpgrade"); sContext.deleteDatabase("test-databaseStorageScannerRemove"); sContext.deleteDatabase("test-databaseStorageScannerNext"); - sContext.deleteDatabase("test-databaseStorageInMemoryDB"); sContext.deleteDatabase("test-setMaximumSize"); } @@ -233,7 +232,7 @@ public static void assertContentValuesEquals(ContentValues expected, ContentValu } @SuppressWarnings("SpellCheckingInspection") - private static void runDatabaseManagerTest(DatabaseManager databaseManager, boolean imdbTest) { + private static void runDatabaseManagerTest(DatabaseManager databaseManager) { ContentValues value1 = generateContentValues(); ContentValues value2 = generateContentValues(); ContentValues value3 = generateContentValues(); @@ -242,20 +241,6 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager, bool Long value1Id = databaseManager.put(value1); assertNotNull(value1Id); - /* Get for in-memory database test. */ - if (imdbTest) { - - /* Try with invalid key. */ - ContentValues valueFromDatabase = databaseManager.get("COL_STRINGX", value1.getAsString("COL_STRING")); - assertNull(valueFromDatabase); - - /* Try with valid key. */ - valueFromDatabase = databaseManager.get("COL_STRING", value1.getAsString("COL_STRING")); - assertContentValuesEquals(value1, valueFromDatabase); - valueFromDatabase = databaseManager.get("COL_STRING", value1.getAsString("COL_STRING") + "X"); - assertNull(valueFromDatabase); - } - /* Put another. */ Long value2Id = databaseManager.put(value2); assertNotNull(value2Id); @@ -324,14 +309,6 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager, bool assertNotNull(value6Id); assertNotNull(value7Id); - /* Delete for in-memory database test. */ - if (imdbTest) { - - /* Try with invalid key. */ - databaseManager.delete("COL_STRINGX", value2.getAsString("COL_STRING")); - assertEquals(3, databaseManager.getRowCount()); - } - /* Delete logs with condition. */ databaseManager.delete("COL_STRING", value2.getAsString("COL_STRING")); assertEquals(1, databaseManager.getRowCount()); @@ -527,16 +504,11 @@ public void databaseStorage() { public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { return false; } - - @Override - public void onError(String operation, RuntimeException e) { - throw e; - } }); //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) try { - runDatabaseManagerTest(databaseManager, false); + runDatabaseManagerTest(databaseManager); } finally { /* Close. */ @@ -564,11 +536,6 @@ public void databaseStorageUpgradeNotHandled() { public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { return false; } - - @Override - public void onError(String operation, RuntimeException e) { - throw e; - } }); try { @@ -594,11 +561,6 @@ public void onError(String operation, RuntimeException e) { public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { return false; } - - @Override - public void onError(String operation, RuntimeException e) { - throw e; - } }); /* Verify data deleted since no handled upgrade. */ @@ -631,11 +593,6 @@ public void databaseStorageUpgradeHandled() { public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { return false; } - - @Override - public void onError(String operation, RuntimeException e) { - throw e; - } }); /* Put data. */ @@ -663,11 +620,6 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("ALTER TABLE databaseStorageUpgrade ADD COLUMN COL_INT INTEGER"); return true; } - - @Override - public void onError(String operation, RuntimeException e) { - throw e; - } }); try { @@ -704,11 +656,6 @@ public void databaseStorageScannerRemove() { public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { return false; } - - @Override - public void onError(String operation, RuntimeException e) { - throw e; - } }); //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) @@ -733,11 +680,6 @@ public void databaseStorageScannerNext() { public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { return false; } - - @Override - public void onError(String operation, RuntimeException e) { - throw e; - } }); //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) @@ -751,40 +693,6 @@ public void onError(String operation, RuntimeException e) { } } - /* This is a hack to test database failure by passing a weird table name which is actually valid. - SQLite database allows to create a table that contains period (.) but it doesn't actually create the table and doesn't raise any exceptions. - This test method will then be able to test in-memory database by accessing a table which is not created. - Only tested on emulator so it might not work in the future or on any other devices. */ - @Test - public void databaseStorageInMemoryDB() { - Log.i(TAG, "Testing Database Storage switch over to in-memory database"); - - /* Get instance to access database. */ - DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseStorageInMemoryDB", "test.databaseStorageInMemoryDB", 1, mSchema, new DatabaseManager.Listener() { - - @Override - public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - return false; - } - - @Override - public void onError(String operation, RuntimeException e) { - - /* Do not handle any errors. This is simulating errors so this is expected. */ - } - }); - - //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) - try { - runDatabaseManagerTest(databaseManager, true); - } finally { - - /* Close. */ - //noinspection ThrowFromFinallyBlock - databaseManager.close(); - } - } - @Test public void setMaximumSize() { Log.i(TAG, "Testing Database Storage set maximum size"); @@ -796,12 +704,6 @@ public void setMaximumSize() { public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { return false; } - - @Override - public void onError(String operation, RuntimeException e) { - - /* Do not handle any errors. This is simulating errors so this is expected. */ - } }); //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index 3ff294c283..2c9af3f1b0 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -172,11 +172,6 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("ALTER TABLE " + TABLE + " ADD COLUMN `" + COLUMN_TARGET_KEY + "` TEXT"); return true; } - - @Override - public void onError(String operation, RuntimeException e) { - AppCenterLog.error(LOG_TAG, "Cannot complete an operation (" + operation + ")", e); - } }); mLargePayloadDirectory = new File(Constants.FILES_PATH + PAYLOAD_LARGE_DIRECTORY); @@ -232,6 +227,9 @@ public long putLog(@NonNull String group, @NonNull Log log) throws PersistenceEx } contentValues = getContentValues(group, isLargePayload ? null : payload, targetToken, log.getType(), targetKey); long databaseId = mDatabaseManager.put(contentValues); + if (databaseId == -1) { + AppCenterLog.warn(LOG_TAG, "Failed to store a log to the Persistence database for log type " + log.getType() + "."); + } AppCenterLog.debug(LOG_TAG, "Stored a log to the Persistence database for log type " + log.getType() + " with databaseId=" + databaseId); if (isLargePayload) { AppCenterLog.debug(LOG_TAG, "Payload is larger than what SQLite supports, storing payload in a separate file."); diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index 11abee38ea..21bf90ac11 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -19,9 +19,10 @@ import java.io.Closeable; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -29,7 +30,7 @@ import static com.microsoft.appcenter.utils.AppCenterLog.LOG_TAG; /** - * Database manager for SQLite with fail-over to in-memory. + * Database manager for SQLite. */ public class DatabaseManager implements Closeable { @@ -44,12 +45,6 @@ public class DatabaseManager implements Closeable { @VisibleForTesting static final int ALLOWED_SIZE_MULTIPLE = 4096; - /** - * Maximum number of entries of in memory database. - */ - @VisibleForTesting - static final long IN_MEMORY_MAX_SIZE = 300; - /** * Application context instance. */ @@ -80,17 +75,6 @@ public class DatabaseManager implements Closeable { */ private SQLiteOpenHelper mSQLiteOpenHelper; - /** - * In-memory database if SQLite cannot be used. - */ - @SuppressWarnings("SpellCheckingInspection") - private Map mIMDB; - - /** - * In-memory auto increment. - */ - private long mIMDBAutoInc; - /** * Initializes the table in the database. * @@ -195,39 +179,31 @@ private static ContentValues buildValues(Cursor cursor, ContentValues schema) { */ @SuppressWarnings("TryFinallyCanBeTryWithResources") public long put(@NonNull ContentValues values) { + try { + while (true) { + try { - /* Try SQLite. */ - if (mIMDB == null) { - try { - while (true) { - try { + /* Insert data. */ + return getDatabase().insertOrThrow(mTable, null, values); + } catch (SQLiteFullException e) { - /* Insert data. */ - return getDatabase().insertOrThrow(mTable, null, values); - } catch (SQLiteFullException e) { - - /* Delete the oldest log. */ - Cursor cursor = getCursor(null, null, null, null, true); - try { - if (cursor.moveToNext()) { - delete(cursor.getLong(0)); - } else { - return -1; - } - } finally { - cursor.close(); + /* Delete the oldest log. */ + Cursor cursor = getCursor(null, null, null, null, true); + try { + if (cursor.moveToNext()) { + delete(cursor.getLong(0)); + } else { + return -1; } + } finally { + cursor.close(); } } - } catch (RuntimeException e) { - switchToInMemory("put", e); } + } catch (RuntimeException e) { + AppCenterLog.error(AppCenter.LOG_TAG, String.format("Failed to insert values (%s) to database.", values.toString()), e); } - - /* Store the values to in-memory database. */ - values.put(PRIMARY_KEY, mIMDBAutoInc); - mIMDB.put(mIMDBAutoInc, values); - return mIMDBAutoInc++; + return -1; } /** @@ -248,21 +224,10 @@ public void delete(@NonNull List idList) { if (idList.size() <= 0) { return; } - - /* Try SQLite. */ - if (mIMDB == null) { - try { - getDatabase().execSQL(String.format("DELETE FROM " + mTable + " WHERE " + PRIMARY_KEY + " IN (%s);", TextUtils.join(", ", idList))); - } catch (RuntimeException e) { - switchToInMemory("delete", e); - } - } - - /* Deletes the values from in-memory database. */ - else { - for (Long id : idList) { - mIMDB.remove(id); - } + try { + getDatabase().execSQL(String.format("DELETE FROM " + mTable + " WHERE " + PRIMARY_KEY + " IN (%s);", TextUtils.join(", ", idList))); + } catch (RuntimeException e) { + AppCenterLog.error(AppCenter.LOG_TAG, String.format("Failed to delete IDs (%s) from database.", Arrays.toString(idList.toArray())), e); } } @@ -273,30 +238,10 @@ public void delete(@NonNull List idList) { * @param value The optional value for query. */ public void delete(@Nullable String key, @Nullable Object value) { - - /* Try SQLite. */ - if (mIMDB == null) { - try { - getDatabase().delete(mTable, key + " = ?", new String[]{String.valueOf(value)}); - } catch (RuntimeException e) { - switchToInMemory("delete", e); - } - } - - /* Deletes the values from in-memory database. */ - else if (PRIMARY_KEY.equals(key)) { - if (value == null || !(value instanceof Number)) { - throw new IllegalArgumentException("Primary key should be a number type and cannot be null"); - } - mIMDB.remove(((Number) value).longValue()); - } else { - for (Iterator> iterator = mIMDB.entrySet().iterator(); iterator.hasNext(); ) { - Map.Entry entry = iterator.next(); - Object object = entry.getValue().get(key); - if (object != null && object.equals(value)) { - iterator.remove(); - } - } + try { + getDatabase().delete(mTable, key + " = ?", new String[]{String.valueOf(value)}); + } catch (RuntimeException e) { + AppCenterLog.error(AppCenter.LOG_TAG, String.format("Failed to delete values that match key=\"%s\" and value=\"%s\" from database.", key, value), e); } } @@ -318,34 +263,14 @@ public ContentValues get(@IntRange(from = 0) long id) { * @return A matching entry. */ public ContentValues get(@Nullable String key, @Nullable Object value) { - - /* Try SQLite. */ - if (mIMDB == null) { - try { - Cursor cursor = getCursor(key, value, null, null, false); - ContentValues values = cursor.moveToFirst() ? buildValues(cursor, mSchema) : null; - cursor.close(); - return values; - } catch (RuntimeException e) { - switchToInMemory("get", e); - } - } - - /* Get the values from in-memory database. */ - else if (PRIMARY_KEY.equals(key)) { - if (value == null || !(value instanceof Number)) { - throw new IllegalArgumentException("Primary key should be a number type and cannot be null"); - } - return mIMDB.get(((Number) value).longValue()); - } else { - for (ContentValues values : mIMDB.values()) { - Object object = values.get(key); - if (object != null && object.equals(value)) { - return values; - } - } + try { + Cursor cursor = getCursor(key, value, null, null, false); + ContentValues values = cursor.moveToFirst() ? buildValues(cursor, mSchema) : null; + cursor.close(); + return values; + } catch (RuntimeException e) { + AppCenterLog.error(AppCenter.LOG_TAG, String.format("Failed to get values that match key=\"%s\" and value=\"%s\" from database.", key, value), e); } - return null; } @@ -358,7 +283,6 @@ else if (PRIMARY_KEY.equals(key)) { * @param key2 The optional key2 to filter the query. * @param value2Filter The optional value filter for key2. * @param idOnly true to return only identifier, false to return all fields. - * This flag is ignored if using in memory database. * @return A scanner to iterate all values. */ public Scanner getScanner(String key1, Object value1, String key2, Collection value2Filter, boolean idOnly) { @@ -369,62 +293,37 @@ public Scanner getScanner(String key1, Object value1, String key2, Collection-1 if operation failed. */ public final long getRowCount() { - - /* Try SQLite. */ - if (mIMDB == null) { - try { - return DatabaseUtils.queryNumEntries(getDatabase(), mTable); - } catch (RuntimeException e) { - switchToInMemory("count", e); - } + try { + return DatabaseUtils.queryNumEntries(getDatabase(), mTable); + } catch (RuntimeException e) { + AppCenterLog.error(AppCenter.LOG_TAG, "Failed to get row count of database.", e); } - - /* Get row count of in-memory database. */ - return mIMDB.size(); + return -1; } /** @@ -507,30 +406,6 @@ SQLiteDatabase getDatabase() throws RuntimeException { } } - /** - * Switches to in-memory management, triggers error listener. - * - * @param operation The operation that triggered the error. - * @param exception The exception that triggered the switch. - */ - @VisibleForTesting - void switchToInMemory(String operation, RuntimeException exception) { - - /* Create an in-memory database. */ - mIMDB = new LinkedHashMap() { - - @Override - protected boolean removeEldestEntry(Entry eldest) { - return IN_MEMORY_MAX_SIZE < size(); - } - }; - - /* Trigger error listener. */ - if (mListener != null) { - mListener.onError(operation, exception); - } - } - /** * Sets {@link SQLiteOpenHelper} instance. * @@ -605,14 +480,6 @@ public interface Listener { */ @SuppressWarnings({"BooleanMethodIsAlwaysInverted", "unused"}) boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion); - - /** - * Notifies an exception, before switching to in memory storage. - * - * @param operation A name of operation that caused the error. - * @param e A runtime exception for the error. - */ - void onError(String operation, RuntimeException e); } /** @@ -670,7 +537,7 @@ public void close() { cursor.close(); cursor = null; } catch (RuntimeException e) { - switchToInMemory("scan.close", e); + AppCenterLog.error(AppCenter.LOG_TAG, "Failed to close the scanner.", e); } } } @@ -678,141 +545,76 @@ public void close() { @NonNull @Override public Iterator iterator() { + try { - /* Try SQLite. */ - if (mIMDB == null) { - try { + /* Close cursor first if it was being used. */ + close(); + cursor = getCursor(key1, value1, key2, value2Filter, idOnly); - /* Close cursor first if it was being used. */ - close(); - cursor = getCursor(key1, value1, key2, value2Filter, idOnly); + /* Wrap cursor as iterator. */ + return new Iterator() { - /* Wrap cursor as iterator. */ - return new Iterator() { + /** + * If null, cursor needs to be moved to next. + */ + Boolean hasNext; - /** - * If null, cursor needs to be moved to next. - */ - Boolean hasNext; + @Override + public boolean hasNext() { + if (hasNext == null) { + try { + hasNext = cursor.moveToNext(); + } catch (RuntimeException e) { - @Override - public boolean hasNext() { - if (hasNext == null) { + /* Consider no next on errors. */ + hasNext = false; + + /* Close cursor. */ try { - hasNext = cursor.moveToNext(); - } catch (RuntimeException e) { - - /* Consider no next on errors. */ - hasNext = false; - - /* Close cursor. */ - try { - cursor.close(); - } catch (RuntimeException e1) { - AppCenterLog.warn(AppCenter.LOG_TAG, "Closing cursor failed", e1); - } - cursor = null; - - /* Switch to in-memory database. */ - switchToInMemory("scan.hasNext", e); + cursor.close(); + } catch (RuntimeException e1) { + AppCenterLog.warn(AppCenter.LOG_TAG, "Closing cursor failed", e1); } + cursor = null; } - return hasNext; } + return hasNext; + } - @Override - public ContentValues next() { - - /* Check next. */ - if (!hasNext()) { - throw new NoSuchElementException(); - } - hasNext = null; - - /* Build object. */ - return buildValues(cursor, mSchema); - } + @Override + public ContentValues next() { - @Override - public void remove() { - throw new UnsupportedOperationException(); + /* Check next. */ + if (!hasNext()) { + throw new NoSuchElementException(); } - }; - } catch (RuntimeException e) { - switchToInMemory("scan.iterator", e); - } - } - - /* Scanner for in-memory database. */ - return new Iterator() { - - /** In memory map iterator that we wrap because of the filter logic. */ - final Iterator iterator = mIMDB.values().iterator(); - - /** True if we moved the iterator but not retrieved the value. */ - boolean advanced; + hasNext = null; - /** Next value. */ - ContentValues next; - - @Override - public boolean hasNext() { - - /* Iterator needs to be moved to the next. */ - if (!advanced) { - next = null; - while (iterator.hasNext()) { - ContentValues nextCandidate = iterator.next(); - Object value1 = nextCandidate.get(key1); - Object rawValue2 = nextCandidate.get(key2); - String value2 = null; - if (rawValue2 instanceof String) { - value2 = rawValue2.toString(); - } - if (key1 == null || (Scanner.this.value1 != null && Scanner.this.value1.equals(value1)) || (Scanner.this.value1 == null && value1 == null)) { - if (key2 == null || value2Filter == null || !value2Filter.contains(value2)) { - next = nextCandidate; - break; - } - } - } - advanced = true; + /* Build object. */ + return buildValues(cursor, mSchema); } - return next != null; - } - @Override - public ContentValues next() { - if (!hasNext()) { - throw new NoSuchElementException(); + @Override + public void remove() { + throw new UnsupportedOperationException(); } - advanced = false; - return next; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; + }; + } catch (RuntimeException e) { + AppCenterLog.error(AppCenter.LOG_TAG, "Failed to get iterator of the scanner.", e); + } + return Collections.emptyList().iterator(); } public int getCount() { - if (mIMDB == null) { - try { - if (cursor == null) { - cursor = getCursor(key1, value1, key2, value2Filter, idOnly); - } - return cursor.getCount(); - } catch (RuntimeException e) { - switchToInMemory("scan.count", e); + try { + if (cursor == null) { + cursor = getCursor(key1, value1, key2, value2Filter, idOnly); } + return cursor.getCount(); + } catch (RuntimeException e) { + AppCenterLog.error(AppCenter.LOG_TAG, "Failed to get count of the scanner.", e); } - int count = 0; - for (Iterator iterator = iterator(); iterator.hasNext(); iterator.next()) { - count++; - } - return count; + return -1; } } } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java index f15840c9a8..0903cd8464 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java @@ -38,6 +38,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mockStatic; @@ -72,7 +73,8 @@ public void databaseOperationException() throws Persistence.PersistenceException mockPersistence.close(); } - verifyStatic(); + // There are two error logs on putLog and close + verifyStatic(times(2)); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java index e9b89a1f1b..ada2dc26a3 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java @@ -9,6 +9,9 @@ import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; +import com.microsoft.appcenter.AppCenter; +import com.microsoft.appcenter.utils.AppCenterLog; + import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.internal.stubbing.answers.Returns; @@ -17,14 +20,11 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.NoSuchElementException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doThrow; @@ -34,11 +34,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mockStatic; - +import static org.powermock.api.mockito.PowerMockito.verifyStatic; @SuppressWarnings("unused") @RunWith(PowerMockRunner.class) -@PrepareForTest(SQLiteUtils.class) +@PrepareForTest({SQLiteUtils.class, AppCenterLog.class}) public class DatabaseManagerTest { private static DatabaseManager getDatabaseManagerMock() { @@ -51,181 +51,161 @@ private static DatabaseManager getDatabaseManagerMock() { } @Test - public void switchInMemory() { + public void putFailed() { + mockStatic(AppCenterLog.class); DatabaseManager databaseManagerMock; + databaseManagerMock = getDatabaseManagerMock(); + databaseManagerMock.get(0); + verifyStatic(); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); + } - /* Put. */ + @Test + public void getFailed() { + mockStatic(AppCenterLog.class); + DatabaseManager databaseManagerMock; databaseManagerMock = getDatabaseManagerMock(); databaseManagerMock.put(new ContentValues()); - verify(databaseManagerMock).switchToInMemory(eq("put"), any(RuntimeException.class)); + verifyStatic(); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); + } - /* Get. */ - databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.get(0); - verify(databaseManagerMock).switchToInMemory(eq("get"), any(RuntimeException.class)); - - /* Scanner. */ - { - databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.getScanner(null, null, null, null, false).iterator(); - verify(databaseManagerMock).switchToInMemory(eq("scan.iterator"), any(RuntimeException.class)); - } - { - databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.getScanner(null, null, null, null, false).getCount(); - verify(databaseManagerMock).switchToInMemory(eq("scan.count"), any(RuntimeException.class)); - } - { - /* Cursor next failing but closing working. */ - databaseManagerMock = spy(new DatabaseManager(null, "database", "table", 1, null, null)); - when(databaseManagerMock.getDatabase()).thenReturn(mock(SQLiteDatabase.class)); - mockStatic(SQLiteUtils.class); - Cursor cursor = mock(Cursor.class); - SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class, new Returns(cursor)); - when(SQLiteUtils.newSQLiteQueryBuilder()).thenReturn(sqLiteQueryBuilder); - when(cursor.moveToNext()).thenThrow(new RuntimeException()); - DatabaseManager.Scanner scanner = databaseManagerMock.getScanner(null, null, null, null, false); - assertFalse(scanner.iterator().hasNext()); - verify(databaseManagerMock).switchToInMemory(eq("scan.hasNext"), any(RuntimeException.class)); - - /* We switched over in memory so closing will not switch again. Cursor is closed already. */ - doThrow(new RuntimeException()).when(cursor).close(); - scanner.close(); - verify(databaseManagerMock, never()).switchToInMemory(eq("scan.close"), any(RuntimeException.class)); - } - { - /* Cursor next failing and closing failing. */ - databaseManagerMock = spy(new DatabaseManager(null, "database", "table", 1, null, null)); - when(databaseManagerMock.getDatabase()).thenReturn(mock(SQLiteDatabase.class)); - mockStatic(SQLiteUtils.class); - Cursor cursor = mock(Cursor.class); - SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class, new Returns(cursor)); - when(SQLiteUtils.newSQLiteQueryBuilder()).thenReturn(sqLiteQueryBuilder); - when(cursor.moveToNext()).thenThrow(new RuntimeException()); - doThrow(new RuntimeException()).when(cursor).close(); - DatabaseManager.Scanner scanner = databaseManagerMock.getScanner(null, null, null, null, false); - assertFalse(scanner.iterator().hasNext()); - verify(databaseManagerMock).switchToInMemory(eq("scan.hasNext"), any(RuntimeException.class)); - - /* We switched over in memory so closing will not switch again. Cursor is closed already in hasNext(). */ - scanner.close(); - verify(databaseManagerMock, never()).switchToInMemory(eq("scan.close"), any(RuntimeException.class)); - } - { - /* Cursor closing failing. */ - databaseManagerMock = spy(new DatabaseManager(null, "database", "table", 1, null, null)); - when(databaseManagerMock.getDatabase()).thenReturn(mock(SQLiteDatabase.class)); - mockStatic(SQLiteUtils.class); - Cursor cursor = mock(Cursor.class); - SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class, new Returns(cursor)); - when(SQLiteUtils.newSQLiteQueryBuilder()).thenReturn(sqLiteQueryBuilder); - doThrow(new RuntimeException()).when(cursor).close(); - DatabaseManager.Scanner scanner = databaseManagerMock.getScanner(null, null, null, null, false); - assertFalse(scanner.iterator().hasNext()); - scanner.close(); - verify(databaseManagerMock).switchToInMemory(eq("scan.close"), any(RuntimeException.class)); - } - - /* Delete. */ + @Test + public void deleteFailed() { + mockStatic(AppCenterLog.class); + DatabaseManager databaseManagerMock; databaseManagerMock = getDatabaseManagerMock(); databaseManagerMock.delete(0); - verify(databaseManagerMock).switchToInMemory(eq("delete"), any(RuntimeException.class)); + verifyStatic(); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); + } - /* Delete multiple IDs. */ + @Test + public void deleteMultipleIDsFailed() { + mockStatic(AppCenterLog.class); + DatabaseManager databaseManagerMock; databaseManagerMock = getDatabaseManagerMock(); databaseManagerMock.delete(new ArrayList()); - verify(databaseManagerMock, never()).switchToInMemory(eq("delete"), any(RuntimeException.class)); - databaseManagerMock = getDatabaseManagerMock(); + verifyStatic(never()); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); databaseManagerMock.delete(Arrays.asList(0L, 1L)); - verify(databaseManagerMock).switchToInMemory(eq("delete"), any(RuntimeException.class)); + verifyStatic(); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); + } - /* Clear. */ + @Test + public void clearFailed() { + mockStatic(AppCenterLog.class); + DatabaseManager databaseManagerMock; databaseManagerMock = getDatabaseManagerMock(); databaseManagerMock.clear(); - verify(databaseManagerMock).switchToInMemory(eq("clear"), any(RuntimeException.class)); + verifyStatic(); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); + } - /* Close. */ + @Test + public void closeFailed() { + mockStatic(AppCenterLog.class); + DatabaseManager databaseManagerMock; databaseManagerMock = getDatabaseManagerMock(); databaseManagerMock.close(); - verify(databaseManagerMock).switchToInMemory(eq("close"), any(RuntimeException.class)); - - /* Row count. */ - databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.getRowCount(); - verify(databaseManagerMock).switchToInMemory(eq("count"), any(RuntimeException.class)); - + verifyStatic(); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } - @Test(expected = IllegalArgumentException.class) - public void deleteExceptionWithInvalidValue() { + @Test + public void rowCountFailed() { + mockStatic(AppCenterLog.class); DatabaseManager databaseManagerMock; - - /* Switch over to in-memory database. */ databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.get(0); - - /* Get. */ - databaseManagerMock.delete(DatabaseManager.PRIMARY_KEY, "non-number"); + databaseManagerMock.getRowCount(); + verifyStatic(); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } - @Test(expected = IllegalArgumentException.class) - public void deleteExceptionWithNullValue() { + @Test + public void scannerIteratorFailed() { + mockStatic(AppCenterLog.class); DatabaseManager databaseManagerMock; - - /* Switch over to in-memory database. */ databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.get(0); - - /* Get. */ - databaseManagerMock.delete(DatabaseManager.PRIMARY_KEY, null); + databaseManagerMock.getScanner(null, null, null, null, false).iterator(); + verifyStatic(); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } - @Test(expected = IllegalArgumentException.class) - public void getExceptionWithInvalidValue() { + @Test + public void scannerCountFailed() { + mockStatic(AppCenterLog.class); DatabaseManager databaseManagerMock; - - /* Switch over to in-memory database. */ databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.get(0); - - /* Get. */ - databaseManagerMock.get(DatabaseManager.PRIMARY_KEY, "non-number"); + databaseManagerMock.getScanner(null, null, null, null, false).getCount(); + verifyStatic(); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } - @Test(expected = IllegalArgumentException.class) - public void getExceptionWithNullValue() { + @Test + public void scannerNextFailedButClosingWorks() { + mockStatic(AppCenterLog.class); DatabaseManager databaseManagerMock; - /* Switch over to in-memory database. */ - databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.get(0); - - /* Get. */ - databaseManagerMock.get(DatabaseManager.PRIMARY_KEY, null); + /* Cursor next failing but closing working. */ + databaseManagerMock = spy(new DatabaseManager(null, "database", "table", 1, null, null)); + when(databaseManagerMock.getDatabase()).thenReturn(mock(SQLiteDatabase.class)); + mockStatic(SQLiteUtils.class); + Cursor cursor = mock(Cursor.class); + SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class, new Returns(cursor)); + when(SQLiteUtils.newSQLiteQueryBuilder()).thenReturn(sqLiteQueryBuilder); + when(cursor.moveToNext()).thenThrow(new RuntimeException()); + DatabaseManager.Scanner scanner = databaseManagerMock.getScanner(null, null, null, null, false); + assertFalse(scanner.iterator().hasNext()); + verifyStatic(never()); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); + + /* Verify closing failed. */ + verifyStatic(never()); + AppCenterLog.warn(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } - @Test(expected = UnsupportedOperationException.class) - public void scannerRemoveInMemoryDB() { + @Test + public void scannerNextFailedAndClosingFails() { + mockStatic(AppCenterLog.class); DatabaseManager databaseManagerMock; - /* Switch over to in-memory database. */ - databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.get(0); - - /* Remove. */ - databaseManagerMock.getScanner(null, null, null, null, false).iterator().remove(); + /* Cursor next failing and closing failing. */ + databaseManagerMock = spy(new DatabaseManager(null, "database", "table", 1, null, null)); + when(databaseManagerMock.getDatabase()).thenReturn(mock(SQLiteDatabase.class)); + mockStatic(SQLiteUtils.class); + Cursor cursor = mock(Cursor.class); + SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class, new Returns(cursor)); + when(SQLiteUtils.newSQLiteQueryBuilder()).thenReturn(sqLiteQueryBuilder); + when(cursor.moveToNext()).thenThrow(new RuntimeException()); + doThrow(new RuntimeException()).when(cursor).close(); + DatabaseManager.Scanner scanner = databaseManagerMock.getScanner(null, null, null, null, false); + assertFalse(scanner.iterator().hasNext()); + verifyStatic(never()); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); + + /* Verify closing failed. */ + verifyStatic(); + AppCenterLog.warn(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } - @Test(expected = NoSuchElementException.class) - public void scannerNextInMemoryDB() { + @Test + public void cursorCloseFailed() { + mockStatic(AppCenterLog.class); DatabaseManager databaseManagerMock; - - /* Switch over to in-memory database. */ - databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.get(0); - - /* Next. */ - databaseManagerMock.getScanner(null, null, null, null, false).iterator().next(); + databaseManagerMock = spy(new DatabaseManager(null, "database", "table", 1, null, null)); + when(databaseManagerMock.getDatabase()).thenReturn(mock(SQLiteDatabase.class)); + mockStatic(SQLiteUtils.class); + Cursor cursor = mock(Cursor.class); + SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class, new Returns(cursor)); + when(SQLiteUtils.newSQLiteQueryBuilder()).thenReturn(sqLiteQueryBuilder); + doThrow(new RuntimeException()).when(cursor).close(); + DatabaseManager.Scanner scanner = databaseManagerMock.getScanner(null, null, null, null, false); + assertFalse(scanner.iterator().hasNext()); + scanner.close(); + verifyStatic(); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @Test @@ -264,40 +244,6 @@ public void getDatabaseException() { databaseManager.getDatabase(); } - @Test - public void inMemoryEviction() { - - /* Mocking instances. */ - Context contextMock = mock(Context.class); - - /* Instantiate real instance for DatabaseManager. */ - DatabaseManager databaseManager = spy(new DatabaseManager(contextMock, "database", "table", 1, null, null)); - databaseManager.switchToInMemory("test", null); - - /* Put a first value, the test will eventually evict this one after inserting many more and hitting the limit. */ - ContentValues valueToBeEvicted = mock(ContentValues.class); - long valueToBeEvictedId = databaseManager.put(valueToBeEvicted); - verify(valueToBeEvicted).put(eq(DatabaseManager.PRIMARY_KEY), anyLong()); - assertEquals(1, databaseManager.getRowCount()); - - /* Put enough items to just reach the size limit. */ - for (int i = 1; i < DatabaseManager.IN_MEMORY_MAX_SIZE; i++) { - ContentValues value = mock(ContentValues.class); - long valueId = databaseManager.put(value); - verify(value).put(eq(DatabaseManager.PRIMARY_KEY), anyLong()); - assertEquals(i + 1, databaseManager.getRowCount()); - } - - /* Put 1 more log after limit reached so that it removes the oldest item. */ - ContentValues value = mock(ContentValues.class); - long valueId = databaseManager.put(value); - verify(value).put(eq(DatabaseManager.PRIMARY_KEY), anyLong()); - assertEquals(DatabaseManager.IN_MEMORY_MAX_SIZE, databaseManager.getRowCount()); - - /* Verify the oldest item was evicted. */ - assertNull(databaseManager.get(valueToBeEvictedId)); - } - @Test public void failsToDeleteLogDuringPutWhenFull() { @@ -322,8 +268,7 @@ public void failsToDeleteLogDuringPutWhenFull() { DatabaseManager databaseManager = spy(new DatabaseManager(contextMock, "database", "table", 1, null, null)); databaseManager.setSQLiteOpenHelper(helperMock); - /* When we put a log, it will fail to purge and switch to in memory database. */ - databaseManager.put(mock(ContentValues.class)); - verify(databaseManager).switchToInMemory("put", fatalException); + /* When we put a log, it will fail to purge. */ + assertEquals(-1, databaseManager.put(mock(ContentValues.class))); } } \ No newline at end of file From cd621759126e3d642cf193bc22910c42f06419a2 Mon Sep 17 00:00:00 2001 From: Jae Lim Date: Thu, 25 Oct 2018 10:18:56 -0700 Subject: [PATCH 03/68] Refactor StorageHelperAndroidTest class for database manager --- .../storage/DatabaseManagerAndroidTest.java | 445 ++++++++++++++++++ .../storage/StorageHelperAndroidTest.java | 395 ---------------- 2 files changed, 445 insertions(+), 395 deletions(-) create mode 100644 sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java new file mode 100644 index 0000000000..3d7f44fb9b --- /dev/null +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java @@ -0,0 +1,445 @@ +package com.microsoft.appcenter.utils.storage; + +import android.annotation.SuppressLint; +import android.content.ContentValues; +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.util.Log; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Random; + +import static com.microsoft.appcenter.utils.storage.DatabaseManager.ALLOWED_SIZE_MULTIPLE; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +@SuppressWarnings("unused") +@SmallTest +@RunWith(AndroidJUnit4.class) +public class DatabaseManagerAndroidTest { + + /** + * Log tag. + */ + private static final String TAG = "DatabaseManagerTest"; + + /** + * Random tool. + */ + private static final Random RANDOM = new Random(); + + /** + * Initial maximum database size for some of the tests. + */ + private static final long MAX_SIZE_IN_BYTES = 20480; + + /** + * Context instance. + */ + @SuppressLint("StaticFieldLeak") + private static Context sContext; + + /** + * Database schema. + */ + private static ContentValues mSchema; + + /** + * Boolean value to simulate both true and false. + */ + private static boolean mRandomBooleanValue = false; + + @BeforeClass + public static void setUpClass() { + sContext = InstrumentationRegistry.getTargetContext(); + + /* Create a test schema. */ + mSchema = generateContentValues(); + } + + @AfterClass + public static void tearDownClass() { + + /* Delete database. */ + sContext.deleteDatabase("test-databaseManager"); + sContext.deleteDatabase("test-databaseManagerUpgrade"); + sContext.deleteDatabase("test-databaseManagerScannerRemove"); + sContext.deleteDatabase("test-databaseManagerScannerNext"); + sContext.deleteDatabase("test-setMaximumSize"); + } + + @SuppressWarnings("SpellCheckingInspection") + private static void runDatabaseManagerTest(DatabaseManager databaseManager) { + ContentValues value1 = generateContentValues(); + ContentValues value2 = generateContentValues(); + ContentValues value3 = generateContentValues(); + + /* Put. */ + Long value1Id = databaseManager.put(value1); + assertNotNull(value1Id); + + /* Put another. */ + Long value2Id = databaseManager.put(value2); + assertNotNull(value2Id); + + /* Generate an ID that is neither value1Id nor value2Id. */ + + /* Get. */ + ContentValues value1FromDatabase = databaseManager.get(value1Id); + assertContentValuesEquals(value1, value1FromDatabase); + ContentValues value2FromDatabase = databaseManager.get(DatabaseManager.PRIMARY_KEY, value2Id); + assertContentValuesEquals(value2, value2FromDatabase); + //noinspection ResourceType + ContentValues nullValueFromDatabase = databaseManager.get(-1); + assertNull(nullValueFromDatabase); + + /* Count with scanner. */ + DatabaseManager.Scanner scanner = databaseManager.getScanner(null, null, null, null, false); + assertEquals(2, scanner.getCount()); + assertEquals(2, scanner.getCount()); + DatabaseManager.Scanner scanner1 = databaseManager.getScanner("COL_STRING", value1.getAsString("COL_STRING"), null, null, false); + assertEquals(1, scanner1.getCount()); + Iterator iterator = scanner1.iterator(); + assertContentValuesEquals(value1, iterator.next()); + assertFalse(iterator.hasNext()); + + /* Null value matching. */ + assertEquals(0, databaseManager.getScanner("COL_STRING", null, null, null, false).getCount()); + assertEquals(2, databaseManager.getScanner("COL_STRING_NULL", null, null, null, false).getCount()); + + /* Test null value filter does not exclude anything, so returns the 2 logs. */ + scanner = databaseManager.getScanner(null, null, "COL_STRING", null, false); + assertEquals(2, scanner.getCount()); + + /* Test filtering only with the second key parameter to get only the second log. */ + scanner = databaseManager.getScanner(null, null, "COL_STRING", Collections.singletonList(value1.getAsString("COL_STRING")), false); + assertEquals(1, scanner.getCount()); + assertContentValuesEquals(value2, scanner.iterator().next()); + + /* Delete. */ + databaseManager.delete(value1Id); + assertNull(databaseManager.get(value1Id)); + assertEquals(1, databaseManager.getRowCount()); + assertEquals(1, databaseManager.getScanner(null, null, null, null, false).getCount()); + + /* Put logs to delete multiple IDs. */ + ContentValues value4 = generateContentValues(); + ContentValues value5 = generateContentValues(); + Long value4Id = databaseManager.put(value4); + Long value5Id = databaseManager.put(value5); + assertNotNull(value4Id); + assertNotNull(value5Id); + + /* Delete multiple logs. */ + databaseManager.delete(Arrays.asList(value4Id, value5Id)); + assertNull(databaseManager.get(value4Id)); + assertNull(databaseManager.get(value5Id)); + assertEquals(1, databaseManager.getRowCount()); + + /* Put logs to delete with condition. */ + ContentValues value6 = generateContentValues(); + ContentValues value7 = generateContentValues(); + value6.put("COL_STRING", value2.getAsString("COL_STRING")); + value7.put("COL_STRING", value2.getAsString("COL_STRING") + "A"); + Long value6Id = databaseManager.put(value6); + Long value7Id = databaseManager.put(value7); + assertNotNull(value6Id); + assertNotNull(value7Id); + + /* Delete logs with condition. */ + databaseManager.delete("COL_STRING", value2.getAsString("COL_STRING")); + assertEquals(1, databaseManager.getRowCount()); + ContentValues value7FromDatabase = databaseManager.get(value7Id); + assertContentValuesEquals(value7, value7FromDatabase); + + /* Clear. */ + databaseManager.clear(); + assertEquals(0, databaseManager.getRowCount()); + } + + @Test + public void databaseManager() { + Log.i(TAG, "Testing Database Manager"); + + /* Get instance to access database. */ + DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseManager", "databaseManager", 1, mSchema, new DatabaseManager.Listener() { + + @Override + public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + return false; + } + }); + + //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) + try { + runDatabaseManagerTest(databaseManager); + } finally { + + /* Close. */ + //noinspection ThrowFromFinallyBlock + databaseManager.close(); + } + } + + @Test + public void databaseManagerUpgradeNotHandled() { + Log.i(TAG, "Testing Database Manager Upgrade by recreating table"); + + /* Create a schema for v1. */ + ContentValues schema = new ContentValues(); + schema.put("COL_STRING", ""); + + /* Create a row for v1. */ + ContentValues oldVersionValue = new ContentValues(); + oldVersionValue.put("COL_STRING", "Hello World"); + + /* Get instance to access database. */ + DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseManagerUpgrade", "databaseManagerUpgrade", 1, schema, new DatabaseManager.Listener() { + + @Override + public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + return false; + } + }); + try { + + /* Database will always create a column for identifiers so default length of all tables is 1. */ + assertEquals(2, databaseManager.getColumnNames().length); + long id = databaseManager.put(oldVersionValue); + + /* Put data. */ + ContentValues actual = databaseManager.get(id); + actual.remove("oid"); + assertEquals(oldVersionValue, actual); + assertEquals(1, databaseManager.getRowCount()); + } finally { + + /* Close. */ + databaseManager.close(); + } + + /* Get instance to access database with a newer schema without handling upgrade. */ + databaseManager = new DatabaseManager(sContext, "test-databaseManagerUpgrade", "databaseManagerUpgrade", 2, mSchema, new DatabaseManager.Listener() { + + @Override + public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + return false; + } + }); + + /* Verify data deleted since no handled upgrade. */ + try { + assertEquals(11, databaseManager.getColumnNames().length); + assertEquals(0, databaseManager.getRowCount()); + } finally { + + /* Close. */ + databaseManager.close(); + } + } + + @Test + public void databaseManagerUpgradeHandled() { + Log.i(TAG, "Testing Database Manager Upgrade by updating table"); + + /* Create a schema for v1. */ + ContentValues schema = new ContentValues(); + schema.put("COL_STRING", ""); + + /* Create a row for v1. */ + ContentValues oldVersionValue = new ContentValues(); + oldVersionValue.put("COL_STRING", "Hello World"); + + /* Get instance to access database. */ + DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseManagerUpgrade", "databaseManagerUpgrade", 1, schema, new DatabaseManager.Listener() { + + @Override + public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + return false; + } + }); + + /* Put data. */ + long id; + try { + id = databaseManager.put(oldVersionValue); + ContentValues actual = databaseManager.get(id); + actual.remove("oid"); + assertEquals(oldVersionValue, actual); + assertEquals(1, databaseManager.getRowCount()); + } finally { + + /* Close. */ + databaseManager.close(); + } + + /* Upgrade schema. */ + schema.put("COL_INT", 1); + + /* Get instance to access database with a newer schema without handling upgrade. */ + databaseManager = new DatabaseManager(sContext, "test-databaseManagerUpgrade", "databaseManagerUpgrade", 2, schema, new DatabaseManager.Listener() { + + @Override + public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + db.execSQL("ALTER TABLE databaseManagerUpgrade ADD COLUMN COL_INT INTEGER"); + return true; + } + }); + try { + + /* Verify data still there. */ + ContentValues actual = databaseManager.get(id); + actual.remove("oid"); + assertEquals(oldVersionValue, actual); + assertEquals(1, databaseManager.getRowCount()); + + /* Put new data. */ + ContentValues data = new ContentValues(); + data.put("COL_STRING", "Hello World"); + data.put("COL_INT", 2); + id = databaseManager.put(data); + actual = databaseManager.get(id); + actual.remove("oid"); + assertEquals(data, actual); + assertEquals(2, databaseManager.getRowCount()); + } finally { + + /* Close. */ + databaseManager.close(); + } + } + + @Test(expected = UnsupportedOperationException.class) + public void databaseManagerScannerRemove() { + Log.i(TAG, "Testing Database Manager Exceptions"); + + /* Get instance to access database. */ + DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseManagerScannerRemove", "databaseManagerScannerRemove", 1, mSchema, new DatabaseManager.Listener() { + + @Override + public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + return false; + } + }); + + //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) + try { + databaseManager.getScanner(null, null, null, null, false).iterator().remove(); + } finally { + + /* Close. */ + //noinspection ThrowFromFinallyBlock + databaseManager.close(); + } + } + + @Test(expected = NoSuchElementException.class) + public void databaseManagerScannerNext() { + Log.i(TAG, "Testing Database Manager Exceptions"); + + /* Get instance to access database. */ + DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseManagerScannerNext", "databaseManagerScannerNext", 1, mSchema, new DatabaseManager.Listener() { + + @Override + public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + return false; + } + }); + + //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) + try { + databaseManager.getScanner(null, null, null, null, false).iterator().next(); + } finally { + + /* Close. */ + //noinspection ThrowFromFinallyBlock + databaseManager.close(); + } + } + + + @Test + public void setMaximumSize() { + Log.i(TAG, "Testing Database Manager set maximum size"); + + /* Get instance to access database. */ + DatabaseManager databaseManager = new DatabaseManager(sContext, "test-setMaximumSize", "test.setMaximumSize", 1, mSchema, new DatabaseManager.Listener() { + + @Override + public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + return false; + } + }); + + //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) + try { + + /* Test to change to an exact size as its multiple of 4KB. */ + assertTrue(databaseManager.setMaxSize(MAX_SIZE_IN_BYTES)); + assertEquals(MAX_SIZE_IN_BYTES, databaseManager.getMaxSize()); + + /* Test inexact value, it will use next multiple of 4KB. */ + long desiredSize = MAX_SIZE_IN_BYTES * 2 + 1; + assertTrue(databaseManager.setMaxSize(desiredSize)); + assertEquals(desiredSize - 1 + ALLOWED_SIZE_MULTIPLE, databaseManager.getMaxSize()); + + /* Try to set to a very small value. */ + assertFalse(databaseManager.setMaxSize(2)); + + /* Test the side effect is that we shrunk to the minimum size that is possible. */ + assertEquals(MAX_SIZE_IN_BYTES, databaseManager.getMaxSize()); + } finally { + + /* Close. */ + //noinspection ThrowFromFinallyBlock + databaseManager.close(); + } + } + + private static ContentValues generateContentValues() { + byte[] randomBytes = new byte[10]; + RANDOM.nextBytes(randomBytes); + + ContentValues values = new ContentValues(); + values.put("COL_STRING", new String(randomBytes)); + values.put("COL_STRING_NULL", (String) null); + values.put("COL_BYTE", randomBytes[0]); + values.put("COL_SHORT", (short) RANDOM.nextInt(100)); + values.put("COL_INTEGER", RANDOM.nextInt()); + values.put("COL_LONG", RANDOM.nextLong()); + values.put("COL_FLOAT", RANDOM.nextFloat()); + values.put("COL_DOUBLE", RANDOM.nextDouble()); + values.put("COL_BOOLEAN", (mRandomBooleanValue = !mRandomBooleanValue)/*RANDOM.nextBoolean()*/); + values.put("COL_BYTE_ARRAY", randomBytes); + return values; + } + + private static void assertContentValuesEquals(ContentValues expected, ContentValues actual) { + assertEquals(expected.getAsString("COL_STRING"), actual.getAsString("COL_STRING")); + assertEquals(expected.getAsString("COL_STRING_NULL"), actual.getAsString("COL_STRING_NULL")); + assertEquals(expected.getAsByte("COL_BYTE"), actual.getAsByte("COL_BYTE")); + assertEquals(expected.getAsShort("COL_SHORT"), actual.getAsShort("COL_SHORT")); + assertEquals(expected.getAsInteger("COL_INTEGER"), actual.getAsInteger("COL_INTEGER")); + assertEquals(expected.getAsLong("COL_LONG"), actual.getAsLong("COL_LONG")); + assertEquals(expected.getAsFloat("COL_FLOAT"), actual.getAsFloat("COL_FLOAT")); + assertEquals(expected.getAsDouble("COL_DOUBLE"), actual.getAsDouble("COL_DOUBLE")); + assertEquals(expected.getAsBoolean("COL_BOOLEAN"), actual.getAsBoolean("COL_BOOLEAN")); + assertArrayEquals(expected.getAsByteArray("COL_BYTE_ARRAY"), actual.getAsByteArray("COL_BYTE_ARRAY")); + } + +} \ No newline at end of file diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java index fa50aa3486..4e6613d37d 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java @@ -1,9 +1,7 @@ package com.microsoft.appcenter.utils.storage; import android.annotation.SuppressLint; -import android.content.ContentValues; import android.content.Context; -import android.database.sqlite.SQLiteDatabase; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -23,16 +21,11 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; -import java.util.Iterator; import java.util.List; -import java.util.NoSuchElementException; -import java.util.Random; import java.util.Set; import java.util.concurrent.TimeUnit; -import static com.microsoft.appcenter.utils.storage.DatabaseManager.ALLOWED_SIZE_MULTIPLE; import static com.microsoft.appcenter.utils.storage.StorageHelper.InternalStorage; import static com.microsoft.appcenter.utils.storage.StorageHelper.PreferencesStorage; import static org.junit.Assert.assertArrayEquals; @@ -57,16 +50,6 @@ public class StorageHelperAndroidTest { */ private static final String INTERNAL_STORAGE_TEST_FILE_EXTENSION = ".stacktrace"; - /** - * Random tool. - */ - private static final Random RANDOM = new Random(); - - /** - * Initial maximum database size for some of the tests. - */ - private static final long MAX_SIZE_IN_BYTES = 20480; - /** * Context instance. */ @@ -78,16 +61,6 @@ public class StorageHelperAndroidTest { */ private static String sAndroidFilesPath; - /** - * Database schema. - */ - private static ContentValues mSchema; - - /** - * Boolean value to simulate both true and false. - */ - private static boolean mRandomBooleanValue = false; - @BeforeClass public static void setUpClass() { sContext = InstrumentationRegistry.getTargetContext(); @@ -96,9 +69,6 @@ public static void setUpClass() { /* Create a test directory. */ InternalStorage.mkdir(sAndroidFilesPath); - - /* Create a test schema. */ - mSchema = generateContentValues(); } @AfterClass @@ -130,13 +100,6 @@ public boolean accept(File dir, String filename) { } InternalStorage.delete(sAndroidFilesPath); - - /* Delete database. */ - sContext.deleteDatabase("test-databaseStorage"); - sContext.deleteDatabase("test-databaseStorageUpgrade"); - sContext.deleteDatabase("test-databaseStorageScannerRemove"); - sContext.deleteDatabase("test-databaseStorageScannerNext"); - sContext.deleteDatabase("test-setMaximumSize"); } private static SharedPreferencesTestData[] generateSharedPreferenceData() throws NoSuchMethodException { @@ -200,126 +163,6 @@ private static SharedPreferencesTestData[] generateSharedPreferenceData() throws return testData; } - private static ContentValues generateContentValues() { - byte[] randomBytes = new byte[10]; - RANDOM.nextBytes(randomBytes); - - ContentValues values = new ContentValues(); - values.put("COL_STRING", new String(randomBytes)); - values.put("COL_STRING_NULL", (String) null); - values.put("COL_BYTE", randomBytes[0]); - values.put("COL_SHORT", (short) RANDOM.nextInt(100)); - values.put("COL_INTEGER", RANDOM.nextInt()); - values.put("COL_LONG", RANDOM.nextLong()); - values.put("COL_FLOAT", RANDOM.nextFloat()); - values.put("COL_DOUBLE", RANDOM.nextDouble()); - values.put("COL_BOOLEAN", (mRandomBooleanValue = !mRandomBooleanValue)/*RANDOM.nextBoolean()*/); - values.put("COL_BYTE_ARRAY", randomBytes); - return values; - } - - public static void assertContentValuesEquals(ContentValues expected, ContentValues actual) { - assertEquals(expected.getAsString("COL_STRING"), actual.getAsString("COL_STRING")); - assertEquals(expected.getAsString("COL_STRING_NULL"), actual.getAsString("COL_STRING_NULL")); - assertEquals(expected.getAsByte("COL_BYTE"), actual.getAsByte("COL_BYTE")); - assertEquals(expected.getAsShort("COL_SHORT"), actual.getAsShort("COL_SHORT")); - assertEquals(expected.getAsInteger("COL_INTEGER"), actual.getAsInteger("COL_INTEGER")); - assertEquals(expected.getAsLong("COL_LONG"), actual.getAsLong("COL_LONG")); - assertEquals(expected.getAsFloat("COL_FLOAT"), actual.getAsFloat("COL_FLOAT")); - assertEquals(expected.getAsDouble("COL_DOUBLE"), actual.getAsDouble("COL_DOUBLE")); - assertEquals(expected.getAsBoolean("COL_BOOLEAN"), actual.getAsBoolean("COL_BOOLEAN")); - assertArrayEquals(expected.getAsByteArray("COL_BYTE_ARRAY"), actual.getAsByteArray("COL_BYTE_ARRAY")); - } - - @SuppressWarnings("SpellCheckingInspection") - private static void runDatabaseManagerTest(DatabaseManager databaseManager) { - ContentValues value1 = generateContentValues(); - ContentValues value2 = generateContentValues(); - ContentValues value3 = generateContentValues(); - - /* Put. */ - Long value1Id = databaseManager.put(value1); - assertNotNull(value1Id); - - /* Put another. */ - Long value2Id = databaseManager.put(value2); - assertNotNull(value2Id); - - /* Generate an ID that is neither value1Id nor value2Id. */ - - /* Get. */ - ContentValues value1FromDatabase = databaseManager.get(value1Id); - assertContentValuesEquals(value1, value1FromDatabase); - ContentValues value2FromDatabase = databaseManager.get(DatabaseManager.PRIMARY_KEY, value2Id); - assertContentValuesEquals(value2, value2FromDatabase); - //noinspection ResourceType - ContentValues nullValueFromDatabase = databaseManager.get(-1); - assertNull(nullValueFromDatabase); - - /* Count with scanner. */ - DatabaseManager.Scanner scanner = databaseManager.getScanner(null, null, null, null, false); - assertEquals(2, scanner.getCount()); - assertEquals(2, scanner.getCount()); - DatabaseManager.Scanner scanner1 = databaseManager.getScanner("COL_STRING", value1.getAsString("COL_STRING"), null, null, false); - assertEquals(1, scanner1.getCount()); - Iterator iterator = scanner1.iterator(); - assertContentValuesEquals(value1, iterator.next()); - assertFalse(iterator.hasNext()); - - /* Null value matching. */ - assertEquals(0, databaseManager.getScanner("COL_STRING", null, null, null, false).getCount()); - assertEquals(2, databaseManager.getScanner("COL_STRING_NULL", null, null, null, false).getCount()); - - /* Test null value filter does not exclude anything, so returns the 2 logs. */ - scanner = databaseManager.getScanner(null, null, "COL_STRING", null, false); - assertEquals(2, scanner.getCount()); - - /* Test filtering only with the second key parameter to get only the second log. */ - scanner = databaseManager.getScanner(null, null, "COL_STRING", Collections.singletonList(value1.getAsString("COL_STRING")), false); - assertEquals(1, scanner.getCount()); - assertContentValuesEquals(value2, scanner.iterator().next()); - - /* Delete. */ - databaseManager.delete(value1Id); - assertNull(databaseManager.get(value1Id)); - assertEquals(1, databaseManager.getRowCount()); - assertEquals(1, databaseManager.getScanner(null, null, null, null, false).getCount()); - - /* Put logs to delete multiple IDs. */ - ContentValues value4 = generateContentValues(); - ContentValues value5 = generateContentValues(); - Long value4Id = databaseManager.put(value4); - Long value5Id = databaseManager.put(value5); - assertNotNull(value4Id); - assertNotNull(value5Id); - - /* Delete multiple logs. */ - databaseManager.delete(Arrays.asList(value4Id, value5Id)); - assertNull(databaseManager.get(value4Id)); - assertNull(databaseManager.get(value5Id)); - assertEquals(1, databaseManager.getRowCount()); - - /* Put logs to delete with condition. */ - ContentValues value6 = generateContentValues(); - ContentValues value7 = generateContentValues(); - value6.put("COL_STRING", value2.getAsString("COL_STRING")); - value7.put("COL_STRING", value2.getAsString("COL_STRING") + "A"); - Long value6Id = databaseManager.put(value6); - Long value7Id = databaseManager.put(value7); - assertNotNull(value6Id); - assertNotNull(value7Id); - - /* Delete logs with condition. */ - databaseManager.delete("COL_STRING", value2.getAsString("COL_STRING")); - assertEquals(1, databaseManager.getRowCount()); - ContentValues value7FromDatabase = databaseManager.get(value7Id); - assertContentValuesEquals(value7, value7FromDatabase); - - /* Clear. */ - databaseManager.clear(); - assertEquals(0, databaseManager.getRowCount()); - } - @Test public void sharedPreferences() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { Log.i(TAG, "Testing Shared Preference"); @@ -493,244 +336,6 @@ public void internalStorageForBytes() throws IOException { assertNull(StorageHelper.InternalStorage.readBytes(file)); } - @Test - public void databaseStorage() { - Log.i(TAG, "Testing Database Manager"); - - /* Get instance to access database. */ - DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseStorage", "databaseStorage", 1, mSchema, new DatabaseManager.Listener() { - - @Override - public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - return false; - } - }); - - //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) - try { - runDatabaseManagerTest(databaseManager); - } finally { - - /* Close. */ - //noinspection ThrowFromFinallyBlock - databaseManager.close(); - } - } - - @Test - public void databaseStorageUpgradeNotHandled() { - Log.i(TAG, "Testing Database Storage Upgrade by recreating table"); - - /* Create a schema for v1. */ - ContentValues schema = new ContentValues(); - schema.put("COL_STRING", ""); - - /* Create a row for v1. */ - ContentValues oldVersionValue = new ContentValues(); - oldVersionValue.put("COL_STRING", "Hello World"); - - /* Get instance to access database. */ - DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseStorageUpgrade", "databaseStorageUpgrade", 1, schema, new DatabaseManager.Listener() { - - @Override - public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - return false; - } - }); - try { - - /* Database will always create a column for identifiers so default length of all tables is 1. */ - assertEquals(2, databaseManager.getColumnNames().length); - long id = databaseManager.put(oldVersionValue); - - /* Put data. */ - ContentValues actual = databaseManager.get(id); - actual.remove("oid"); - assertEquals(oldVersionValue, actual); - assertEquals(1, databaseManager.getRowCount()); - } finally { - - /* Close. */ - databaseManager.close(); - } - - /* Get instance to access database with a newer schema without handling upgrade. */ - databaseManager = new DatabaseManager(sContext, "test-databaseStorageUpgrade", "databaseStorageUpgrade", 2, mSchema, new DatabaseManager.Listener() { - - @Override - public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - return false; - } - }); - - /* Verify data deleted since no handled upgrade. */ - try { - assertEquals(11, databaseManager.getColumnNames().length); - assertEquals(0, databaseManager.getRowCount()); - } finally { - - /* Close. */ - databaseManager.close(); - } - } - - @Test - public void databaseStorageUpgradeHandled() { - Log.i(TAG, "Testing Database Storage Upgrade by updating table"); - - /* Create a schema for v1. */ - ContentValues schema = new ContentValues(); - schema.put("COL_STRING", ""); - - /* Create a row for v1. */ - ContentValues oldVersionValue = new ContentValues(); - oldVersionValue.put("COL_STRING", "Hello World"); - - /* Get instance to access database. */ - DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseStorageUpgrade", "databaseStorageUpgrade", 1, schema, new DatabaseManager.Listener() { - - @Override - public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - return false; - } - }); - - /* Put data. */ - long id; - try { - id = databaseManager.put(oldVersionValue); - ContentValues actual = databaseManager.get(id); - actual.remove("oid"); - assertEquals(oldVersionValue, actual); - assertEquals(1, databaseManager.getRowCount()); - } finally { - - /* Close. */ - databaseManager.close(); - } - - /* Upgrade schema. */ - schema.put("COL_INT", 1); - - /* Get instance to access database with a newer schema without handling upgrade. */ - databaseManager = new DatabaseManager(sContext, "test-databaseStorageUpgrade", "databaseStorageUpgrade", 2, schema, new DatabaseManager.Listener() { - - @Override - public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - db.execSQL("ALTER TABLE databaseStorageUpgrade ADD COLUMN COL_INT INTEGER"); - return true; - } - }); - try { - - /* Verify data still there. */ - ContentValues actual = databaseManager.get(id); - actual.remove("oid"); - assertEquals(oldVersionValue, actual); - assertEquals(1, databaseManager.getRowCount()); - - /* Put new data. */ - ContentValues data = new ContentValues(); - data.put("COL_STRING", "Hello World"); - data.put("COL_INT", 2); - id = databaseManager.put(data); - actual = databaseManager.get(id); - actual.remove("oid"); - assertEquals(data, actual); - assertEquals(2, databaseManager.getRowCount()); - } finally { - - /* Close. */ - databaseManager.close(); - } - } - - @Test(expected = UnsupportedOperationException.class) - public void databaseStorageScannerRemove() { - Log.i(TAG, "Testing Database Storage Exceptions"); - - /* Get instance to access database. */ - DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseStorageScannerRemove", "databaseStorageScannerRemove", 1, mSchema, new DatabaseManager.Listener() { - - @Override - public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - return false; - } - }); - - //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) - try { - databaseManager.getScanner(null, null, null, null, false).iterator().remove(); - } finally { - - /* Close. */ - //noinspection ThrowFromFinallyBlock - databaseManager.close(); - } - } - - @Test(expected = NoSuchElementException.class) - public void databaseStorageScannerNext() { - Log.i(TAG, "Testing Database Storage Exceptions"); - - /* Get instance to access database. */ - DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseStorageScannerNext", "databaseStorageScannerNext", 1, mSchema, new DatabaseManager.Listener() { - - @Override - public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - return false; - } - }); - - //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) - try { - databaseManager.getScanner(null, null, null, null, false).iterator().next(); - } finally { - - /* Close. */ - //noinspection ThrowFromFinallyBlock - databaseManager.close(); - } - } - - @Test - public void setMaximumSize() { - Log.i(TAG, "Testing Database Storage set maximum size"); - - /* Get instance to access database. */ - DatabaseManager databaseManager = new DatabaseManager(sContext, "test-setMaximumSize", "test.setMaximumSize", 1, mSchema, new DatabaseManager.Listener() { - - @Override - public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - return false; - } - }); - - //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) - try { - - /* Test to change to an exact size as its multiple of 4KB. */ - assertTrue(databaseManager.setMaxSize(MAX_SIZE_IN_BYTES)); - assertEquals(MAX_SIZE_IN_BYTES, databaseManager.getMaxSize()); - - /* Test inexact value, it will use next multiple of 4KB. */ - long desiredSize = MAX_SIZE_IN_BYTES * 2 + 1; - assertTrue(databaseManager.setMaxSize(desiredSize)); - assertEquals(desiredSize - 1 + ALLOWED_SIZE_MULTIPLE, databaseManager.getMaxSize()); - - /* Try to set to a very small value. */ - assertFalse(databaseManager.setMaxSize(2)); - - /* Test the side effect is that we shrunk to the minimum size that is possible. */ - assertEquals(MAX_SIZE_IN_BYTES, databaseManager.getMaxSize()); - } finally { - - /* Close. */ - //noinspection ThrowFromFinallyBlock - databaseManager.close(); - } - } - /** * Temporary class for testing shared preferences. */ From 1739e9f2e752ddccdcc50c8e0141fde4c58035f6 Mon Sep 17 00:00:00 2001 From: Jae Lim Date: Thu, 25 Oct 2018 12:37:25 -0700 Subject: [PATCH 04/68] Refactor PreferencesStorage helper class --- .../sasquatch/activities/CrashesTest.java | 6 +- .../activities/SettingsActivityTest.java | 6 +- .../activities/SettingsActivity.java | 6 +- .../AnalyticsTransmissionTarget.java | 6 +- .../analytics/AbstractAnalyticsTest.java | 14 +- .../appcenter/analytics/AnalyticsTest.java | 7 +- .../analytics/channel/SessionTrackerTest.java | 38 +- .../appcenter/crashes/CrashesAndroidTest.java | 4 +- ...WrapperSdkExceptionManagerAndroidTest.java | 5 +- .../microsoft/appcenter/crashes/Crashes.java | 5 +- .../appcenter/crashes/CrashesTest.java | 27 +- .../crashes/UncaughtExceptionHandlerTest.java | 13 +- .../WrapperSdkExceptionManagerTest.java | 9 +- .../distribute/CheckDownloadTask.java | 8 +- .../appcenter/distribute/Distribute.java | 125 ++++--- .../appcenter/distribute/DistributeUtils.java | 14 +- .../AbstractDistributeAfterDownloadTest.java | 42 +-- .../distribute/AbstractDistributeTest.java | 16 +- .../DistributeBeforeApiSuccessTest.java | 332 +++++++++--------- .../DistributeBeforeDownloadTest.java | 126 +++---- .../DistributeCustomizationTest.java | 18 +- .../distribute/DistributeDownloadTest.java | 94 ++--- .../DistributeMandatoryDownloadTest.java | 36 +- .../DistributePlusDownloadReceiverTest.java | 4 +- .../appcenter/distribute/DistributeTest.java | 6 +- .../DistributeWarnUnknownSourcesTest.java | 24 +- .../microsoft/appcenter/push/PushTest.java | 14 +- .../appcenter/AppCenterAndroidTest.java | 8 +- .../DatabasePersistenceAndroidTest.java | 2 + .../persistence/PersistenceAndroidTest.java | 4 +- .../appcenter/utils/IdHelperAndroidTest.java | 8 +- .../storage/StorageHelperAndroidTest.java | 52 +-- .../appcenter/AbstractAppCenterService.java | 6 +- .../com/microsoft/appcenter/AppCenter.java | 8 +- .../microsoft/appcenter/SessionContext.java | 8 +- .../microsoft/appcenter/utils/IdHelper.java | 6 +- .../storage/SharedPreferencesManager.java | 275 +++++++++++++++ .../utils/storage/StorageHelper.java | 240 ------------- .../AbstractAppCenterServiceTest.java | 28 +- .../appcenter/AbstractAppCenterTest.java | 13 +- .../microsoft/appcenter/AppCenterTest.java | 12 +- .../appcenter/InstantiationTest.java | 3 +- 42 files changed, 863 insertions(+), 815 deletions(-) create mode 100644 sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManager.java diff --git a/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/CrashesTest.java b/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/CrashesTest.java index cb309ca2dd..05b0645082 100644 --- a/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/CrashesTest.java +++ b/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/CrashesTest.java @@ -20,7 +20,7 @@ import com.microsoft.appcenter.crashes.utils.ErrorLogHelper; import com.microsoft.appcenter.sasquatch.R; import com.microsoft.appcenter.sasquatch.listeners.SasquatchCrashesListener; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.hamcrest.Description; import org.hamcrest.Matcher; @@ -87,8 +87,8 @@ public void setUp() { mContext = getInstrumentation().getTargetContext(); /* Clear preferences. */ - StorageHelper.initialize(mContext); - StorageHelper.PreferencesStorage.clear(); + SharedPreferencesManager.initialize(mContext); + SharedPreferencesManager.clear(); /* Clear crashes. */ Constants.loadFromContext(mContext); diff --git a/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivityTest.java b/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivityTest.java index 1032ddc9d6..1926416f35 100644 --- a/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivityTest.java +++ b/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivityTest.java @@ -11,7 +11,7 @@ import com.microsoft.appcenter.distribute.Distribute; import com.microsoft.appcenter.push.Push; import com.microsoft.appcenter.sasquatch.R; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.Assert; import org.junit.Before; @@ -41,8 +41,8 @@ public void setUp() { mContext = getInstrumentation().getTargetContext(); /* Clear preferences. */ - StorageHelper.initialize(mContext); - StorageHelper.PreferencesStorage.clear(); + SharedPreferencesManager.initialize(mContext); + SharedPreferencesManager.clear(); /* Launch main activity and go to setting page. Required to properly initialize. */ mActivityTestRule.launchActivity(new Intent()); diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivity.java index a45d9b5f59..1c4456200d 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivity.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivity.java @@ -35,7 +35,7 @@ import com.microsoft.appcenter.sasquatch.eventfilter.EventFilter; import com.microsoft.appcenter.utils.PrefStorageConstants; import com.microsoft.appcenter.utils.async.AppCenterFuture; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import java.io.File; import java.lang.reflect.Method; @@ -367,7 +367,7 @@ public boolean onPreferenceClick(final Preference preference) { public void onClick(DialogInterface dialog, int which) { if (input.getText().toString().matches(UUID_FORMAT_REGEX)) { UUID uuid = UUID.fromString(input.getText().toString()); - StorageHelper.PreferencesStorage.putString(PrefStorageConstants.KEY_INSTALL_ID, uuid.toString()); + SharedPreferencesManager.putString(PrefStorageConstants.KEY_INSTALL_ID, uuid.toString()); Toast.makeText(getActivity(), String.format(getActivity().getString(R.string.install_id_changed_format), uuid.toString()), Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getActivity(), R.string.install_id_invalid, Toast.LENGTH_SHORT).show(); @@ -492,7 +492,7 @@ public void onClick(DialogInterface dialog, int which) { @Override public boolean onPreferenceClick(Preference preference) { - StorageHelper.PreferencesStorage.remove(Crashes.PREF_KEY_ALWAYS_SEND); + SharedPreferencesManager.remove(Crashes.PREF_KEY_ALWAYS_SEND); Toast.makeText(getActivity(), R.string.clear_crash_user_confirmation_toast, Toast.LENGTH_SHORT).show(); return true; } diff --git a/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTarget.java b/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTarget.java index 4c8f0fd5f4..f6914f398c 100644 --- a/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTarget.java +++ b/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTarget.java @@ -14,7 +14,7 @@ import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.async.AppCenterFuture; import com.microsoft.appcenter.utils.async.DefaultAppCenterFuture; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import java.util.Collections; import java.util.HashMap; @@ -293,7 +293,7 @@ public void run() { while (descendantIterator.hasNext()) { AnalyticsTransmissionTarget descendantTarget = descendantIterator.next(); descendantIterator.remove(); - StorageHelper.PreferencesStorage.putBoolean(descendantTarget.getEnabledPreferenceKey(), enabled); + SharedPreferencesManager.putBoolean(descendantTarget.getEnabledPreferenceKey(), enabled); for (AnalyticsTransmissionTarget childTarget : descendantTarget.mChildrenTargets.values()) { descendantIterator.add(childTarget); } @@ -387,7 +387,7 @@ private String getEnabledPreferenceKey() { @WorkerThread private boolean isEnabledInStorage() { - return StorageHelper.PreferencesStorage.getBoolean(getEnabledPreferenceKey(), true); + return SharedPreferencesManager.getBoolean(getEnabledPreferenceKey(), true); } @WorkerThread diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AbstractAnalyticsTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AbstractAnalyticsTest.java index 99902973ca..48e5f97c6b 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AbstractAnalyticsTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AbstractAnalyticsTest.java @@ -8,7 +8,7 @@ import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.PrefStorageConstants; import com.microsoft.appcenter.utils.async.AppCenterFuture; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.Before; import org.junit.runner.RunWith; @@ -28,7 +28,7 @@ import static org.powermock.api.mockito.PowerMockito.mockStatic; @RunWith(PowerMockRunner.class) -@PrepareForTest({SystemClock.class, StorageHelper.PreferencesStorage.class, AppCenterLog.class, AppCenter.class, HandlerUtils.class}) +@PrepareForTest({SystemClock.class, SharedPreferencesManager.class, AppCenterLog.class, AppCenter.class, HandlerUtils.class}) abstract class AbstractAnalyticsTest { static final String ANALYTICS_ENABLED_KEY = PrefStorageConstants.KEY_ENABLED + "_" + Analytics.getInstance().getServiceName(); @@ -62,8 +62,8 @@ public Void answer(InvocationOnMock invocation) { HandlerUtils.runOnUiThread(any(Runnable.class)); /* First call to com.microsoft.appcenter.AppCenter.isEnabled shall return true, initial state. */ - mockStatic(StorageHelper.PreferencesStorage.class); - when(StorageHelper.PreferencesStorage.getBoolean(anyString(), eq(true))).thenReturn(true); + mockStatic(SharedPreferencesManager.class); + when(SharedPreferencesManager.getBoolean(anyString(), eq(true))).thenReturn(true); /* Then simulate further changes to state. */ doAnswer(new Answer() { @@ -74,11 +74,11 @@ public Object answer(InvocationOnMock invocation) { /* Whenever the new state is persisted, make further calls return the new state. */ String key = (String) invocation.getArguments()[0]; boolean enabled = (Boolean) invocation.getArguments()[1]; - when(StorageHelper.PreferencesStorage.getBoolean(eq(key), eq(true))).thenReturn(enabled); + when(SharedPreferencesManager.getBoolean(eq(key), eq(true))).thenReturn(enabled); return null; } - }).when(StorageHelper.PreferencesStorage.class); - StorageHelper.PreferencesStorage.putBoolean(anyString(), anyBoolean()); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putBoolean(anyString(), anyBoolean()); /* Pretend automatic page tracking is enabled by default, this will be the case if service becomes public. */ // TODO remove that after that feature becomes public and thus a default. diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java index 35f19d0cea..f1b08f430c 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java @@ -26,6 +26,7 @@ import com.microsoft.appcenter.ingestion.models.properties.TypedProperty; import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.async.AppCenterConsumer; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import com.microsoft.appcenter.utils.storage.StorageHelper; import org.junit.Assert; @@ -223,7 +224,7 @@ public void trackEventFromAppWithNullMapProperty() { analytics.onStarted(mock(Context.class), channel, "", null, true); /* Send event with empty Map properties. */ - Analytics.trackEvent("eventName", (Map)null); + Analytics.trackEvent("eventName", (Map) null); verify(channel).enqueue(argumentCaptor.capture(), anyString()); assertNotNull(argumentCaptor.getValue()); assertEquals("eventName", argumentCaptor.getValue().getName()); @@ -402,7 +403,7 @@ public void setEnabled() throws InterruptedException { verify(channel, times(2)).removeGroup(analytics.getGroupName()); verify(channel).clear(analytics.getGroupName()); verifyStatic(); - StorageHelper.PreferencesStorage.remove("sessions"); + SharedPreferencesManager.remove("sessions"); /* Now try to use all methods. Should not work. */ Analytics.trackEvent("test"); @@ -448,7 +449,7 @@ public void accept(Void aVoid) { @Test public void disablePersisted() { - when(StorageHelper.PreferencesStorage.getBoolean(ANALYTICS_ENABLED_KEY, true)).thenReturn(false); + when(SharedPreferencesManager.getBoolean(ANALYTICS_ENABLED_KEY, true)).thenReturn(false); Analytics analytics = Analytics.getInstance(); /* Start. */ diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java index 7a63d4b4ba..a2cfdb73ea 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java @@ -9,7 +9,7 @@ import com.microsoft.appcenter.channel.Channel; import com.microsoft.appcenter.ingestion.models.Log; import com.microsoft.appcenter.ingestion.models.StartServiceLog; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.Before; import org.junit.Rule; @@ -50,7 +50,7 @@ import static org.powermock.api.mockito.PowerMockito.verifyStatic; @SuppressWarnings("unused") -@PrepareForTest({SessionTracker.class, SessionContext.class, StorageHelper.PreferencesStorage.class, SystemClock.class}) +@PrepareForTest({SessionTracker.class, SessionContext.class, SharedPreferencesManager.class, SystemClock.class}) public class SessionTrackerTest { private final static String TEST_GROUP = "group_test"; @@ -80,7 +80,7 @@ private void spendTime(long time) { public void setUp() { mockStatic(System.class); mockStatic(SystemClock.class); - mockStatic(StorageHelper.PreferencesStorage.class); + mockStatic(SharedPreferencesManager.class); PowerMockito.doAnswer(new Answer() { @Override @@ -90,12 +90,12 @@ public Void answer(InvocationOnMock invocation) { /* Whenever the new state is persisted, make further calls return the new state. */ String key = (String) invocation.getArguments()[0]; Set value = (Set) invocation.getArguments()[1]; - when(StorageHelper.PreferencesStorage.getStringSet(key)).thenReturn(value); + when(SharedPreferencesManager.getStringSet(key)).thenReturn(value); return null; } - }).when(StorageHelper.PreferencesStorage.class); - StorageHelper.PreferencesStorage.putStringSet(anyString(), anySetOf(String.class)); - when(StorageHelper.PreferencesStorage.getStringSet(anyString())).thenReturn(null); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putStringSet(anyString(), anySetOf(String.class)); + when(SharedPreferencesManager.getStringSet(anyString())).thenReturn(null); SessionContext.unsetInstance(); spendTime(1000); mChannel = mock(Channel.class); @@ -415,24 +415,24 @@ public void sdkConfiguredBetweenPauseAndResume() { public void maxOutStoredSessions() { SessionContext.getInstance(); spendTime(1000); - Set sessions = StorageHelper.PreferencesStorage.getStringSet("sessions"); + Set sessions = SharedPreferencesManager.getStringSet("sessions"); assertNotNull(sessions); assertEquals(1, sessions.size()); String firstSession = sessions.iterator().next(); mSessionTracker.onPreparingLog(newEvent(), TEST_GROUP); - sessions = StorageHelper.PreferencesStorage.getStringSet("sessions"); + sessions = SharedPreferencesManager.getStringSet("sessions"); assertNotNull(sessions); assertEquals(2, sessions.size()); spendTime(30000); for (int i = 3; i <= 10; i++) { mSessionTracker.onPreparingLog(newEvent(), TEST_GROUP); - Set intermediateSessions = StorageHelper.PreferencesStorage.getStringSet("sessions"); + Set intermediateSessions = SharedPreferencesManager.getStringSet("sessions"); assertNotNull(intermediateSessions); assertEquals(i, intermediateSessions.size()); spendTime(30000); } mSessionTracker.onPreparingLog(newEvent(), TEST_GROUP); - Set finalSessions = StorageHelper.PreferencesStorage.getStringSet("sessions"); + Set finalSessions = SharedPreferencesManager.getStringSet("sessions"); assertNotNull(finalSessions); assertEquals(10, finalSessions.size()); assertFalse(finalSessions.contains(firstSession)); @@ -473,7 +473,7 @@ public void pastSessions() { log.setTimestamp(new Date(firstSessionTime + 1)); mSessionTracker.onPreparingLog(log, TEST_GROUP); assertEquals(currentSid, log.getSid()); - Set sessions = StorageHelper.PreferencesStorage.getStringSet("sessions"); + Set sessions = SharedPreferencesManager.getStringSet("sessions"); assertNotNull(sessions); assertEquals(1, sessions.size()); } @@ -483,7 +483,7 @@ public void pastSessions() { Log log = newEvent(); mSessionTracker.onPreparingLog(log, TEST_GROUP); assertNotEquals(currentSid, log.getSid()); - Set sessions = StorageHelper.PreferencesStorage.getStringSet("sessions"); + Set sessions = SharedPreferencesManager.getStringSet("sessions"); assertNotNull(sessions); assertEquals(2, sessions.size()); } @@ -495,7 +495,7 @@ public void pastSessions() { log.setTimestamp(new Date(firstSessionTime + 1)); mSessionTracker.onPreparingLog(log, TEST_GROUP); assertEquals(firstSid, log.getSid()); - Set sessions = StorageHelper.PreferencesStorage.getStringSet("sessions"); + Set sessions = SharedPreferencesManager.getStringSet("sessions"); assertNotNull(sessions); assertEquals(2, sessions.size()); } @@ -507,7 +507,7 @@ public void pastSessions() { log.setTimestamp(new Date(firstSessionTime + 1)); mSessionTracker.onPreparingLog(log, TEST_GROUP); assertEquals(firstSid, log.getSid()); - Set sessions = StorageHelper.PreferencesStorage.getStringSet("sessions"); + Set sessions = SharedPreferencesManager.getStringSet("sessions"); assertNotNull(sessions); assertEquals(2, sessions.size()); } @@ -518,7 +518,7 @@ public void pastSessions() { log.setTimestamp(new Date(1)); mSessionTracker.onPreparingLog(log, TEST_GROUP); assertNull(log.getSid()); - Set sessions = StorageHelper.PreferencesStorage.getStringSet("sessions"); + Set sessions = SharedPreferencesManager.getStringSet("sessions"); assertNotNull(sessions); assertEquals(2, sessions.size()); } @@ -526,7 +526,7 @@ public void pastSessions() { /* Clear sessions. */ mSessionTracker.clearSessions(); verifyStatic(); - StorageHelper.PreferencesStorage.remove("sessions"); + SharedPreferencesManager.remove("sessions"); } @Test @@ -542,14 +542,14 @@ public void partiallyInvalidStorage() { sessions.add("800/"); sessions.add("900//899"); sessions.add("999//"); - when(StorageHelper.PreferencesStorage.getStringSet(anyString())).thenReturn(sessions); + when(SharedPreferencesManager.getStringSet(anyString())).thenReturn(sessions); mSessionTracker = new SessionTracker(mChannel, TEST_GROUP); /* Generate a current session. */ mSessionTracker.onPreparingLog(newEvent(), TEST_GROUP); /* Check sessions in store. */ - sessions = StorageHelper.PreferencesStorage.getStringSet("sessions"); + sessions = SharedPreferencesManager.getStringSet("sessions"); assertNotNull(sessions); assertEquals(6, sessions.size()); assertTrue(sessions.contains("100/10abd355-40a5-4b51-8071-cb5a4c338531/99")); diff --git a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java index f88a4a07db..3aae8c1f8e 100644 --- a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java +++ b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java @@ -17,6 +17,7 @@ import com.microsoft.appcenter.ingestion.models.Log; import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.async.AppCenterConsumer; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import com.microsoft.appcenter.utils.storage.StorageHelper; import org.junit.After; @@ -78,13 +79,14 @@ public static void setUpClass() { sDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler(); sApplication = (Application) InstrumentationRegistry.getContext().getApplicationContext(); StorageHelper.initialize(sApplication); + SharedPreferencesManager.initialize(sApplication); Constants.loadFromContext(sApplication); } @Before public void setUp() { Thread.setDefaultUncaughtExceptionHandler(sDefaultCrashHandler); - StorageHelper.PreferencesStorage.clear(); + SharedPreferencesManager.clear(); for (File logFile : ErrorLogHelper.getErrorStorageDirectory().listFiles()) { if (logFile.isDirectory()) { for (File dumpDir : logFile.listFiles()) { diff --git a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerAndroidTest.java b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerAndroidTest.java index 5c621f97ed..2ac2201eb1 100644 --- a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerAndroidTest.java +++ b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerAndroidTest.java @@ -10,6 +10,7 @@ import com.microsoft.appcenter.channel.Channel; import com.microsoft.appcenter.crashes.ingestion.models.Exception; import com.microsoft.appcenter.crashes.utils.ErrorLogHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import com.microsoft.appcenter.utils.storage.StorageHelper; import org.junit.Assert; @@ -36,14 +37,14 @@ public class WrapperSdkExceptionManagerAndroidTest { @BeforeClass public static void setUpClass() { sApplication = (Application) InstrumentationRegistry.getContext().getApplicationContext(); - StorageHelper.initialize(sApplication); + SharedPreferencesManager.initialize(sApplication); Constants.loadFromContext(sApplication); } @Before public void setUp() { android.util.Log.i(TAG, "Cleanup"); - StorageHelper.PreferencesStorage.clear(); + SharedPreferencesManager.clear(); for (File logFile : ErrorLogHelper.getErrorStorageDirectory().listFiles()) { if (!logFile.isDirectory()) { assertTrue(logFile.delete()); diff --git a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java index 197d3698ce..92f797949d 100644 --- a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java +++ b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java @@ -30,6 +30,7 @@ import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.async.AppCenterFuture; import com.microsoft.appcenter.utils.async.DefaultAppCenterFuture; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import com.microsoft.appcenter.utils.storage.StorageHelper; import org.json.JSONException; @@ -677,7 +678,7 @@ private void processPendingErrors() { private boolean sendCrashReportsOrAwaitUserConfirmation() { /* Handle user confirmation in UI thread. */ - final boolean alwaysSend = StorageHelper.PreferencesStorage.getBoolean(PREF_KEY_ALWAYS_SEND, false); + final boolean alwaysSend = SharedPreferencesManager.getBoolean(PREF_KEY_ALWAYS_SEND, false); HandlerUtils.runOnUiThread(new Runnable() { @Override @@ -798,7 +799,7 @@ public void run() { /* Always send: we remember. */ if (userConfirmation == ALWAYS_SEND) { - StorageHelper.PreferencesStorage.putBoolean(PREF_KEY_ALWAYS_SEND, true); + SharedPreferencesManager.putBoolean(PREF_KEY_ALWAYS_SEND, true); } /* Send every pending report. */ diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java index f903a0a7fd..5f30e1c1fe 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java @@ -32,6 +32,7 @@ import com.microsoft.appcenter.utils.UUIDUtils; import com.microsoft.appcenter.utils.async.AppCenterConsumer; import com.microsoft.appcenter.utils.async.AppCenterFuture; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import com.microsoft.appcenter.utils.storage.StorageHelper; import org.json.JSONException; @@ -93,7 +94,7 @@ import static org.powermock.api.mockito.PowerMockito.verifyStatic; @SuppressWarnings("unused") -@PrepareForTest({ErrorLogHelper.class, SystemClock.class, StorageHelper.InternalStorage.class, StorageHelper.PreferencesStorage.class, AppCenterLog.class, AppCenter.class, Crashes.class, HandlerUtils.class, Looper.class}) +@PrepareForTest({ErrorLogHelper.class, SystemClock.class, StorageHelper.InternalStorage.class, SharedPreferencesManager.class, AppCenterLog.class, AppCenter.class, Crashes.class, HandlerUtils.class, Looper.class}) public class CrashesTest { @SuppressWarnings("ThrowableInstanceNeverThrown") @@ -129,7 +130,7 @@ public void setUp() { Crashes.unsetInstance(); mockStatic(SystemClock.class); mockStatic(StorageHelper.InternalStorage.class); - mockStatic(StorageHelper.PreferencesStorage.class); + mockStatic(SharedPreferencesManager.class); mockStatic(AppCenterLog.class); when(SystemClock.elapsedRealtime()).thenReturn(System.currentTimeMillis()); @@ -140,7 +141,7 @@ public void setUp() { when(AppCenter.isEnabled()).thenReturn(future); when(future.get()).thenReturn(true); - when(StorageHelper.PreferencesStorage.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(true); + when(SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(true); /* Then simulate further changes to state. */ doAnswer(new Answer() { @@ -150,11 +151,11 @@ public Object answer(InvocationOnMock invocation) { /* Whenever the new state is persisted, make further calls return the new state. */ boolean enabled = (Boolean) invocation.getArguments()[1]; - when(StorageHelper.PreferencesStorage.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(enabled); + when(SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(enabled); return null; } - }).when(StorageHelper.PreferencesStorage.class); - StorageHelper.PreferencesStorage.putBoolean(eq(CRASHES_ENABLED_KEY), anyBoolean()); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putBoolean(eq(CRASHES_ENABLED_KEY), anyBoolean()); /* Mock handlers. */ mockStatic(HandlerUtils.class); @@ -193,13 +194,13 @@ public void initializeWhenDisabled() { when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); when(dir.listFiles()).thenReturn(new File[]{file1, file2}); crashes.setUncaughtExceptionHandler(mockHandler); - when(StorageHelper.PreferencesStorage.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(false); + when(SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(false); crashes.onStarting(mAppCenterHandler); crashes.onStarted(mock(Context.class), mock(Channel.class), "", null, true); /* Test. */ verifyStatic(times(3)); - StorageHelper.PreferencesStorage.getBoolean(CRASHES_ENABLED_KEY, true); + SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true); assertFalse(Crashes.isEnabled().get()); assertEquals(crashes.getInitializeTimestamp(), -1); assertNull(crashes.getUncaughtExceptionHandler()); @@ -404,7 +405,7 @@ public void queuePendingCrashesAlwaysSend() throws IOException, ClassNotFoundExc when(ErrorLogHelper.getErrorReportFromErrorLog(any(ManagedErrorLog.class), any(Throwable.class))).thenReturn(report); when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn(""); when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(new RuntimeException()); - when(StorageHelper.PreferencesStorage.getBoolean(eq(Crashes.PREF_KEY_ALWAYS_SEND), anyBoolean())).thenReturn(true); + when(SharedPreferencesManager.getBoolean(eq(Crashes.PREF_KEY_ALWAYS_SEND), anyBoolean())).thenReturn(true); CrashesListener mockListener = mock(CrashesListener.class); when(mockListener.shouldProcess(report)).thenReturn(true); @@ -464,7 +465,7 @@ public void processPendingErrorsCorrupted() throws JSONException { public void noQueueingWhenDisabled() { mockStatic(ErrorLogHelper.class); when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(errorStorageDirectory.getRoot()); - when(StorageHelper.PreferencesStorage.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(false); + when(SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(false); Channel channel = mock(Channel.class); Crashes crashes = Crashes.getInstance(); crashes.onStarting(mAppCenterHandler); @@ -846,7 +847,7 @@ public void handleUserConfirmationAlwaysSend() throws IOException, ClassNotFound Crashes.notifyUserConfirmation(Crashes.ALWAYS_SEND); verifyStatic(); - StorageHelper.PreferencesStorage.putBoolean(Crashes.PREF_KEY_ALWAYS_SEND, true); + SharedPreferencesManager.putBoolean(Crashes.PREF_KEY_ALWAYS_SEND, true); } @Test @@ -1231,8 +1232,8 @@ public ManagedErrorLog answer(InvocationOnMock invocation) { /* Confirm with always send. */ Crashes.notifyUserConfirmation(Crashes.ALWAYS_SEND); verifyStatic(); - StorageHelper.PreferencesStorage.putBoolean(Crashes.PREF_KEY_ALWAYS_SEND, true); - when(StorageHelper.PreferencesStorage.getBoolean(eq(Crashes.PREF_KEY_ALWAYS_SEND), anyBoolean())).thenReturn(true); + SharedPreferencesManager.putBoolean(Crashes.PREF_KEY_ALWAYS_SEND, true); + when(SharedPreferencesManager.getBoolean(eq(Crashes.PREF_KEY_ALWAYS_SEND), anyBoolean())).thenReturn(true); /* 1 log sent. Other one is filtered. */ verify(mockChannel).enqueue(any(ManagedErrorLog.class), eq(crashes.getGroupName())); diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/UncaughtExceptionHandlerTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/UncaughtExceptionHandlerTest.java index 1f0b8ac240..eecc78541d 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/UncaughtExceptionHandlerTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/UncaughtExceptionHandlerTest.java @@ -15,6 +15,7 @@ import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.ShutdownHelper; import com.microsoft.appcenter.utils.async.AppCenterFuture; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import com.microsoft.appcenter.utils.storage.StorageHelper; import org.json.JSONException; @@ -52,7 +53,7 @@ import static org.powermock.api.mockito.PowerMockito.when; @SuppressWarnings("unused") -@PrepareForTest({SystemClock.class, StorageHelper.PreferencesStorage.class, StorageHelper.InternalStorage.class, Crashes.class, ErrorLogHelper.class, DeviceInfoHelper.class, ShutdownHelper.class, AppCenterLog.class, AppCenter.class, HandlerUtils.class}) +@PrepareForTest({SystemClock.class, SharedPreferencesManager.class, StorageHelper.InternalStorage.class, Crashes.class, ErrorLogHelper.class, DeviceInfoHelper.class, ShutdownHelper.class, AppCenterLog.class, AppCenter.class, HandlerUtils.class}) public class UncaughtExceptionHandlerTest { private static final String CRASHES_ENABLED_KEY = KEY_ENABLED + "_" + Crashes.getInstance().getServiceName(); @@ -70,8 +71,8 @@ public void setUp() { mockStatic(AppCenter.class); mockStatic(AppCenterLog.class); mockStatic(SystemClock.class); - mockStatic(StorageHelper.PreferencesStorage.class); mockStatic(StorageHelper.InternalStorage.class); + mockStatic(SharedPreferencesManager.class); mockStatic(ErrorLogHelper.class); mockStatic(DeviceInfoHelper.class); mockStatic(System.class); @@ -80,7 +81,7 @@ public void setUp() { AppCenterFuture future = (AppCenterFuture) mock(AppCenterFuture.class); when(AppCenter.isEnabled()).thenReturn(future); when(future.get()).thenReturn(true); - when(StorageHelper.PreferencesStorage.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(true); + when(SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(true); /* Then simulate further changes to state. */ PowerMockito.doAnswer(new Answer() { @@ -90,11 +91,11 @@ public Object answer(InvocationOnMock invocation) { /* Whenever the new state is persisted, make further calls return the new state. */ boolean enabled = (Boolean) invocation.getArguments()[1]; - Mockito.when(StorageHelper.PreferencesStorage.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(enabled); + Mockito.when(SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(enabled); return null; } - }).when(StorageHelper.PreferencesStorage.class); - StorageHelper.PreferencesStorage.putBoolean(eq(CRASHES_ENABLED_KEY), anyBoolean()); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putBoolean(eq(CRASHES_ENABLED_KEY), anyBoolean()); ManagedErrorLog errorLogMock = mock(ManagedErrorLog.class); when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(new File(".")); diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java index 291e967399..2183425cc0 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java @@ -12,6 +12,7 @@ import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.async.AppCenterFuture; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import com.microsoft.appcenter.utils.storage.StorageHelper; import org.json.JSONException; @@ -52,7 +53,7 @@ import static org.powermock.api.mockito.PowerMockito.when; import static org.powermock.api.mockito.PowerMockito.whenNew; -@PrepareForTest({AppCenter.class, WrapperSdkExceptionManager.class, AppCenterLog.class, StorageHelper.PreferencesStorage.class, StorageHelper.InternalStorage.class, Crashes.class, ErrorLogHelper.class, HandlerUtils.class}) +@PrepareForTest({AppCenter.class, WrapperSdkExceptionManager.class, AppCenterLog.class, SharedPreferencesManager.class, StorageHelper.InternalStorage.class, Crashes.class, ErrorLogHelper.class, HandlerUtils.class}) public class WrapperSdkExceptionManagerTest { private static final String CRASHES_ENABLED_KEY = KEY_ENABLED + "_" + Crashes.getInstance().getServiceName(); @@ -67,8 +68,8 @@ public class WrapperSdkExceptionManagerTest { public void setUp() { Crashes.unsetInstance(); mockStatic(AppCenter.class); - mockStatic(StorageHelper.PreferencesStorage.class); mockStatic(StorageHelper.InternalStorage.class); + mockStatic(SharedPreferencesManager.class); mockStatic(AppCenterLog.class); mockStatic(ErrorLogHelper.class); when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(errorStorageDirectory.getRoot()); @@ -81,7 +82,7 @@ public void setUp() { AppCenterFuture future = (AppCenterFuture) mock(AppCenterFuture.class); when(AppCenter.isEnabled()).thenReturn(future); when(future.get()).thenReturn(true); - when(StorageHelper.PreferencesStorage.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(true); + when(SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(true); /* Mock handlers. */ mockStatic(HandlerUtils.class); @@ -346,7 +347,7 @@ public boolean matches(Object argument) { @Test public void saveWrapperExceptionWhenSDKDisabled() throws JSONException { - when(StorageHelper.PreferencesStorage.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(false); + when(SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(false); LogSerializer logSerializer = Mockito.mock(LogSerializer.class); Crashes.getInstance().setLogSerializer(logSerializer); WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), null, new Exception(), new byte[]{'d'}); diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/CheckDownloadTask.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/CheckDownloadTask.java index d02616fe7b..cff41dca7e 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/CheckDownloadTask.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/CheckDownloadTask.java @@ -11,7 +11,7 @@ import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.HandlerUtils; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import java.util.NoSuchElementException; @@ -200,8 +200,8 @@ private void storeDownloadedReleaseDetails() { String releaseHash = mReleaseDetails.getReleaseHash(); int releaseId = mReleaseDetails.getId(); AppCenterLog.debug(LOG_TAG, "Store downloaded group id=" + groupId + " release hash=" + releaseHash + " release id=" + releaseId); - StorageHelper.PreferencesStorage.putString(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID, groupId); - StorageHelper.PreferencesStorage.putString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH, releaseHash); - StorageHelper.PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID, releaseId); + SharedPreferencesManager.putString(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID, groupId); + SharedPreferencesManager.putString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH, releaseHash); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID, releaseId); } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 75f92c4852..99eb70e87d 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -54,8 +54,7 @@ import com.microsoft.appcenter.utils.async.AppCenterConsumer; import com.microsoft.appcenter.utils.async.AppCenterFuture; import com.microsoft.appcenter.utils.crypto.CryptoUtils; -import com.microsoft.appcenter.utils.storage.StorageHelper; -import com.microsoft.appcenter.utils.storage.StorageHelper.PreferencesStorage; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.json.JSONException; @@ -427,7 +426,7 @@ synchronized ReleaseDetails startFromBackground(Context context) { if (mAppSecret == null) { AppCenterLog.debug(LOG_TAG, "Called before onStart, init storage"); mContext = context; - StorageHelper.initialize(mContext); + SharedPreferencesManager.initialize(mContext); mMobileCenterPreferenceStorage = mContext.getSharedPreferences(PREFERENCES_NAME_MOBILE_CENTER, Context.MODE_PRIVATE); mReleaseDetails = DistributeUtils.loadCachedReleaseDetails(); } @@ -479,7 +478,7 @@ protected synchronized void applyEnabledState(boolean enabled) { changeDistributionGroupIdAfterAppUpdateIfNeeded(); /* Enable the distribute info tracker. */ - String distributionGroupId = PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID); + String distributionGroupId = SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID); mDistributeInfoTracker = new DistributeInfoTracker(distributionGroupId); mChannel.addListener(mDistributeInfoTracker); HandlerUtils.runOnUiThread(new Runnable() { @@ -496,11 +495,11 @@ public void run() { mBrowserOpenedOrAborted = false; mWorkflowCompleted = false; cancelPreviousTasks(); - PreferencesStorage.remove(PREFERENCE_KEY_REQUEST_ID); - PreferencesStorage.remove(PREFERENCE_KEY_POSTPONE_TIME); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); - PreferencesStorage.remove(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_POSTPONE_TIME); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY); /* Disable the distribute info tracker. */ mChannel.removeListener(mDistributeInfoTracker); @@ -607,10 +606,10 @@ private synchronized void cancelPreviousTasks() { AppCenterLog.debug(LOG_TAG, "Removing download and notification id=" + downloadId); removeDownload(downloadId); } - PreferencesStorage.remove(PREFERENCE_KEY_RELEASE_DETAILS); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_TIME); + SharedPreferencesManager.remove(PREFERENCE_KEY_RELEASE_DETAILS); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_TIME); } /** @@ -638,16 +637,16 @@ private synchronized void resumeDistributeWorkflow() { * Only if the app build is different (different package hash), try enabling in-app updates again. */ String releaseHash = DistributeUtils.computeReleaseHash(this.mPackageInfo); - String updateSetupFailedPackageHash = PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); + String updateSetupFailedPackageHash = SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); if (updateSetupFailedPackageHash != null) { if (releaseHash.equals(updateSetupFailedPackageHash)) { AppCenterLog.info(LOG_TAG, "Skipping in-app updates setup, because it already failed on this release before."); return; } else { AppCenterLog.info(LOG_TAG, "Re-attempting in-app updates setup and cleaning up failure info from storage."); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); - PreferencesStorage.remove(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY); } } @@ -678,7 +677,7 @@ private synchronized void resumeDistributeWorkflow() { if (downloadState != DOWNLOAD_STATE_COMPLETED && downloadState != DOWNLOAD_STATE_AVAILABLE && !mCheckedDownload) { /* Discard release if application updated. Then immediately check release. */ - if (mPackageInfo.lastUpdateTime > StorageHelper.PreferencesStorage.getLong(PREFERENCE_KEY_DOWNLOAD_TIME)) { + if (mPackageInfo.lastUpdateTime > SharedPreferencesManager.getLong(PREFERENCE_KEY_DOWNLOAD_TIME)) { AppCenterLog.debug(LOG_TAG, "Discarding previous download as application updated."); cancelPreviousTasks(); } @@ -755,7 +754,7 @@ else if (mUnknownSourcesDialog != null) { * message and also store the package hash that the failure occurred on. The setup * will only be re-attempted the next time the app gets updated (and package hash changes). */ - String updateSetupFailedMessage = PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); + String updateSetupFailedMessage = SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); if (updateSetupFailedMessage != null) { AppCenterLog.debug(LOG_TAG, "In-app updates setup failure detected."); showUpdateSetupFailedDialog(); @@ -777,8 +776,8 @@ else if (mUnknownSourcesDialog != null) { * especially if we decide to tie private in-app updates to a specific group. That is * also why we already store the group for future use even for private group updates. */ - String updateToken = PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN); - String distributionGroupId = PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID); + String updateToken = SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN); + String distributionGroupId = SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID); if (updateToken != null || distributionGroupId != null) { decryptAndGetReleaseDetails(updateToken, distributionGroupId, false); return; @@ -794,7 +793,7 @@ else if (mUnknownSourcesDialog != null) { } /* If not, open native app (if installed) to update setup, unless it already failed. Otherwise, use the browser. */ - String testerAppUpdateSetupFailedMessage = PreferencesStorage.getString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY); + String testerAppUpdateSetupFailedMessage = SharedPreferencesManager.getString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY); boolean shouldUseTesterAppForUpdateSetup = isAppCenterTesterAppInstalled() && TextUtils.isEmpty(testerAppUpdateSetupFailedMessage) && !mContext.getPackageName().equals(DistributeUtils.TESTER_APP_PACKAGE_NAME); if (shouldUseTesterAppForUpdateSetup && !mTesterAppOpenedOrAborted) { DistributeUtils.updateSetupUsingTesterApp(mForegroundActivity, mPackageInfo); @@ -824,20 +823,20 @@ private void decryptAndGetReleaseDetails(String updateToken, String distribution /* Store new encrypted value if updated. */ if (newEncryptedData != null) { - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_TOKEN, newEncryptedData); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, newEncryptedData); } updateToken = decryptedData.getDecryptedData(); if (mobileCenterFailOver) { /* Store the token from Mobile Center into App Center storage, re-encrypting it */ String encryptedUpdateToken = CryptoUtils.getInstance(mContext).encrypt(updateToken); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_TOKEN, encryptedUpdateToken); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, encryptedUpdateToken); } } /* If the group was from Mobile Center storage, save it in the new storage. */ if (mobileCenterFailOver) { - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, distributionGroupId); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, distributionGroupId); mDistributeInfoTracker.updateDistributionGroupId(distributionGroupId); } @@ -874,8 +873,8 @@ private synchronized void cancelNotification() { */ synchronized void completeWorkflow() { cancelNotification(); - PreferencesStorage.remove(PREFERENCE_KEY_RELEASE_DETAILS); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_RELEASE_DETAILS); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); mCheckReleaseApiCall = null; mCheckReleaseCallId = null; mUpdateDialog = null; @@ -892,9 +891,9 @@ synchronized void completeWorkflow() { * Store update setup failure message used later to show in setup failure dialog for user. */ synchronized void storeUpdateSetupFailedParameter(@NonNull String requestId, @NonNull String updateSetupFailed) { - if (requestId.equals(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID))) { + if (requestId.equals(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID))) { AppCenterLog.debug(LOG_TAG, "Stored update setup failed parameter."); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY, updateSetupFailed); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY, updateSetupFailed); } } @@ -902,9 +901,9 @@ synchronized void storeUpdateSetupFailedParameter(@NonNull String requestId, @No * Store a flag for failure to enable updates from the tester apps, to later reattempt using the browser update setup. */ synchronized void storeTesterAppUpdateSetupFailedParameter(@NonNull String requestId, @NonNull String updateSetupFailed) { - if (requestId.equals(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID))) { + if (requestId.equals(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID))) { AppCenterLog.debug(LOG_TAG, "Stored tester app update setup failed parameter."); - PreferencesStorage.putString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY, updateSetupFailed); + SharedPreferencesManager.putString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY, updateSetupFailed); } } @@ -919,16 +918,16 @@ synchronized void storeRedirectionParameters(@NonNull String requestId, @NonNull mBeforeStartRequestId = requestId; mBeforeStartUpdateToken = updateToken; mBeforeStartDistributionGroupId = distributionGroupId; - } else if (requestId.equals(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID))) { + } else if (requestId.equals(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID))) { if (updateToken != null) { String encryptedToken = CryptoUtils.getInstance(mContext).encrypt(updateToken); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_TOKEN, encryptedToken); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, encryptedToken); } else { - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_TOKEN); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_TOKEN); } - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, distributionGroupId); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, distributionGroupId); AppCenterLog.debug(LOG_TAG, "Stored redirection parameters."); - PreferencesStorage.remove(PREFERENCE_KEY_REQUEST_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); mDistributeInfoTracker.updateDistributionGroupId(distributionGroupId); enqueueDistributionStartSessionLog(); cancelPreviousTasks(); @@ -1048,8 +1047,8 @@ private synchronized void handleApiCallFailure(Object releaseCallId, Exception e AppCenterLog.info(LOG_TAG, "No release available to the current user."); } else { AppCenterLog.error(LOG_TAG, "Failed to check latest release:", e); - PreferencesStorage.remove(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_TOKEN); + SharedPreferencesManager.remove(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_TOKEN); mDistributeInfoTracker.removeDistributionGroupId(); } } @@ -1060,12 +1059,12 @@ private synchronized void handleApiCallFailure(Object releaseCallId, Exception e * Handle API call success. */ private synchronized void handleApiCallSuccess(Object releaseCallId, String rawReleaseDetails, ReleaseDetails releaseDetails) { - String lastDownloadedReleaseHash = PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); + String lastDownloadedReleaseHash = SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); if (!TextUtils.isEmpty(lastDownloadedReleaseHash)) { if (isCurrentReleaseWasUpdated(lastDownloadedReleaseHash)) { AppCenterLog.debug(LOG_TAG, "Successfully reported app update for downloaded release hash (" + lastDownloadedReleaseHash + "), removing from store.."); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID); } else { AppCenterLog.debug(LOG_TAG, "Stored release hash doesn't match current installation, probably downloaded but not installed yet, keep in store"); } @@ -1083,13 +1082,13 @@ private synchronized void handleApiCallSuccess(Object releaseCallId, String rawR if (isMoreRecent(releaseDetails) && canUpdateNow(releaseDetails)) { /* Update cache. */ - PreferencesStorage.putString(PREFERENCE_KEY_RELEASE_DETAILS, rawReleaseDetails); + SharedPreferencesManager.putString(PREFERENCE_KEY_RELEASE_DETAILS, rawReleaseDetails); /* If previous release is mandatory and still processing, don't do anything right now. */ if (mReleaseDetails != null && mReleaseDetails.isMandatoryUpdate()) { if (mReleaseDetails.getId() != releaseDetails.getId()) { AppCenterLog.debug(LOG_TAG, "Latest release is more recent than the previous mandatory."); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); } else { AppCenterLog.debug(LOG_TAG, "The latest release is mandatory and already being processed."); } @@ -1099,7 +1098,7 @@ private synchronized void handleApiCallSuccess(Object releaseCallId, String rawR /* Show update dialog. */ mReleaseDetails = releaseDetails; AppCenterLog.debug(LOG_TAG, "Latest release is more recent."); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); if (mForegroundActivity != null) { showUpdateDialog(); } @@ -1126,7 +1125,7 @@ private synchronized void handleApiCallSuccess(Object releaseCallId, String rawR private String getReportingParametersForUpdatedRelease(boolean isPublic, String distributionGroupId) { String reportingParameters = ""; AppCenterLog.debug(LOG_TAG, "Check if we need to report release installation.."); - String lastDownloadedReleaseHash = PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); + String lastDownloadedReleaseHash = SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); if (!TextUtils.isEmpty(lastDownloadedReleaseHash)) { if (isCurrentReleaseWasUpdated(lastDownloadedReleaseHash)) { AppCenterLog.debug(LOG_TAG, "Current release was updated but not reported yet, reporting.."); @@ -1135,7 +1134,7 @@ private String getReportingParametersForUpdatedRelease(boolean isPublic, String } else { reportingParameters += "&" + PARAMETER_DISTRIBUTION_GROUP_ID + "=" + distributionGroupId; } - int lastDownloadedReleaseId = PreferencesStorage.getInt(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID); + int lastDownloadedReleaseId = SharedPreferencesManager.getInt(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID); reportingParameters += "&" + PARAMETER_RELEASE_ID + "=" + lastDownloadedReleaseId; } else { AppCenterLog.debug(LOG_TAG, "New release was downloaded but not installed yet, skip reporting."); @@ -1152,22 +1151,22 @@ private String getReportingParametersForUpdatedRelease(boolean isPublic, String * was distributed to another group. */ private void changeDistributionGroupIdAfterAppUpdateIfNeeded() { - String lastDownloadedReleaseHash = PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); - String lastDownloadedDistributionGroupId = PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); + String lastDownloadedReleaseHash = SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); + String lastDownloadedDistributionGroupId = SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); if (!isCurrentReleaseWasUpdated(lastDownloadedReleaseHash) || TextUtils.isEmpty(lastDownloadedDistributionGroupId)) { return; } - String currentDistributionGroupId = PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID); + String currentDistributionGroupId = SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID); if (currentDistributionGroupId != null && lastDownloadedDistributionGroupId.equals(currentDistributionGroupId)) { return; } /* Set group ID from downloaded release details. */ AppCenterLog.debug(LOG_TAG, "Current group ID doesn't match the group ID of downloaded release, updating current group id=" + lastDownloadedDistributionGroupId); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, lastDownloadedDistributionGroupId); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, lastDownloadedDistributionGroupId); /* Remove saved downloaded group ID. */ - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); } /** @@ -1214,10 +1213,10 @@ private boolean canUpdateNow(ReleaseDetails releaseDetails) { return true; } long now = System.currentTimeMillis(); - long postponedTime = PreferencesStorage.getLong(PREFERENCE_KEY_POSTPONE_TIME, 0); + long postponedTime = SharedPreferencesManager.getLong(PREFERENCE_KEY_POSTPONE_TIME, 0); if (now < postponedTime) { AppCenterLog.debug(LOG_TAG, "User clock has been changed in past, cleaning postpone state and showing dialog"); - PreferencesStorage.remove(PREFERENCE_KEY_POSTPONE_TIME); + SharedPreferencesManager.remove(PREFERENCE_KEY_POSTPONE_TIME); return true; } long postponedUntil = postponedTime + POSTPONE_TIME_THRESHOLD; @@ -1343,7 +1342,7 @@ private void viewReleaseNotes(ReleaseDetails releaseDetails) { */ private synchronized void storeUpdateSetupFailedPackageHash(DialogInterface dialog) { if (mUpdateSetupFailedDialog == dialog) { - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY, DistributeUtils.computeReleaseHash(mPackageInfo)); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY, DistributeUtils.computeReleaseHash(mPackageInfo)); } else { showDisabledToast(); } @@ -1365,8 +1364,8 @@ private synchronized void handleUpdateFailedDialogReinstallAction(DialogInterfac BrowserUtils.openBrowser(url, mForegroundActivity); /* Clear the update setup failure info from storage, to re-attempt setup on reinstall. */ - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); - PreferencesStorage.remove(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY); } else { showDisabledToast(); } @@ -1461,7 +1460,7 @@ public void onClick(DialogInterface dialog, int which) { showAndRememberDialogActivity(mUpdateSetupFailedDialog); /* Don't show this dialog again. */ - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); } /** @@ -1505,7 +1504,7 @@ private synchronized void goToUnknownAppsSettings(ReleaseDetails releaseDetails) private synchronized void postponeRelease(ReleaseDetails releaseDetails) { if (releaseDetails == mReleaseDetails) { AppCenterLog.debug(LOG_TAG, "Postpone updates for a day."); - PreferencesStorage.putLong(PREFERENCE_KEY_POSTPONE_TIME, System.currentTimeMillis()); + SharedPreferencesManager.putLong(PREFERENCE_KEY_POSTPONE_TIME, System.currentTimeMillis()); completeWorkflow(); } else { showDisabledToast(); @@ -1577,9 +1576,9 @@ synchronized void storeDownloadRequestId(DownloadManager downloadManager, Downlo } /* Store new download identifier. */ - PreferencesStorage.putLong(PREFERENCE_KEY_DOWNLOAD_ID, downloadId); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_ENQUEUED); - PreferencesStorage.putLong(PREFERENCE_KEY_DOWNLOAD_TIME, enqueueTime); + SharedPreferencesManager.putLong(PREFERENCE_KEY_DOWNLOAD_ID, downloadId); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_ENQUEUED); + SharedPreferencesManager.putLong(PREFERENCE_KEY_DOWNLOAD_TIME, enqueueTime); /* Start monitoring progress for mandatory update. */ if (mReleaseDetails.isMandatoryUpdate()) { @@ -1681,7 +1680,7 @@ synchronized boolean notifyDownload(ReleaseDetails releaseDetails, Intent intent //noinspection ConstantConditions notificationManager.notify(DistributeUtils.getNotificationId(), notification); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_NOTIFIED); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_NOTIFIED); /* Reset check download flag to show install U.I. on resume if notification ignored. */ mCheckedDownload = false; @@ -1843,7 +1842,7 @@ private synchronized void installMandatoryUpdate(ReleaseDetails releaseDetails) synchronized void setInstalling(ReleaseDetails releaseDetails) { if (releaseDetails == mReleaseDetails) { cancelNotification(); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java index 8e831881c3..3847b382de 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java @@ -13,7 +13,7 @@ import com.microsoft.appcenter.utils.HashUtils; import com.microsoft.appcenter.utils.NetworkStateHelper; import com.microsoft.appcenter.utils.UUIDUtils; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.json.JSONException; @@ -76,7 +76,7 @@ static int getNotificationId() { * @return download identifier or negative value if not found. */ static long getStoredDownloadId() { - return StorageHelper.PreferencesStorage.getLong(PREFERENCE_KEY_DOWNLOAD_ID, INVALID_DOWNLOAD_IDENTIFIER); + return SharedPreferencesManager.getLong(PREFERENCE_KEY_DOWNLOAD_ID, INVALID_DOWNLOAD_IDENTIFIER); } /** @@ -85,7 +85,7 @@ static long getStoredDownloadId() { * @return download state (completed by default). */ static int getStoredDownloadState() { - return StorageHelper.PreferencesStorage.getInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_COMPLETED); + return SharedPreferencesManager.getInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_COMPLETED); } @NonNull @@ -117,7 +117,7 @@ static void updateSetupUsingTesterApp(Activity activity, PackageInfo packageInfo AppCenterLog.debug(LOG_TAG, "No token, need to open tester app to url=" + url); /* Store request id. */ - StorageHelper.PreferencesStorage.putString(PREFERENCE_KEY_REQUEST_ID, requestId); + SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId); /* Open the native tester app */ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); @@ -166,7 +166,7 @@ static void updateSetupUsingBrowser(Activity activity, String installUrl, String AppCenterLog.debug(LOG_TAG, "No token, need to open browser to url=" + url); /* Store request id. */ - StorageHelper.PreferencesStorage.putString(PREFERENCE_KEY_REQUEST_ID, requestId); + SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId); /* Open browser, remember that whatever the outcome to avoid opening it twice. */ BrowserUtils.openBrowser(url, activity); @@ -178,13 +178,13 @@ static void updateSetupUsingBrowser(Activity activity, String installUrl, String * @return release details from cache or null. */ static ReleaseDetails loadCachedReleaseDetails() { - String cachedReleaseDetails = StorageHelper.PreferencesStorage.getString(PREFERENCE_KEY_RELEASE_DETAILS); + String cachedReleaseDetails = SharedPreferencesManager.getString(PREFERENCE_KEY_RELEASE_DETAILS); if (cachedReleaseDetails != null) { try { return ReleaseDetails.parse(cachedReleaseDetails); } catch (JSONException e) { AppCenterLog.error(LOG_TAG, "Invalid release details in cache.", e); - StorageHelper.PreferencesStorage.remove(PREFERENCE_KEY_RELEASE_DETAILS); + SharedPreferencesManager.remove(PREFERENCE_KEY_RELEASE_DETAILS); } } return null; diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeAfterDownloadTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeAfterDownloadTest.java index 85a3e662b0..e5a6aa19b6 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeAfterDownloadTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeAfterDownloadTest.java @@ -21,7 +21,7 @@ import com.microsoft.appcenter.http.ServiceCallback; import com.microsoft.appcenter.test.TestUtils; import com.microsoft.appcenter.utils.AsyncTaskUtils; -import com.microsoft.appcenter.utils.storage.StorageHelper.PreferencesStorage; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.After; import org.mockito.ArgumentCaptor; @@ -117,60 +117,60 @@ void setUpDownload(boolean mandatoryUpdate) throws Exception { @Override public Void answer(InvocationOnMock invocation) { - when(PreferencesStorage.getLong(invocation.getArguments()[0].toString(), INVALID_DOWNLOAD_IDENTIFIER)).thenReturn((Long) invocation.getArguments()[1]); + when(SharedPreferencesManager.getLong(invocation.getArguments()[0].toString(), INVALID_DOWNLOAD_IDENTIFIER)).thenReturn((Long) invocation.getArguments()[1]); return null; } - }).when(PreferencesStorage.class); - PreferencesStorage.putLong(eq(PREFERENCE_KEY_DOWNLOAD_ID), anyLong()); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_DOWNLOAD_ID), anyLong()); doAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) { - when(PreferencesStorage.getLong(invocation.getArguments()[0].toString(), INVALID_DOWNLOAD_IDENTIFIER)).thenReturn(INVALID_DOWNLOAD_IDENTIFIER); + when(SharedPreferencesManager.getLong(invocation.getArguments()[0].toString(), INVALID_DOWNLOAD_IDENTIFIER)).thenReturn(INVALID_DOWNLOAD_IDENTIFIER); return null; } - }).when(PreferencesStorage.class); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); doAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) { - when(PreferencesStorage.getInt(invocation.getArguments()[0].toString(), DOWNLOAD_STATE_COMPLETED)).thenReturn((Integer) invocation.getArguments()[1]); + when(SharedPreferencesManager.getInt(invocation.getArguments()[0].toString(), DOWNLOAD_STATE_COMPLETED)).thenReturn((Integer) invocation.getArguments()[1]); return null; } - }).when(PreferencesStorage.class); - PreferencesStorage.putInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), anyInt()); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), anyInt()); doAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) { - when(PreferencesStorage.getInt(invocation.getArguments()[0].toString(), DOWNLOAD_STATE_COMPLETED)).thenReturn(DOWNLOAD_STATE_COMPLETED); + when(SharedPreferencesManager.getInt(invocation.getArguments()[0].toString(), DOWNLOAD_STATE_COMPLETED)).thenReturn(DOWNLOAD_STATE_COMPLETED); return null; } - }).when(PreferencesStorage.class); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); doAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) { - when(PreferencesStorage.getString(invocation.getArguments()[0].toString())).thenReturn(invocation.getArguments()[1].toString()); + when(SharedPreferencesManager.getString(invocation.getArguments()[0].toString())).thenReturn(invocation.getArguments()[1].toString()); return null; } - }).when(PreferencesStorage.class); - PreferencesStorage.putString(eq(PREFERENCE_KEY_RELEASE_DETAILS), anyString()); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putString(eq(PREFERENCE_KEY_RELEASE_DETAILS), anyString()); doAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) { - when(PreferencesStorage.getString(invocation.getArguments()[0].toString())).thenReturn(null); + when(SharedPreferencesManager.getString(invocation.getArguments()[0].toString())).thenReturn(null); return null; } - }).when(PreferencesStorage.class); - PreferencesStorage.remove(PREFERENCE_KEY_RELEASE_DETAILS); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.remove(PREFERENCE_KEY_RELEASE_DETAILS); /* Mock everything that triggers a download. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); when(httpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java index f30a749fbc..0bc757d730 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java @@ -23,7 +23,7 @@ import com.microsoft.appcenter.utils.UUIDUtils; import com.microsoft.appcenter.utils.async.AppCenterFuture; import com.microsoft.appcenter.utils.crypto.CryptoUtils; -import com.microsoft.appcenter.utils.storage.StorageHelper.PreferencesStorage; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.Before; import org.junit.Rule; @@ -52,7 +52,7 @@ import static org.powermock.api.mockito.PowerMockito.whenNew; @SuppressWarnings({"WeakerAccess", "CanBeFinal"}) -@PrepareForTest({Distribute.class, PreferencesStorage.class, AppNameHelper.class, AppCenterLog.class, AppCenter.class, NetworkStateHelper.class, BrowserUtils.class, UUIDUtils.class, ReleaseDetails.class, TextUtils.class, CryptoUtils.class, InstallerUtils.class, Toast.class, HandlerUtils.class}) +@PrepareForTest({Distribute.class, SharedPreferencesManager.class, AppNameHelper.class, AppCenterLog.class, AppCenter.class, NetworkStateHelper.class, BrowserUtils.class, UUIDUtils.class, ReleaseDetails.class, TextUtils.class, CryptoUtils.class, InstallerUtils.class, Toast.class, HandlerUtils.class}) public class AbstractDistributeTest { static final String TEST_HASH = HashUtils.sha256("com.contoso:1.2.3:6"); @@ -129,8 +129,8 @@ public Void answer(InvocationOnMock invocation) { whenNew(DistributeInfoTracker.class).withAnyArguments().thenReturn(mDistributeInfoTracker); /* First call to com.microsoft.appcenter.AppCenter.isEnabled shall return true, initial state. */ - mockStatic(PreferencesStorage.class); - when(PreferencesStorage.getBoolean(DISTRIBUTE_ENABLED_KEY, true)).thenReturn(true); + mockStatic(SharedPreferencesManager.class); + when(SharedPreferencesManager.getBoolean(DISTRIBUTE_ENABLED_KEY, true)).thenReturn(true); /* Mobile Center Preferences failover initialization */ when(mContext.getSharedPreferences(PREFERENCES_NAME_MOBILE_CENTER, Context.MODE_PRIVATE)).thenReturn(mMobileCenterPreferencesStorage); @@ -143,14 +143,14 @@ public Void answer(InvocationOnMock invocation) { /* Whenever the new state is persisted, make further calls return the new state. */ boolean enabled = (Boolean) invocation.getArguments()[1]; - when(PreferencesStorage.getBoolean(DISTRIBUTE_ENABLED_KEY, true)).thenReturn(enabled); + when(SharedPreferencesManager.getBoolean(DISTRIBUTE_ENABLED_KEY, true)).thenReturn(enabled); return null; } - }).when(PreferencesStorage.class); - PreferencesStorage.putBoolean(eq(DISTRIBUTE_ENABLED_KEY), anyBoolean()); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putBoolean(eq(DISTRIBUTE_ENABLED_KEY), anyBoolean()); /* Default download id when not found. */ - when(PreferencesStorage.getLong(PREFERENCE_KEY_DOWNLOAD_ID, INVALID_DOWNLOAD_IDENTIFIER)).thenReturn(INVALID_DOWNLOAD_IDENTIFIER); + when(SharedPreferencesManager.getLong(PREFERENCE_KEY_DOWNLOAD_ID, INVALID_DOWNLOAD_IDENTIFIER)).thenReturn(INVALID_DOWNLOAD_IDENTIFIER); /* Mock package manager. */ when(mContext.getApplicationContext()).thenReturn(mContext); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java index 0cef5c4cb4..2d2ee8f720 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java @@ -26,6 +26,7 @@ import com.microsoft.appcenter.utils.async.AppCenterConsumer; import com.microsoft.appcenter.utils.async.AppCenterFuture; import com.microsoft.appcenter.utils.crypto.CryptoUtils; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.json.JSONException; import org.junit.Test; @@ -69,7 +70,6 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; import static com.microsoft.appcenter.distribute.DistributeConstants.UPDATE_SETUP_PATH_FORMAT; -import static com.microsoft.appcenter.utils.storage.StorageHelper.PreferencesStorage; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; @@ -133,8 +133,8 @@ private void testDistributeInactive() throws Exception { * (if package name and signature matches an APK on Google Play you can upgrade to * Google Play version without losing data. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); restartProcessAndSdk(); @@ -143,7 +143,7 @@ private void testDistributeInactive() throws Exception { } private void showUpdateSetupFailedDialog() { - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn("failed_message"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn("failed_message"); when(mDialog.isShowing()).thenReturn(false); when(mDialogBuilder.create()).thenReturn(mDialog); @@ -157,7 +157,7 @@ private void showUpdateSetupFailedDialog() { verify(mDialogBuilder).setMessage(R.string.appcenter_distribute_update_failed_dialog_message); verify(mDialog).show(); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); } @Test @@ -174,13 +174,13 @@ public void doNothingIfInstallComesFromStore() throws Exception { @Test public void doNothingIfUpdateSetupFailedMessageExist() throws Exception { - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn("failed_message_from_backend"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn("failed_message_from_backend"); testDistributeInactive(); } @Test public void doNothingIfReleaseHashEqualsToFailedPackageHash() throws Exception { - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY)).thenReturn("some_hash"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY)).thenReturn("some_hash"); mockStatic(DistributeUtils.class); /* Mock the computeReleaseHash to some_hash value. */ @@ -190,7 +190,7 @@ public void doNothingIfReleaseHashEqualsToFailedPackageHash() throws Exception { @Test public void continueIfReleaseHashNotEqualsToFailedPackageHash() { - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY)).thenReturn("some_hash"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY)).thenReturn("some_hash"); mockStatic(DistributeUtils.class); /* Mock the computeReleaseHash to other_hash value. */ @@ -200,9 +200,9 @@ public void continueIfReleaseHashNotEqualsToFailedPackageHash() { start(); Distribute.getInstance().onActivityResumed(mock(Activity.class)); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); } @Test @@ -216,51 +216,51 @@ public void doNothingIfGetPackageInfoFails() throws Exception { public void storeUpdateSetupFailedParameterBeforeStart() { /* Setup mock. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); start(); Distribute.getInstance().storeUpdateSetupFailedParameter("r", "error_message"); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY, "error_message"); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY, "error_message"); } @Test public void storeTesterAppUpdateSetupFailedParameterBeforeStart() { /* Setup mock. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); start(); Distribute.getInstance().storeTesterAppUpdateSetupFailedParameter("r", "error_message"); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY, "error_message"); + SharedPreferencesManager.putString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY, "error_message"); } @Test public void storeUpdateSetupFailedParameterWithIncorrectRequestIdBeforeStart() { /* Setup mock. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); start(); Distribute.getInstance().storeUpdateSetupFailedParameter("r2", "error_message"); verifyStatic(never()); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY, "error_message"); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY, "error_message"); } @Test public void storeTesterAppUpdateSetupFailedParameterWithIncorrectRequestIdBeforeStart() { /* Setup mock. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); start(); Distribute.getInstance().storeTesterAppUpdateSetupFailedParameter("r2", "error_message"); verifyStatic(never()); - PreferencesStorage.putString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY, "error_message"); + SharedPreferencesManager.putString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY, "error_message"); } @Test public void storePrivateRedirectionBeforeStart() throws Exception { /* Setup mock. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); @@ -268,22 +268,22 @@ public void storePrivateRedirectionBeforeStart() throws Exception { Distribute.getInstance().storeRedirectionParameters("r", "g", "some token"); start(); verifyStatic(never()); - PreferencesStorage.putString(anyString(), anyString()); + SharedPreferencesManager.putString(anyString(), anyString()); verifyStatic(never()); - PreferencesStorage.remove(anyString()); + SharedPreferencesManager.remove(anyString()); /* Unlock the processing by going into foreground. */ Distribute.getInstance().onActivityResumed(mock(Activity.class)); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_REQUEST_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDistributeInfoTracker).updateDistributionGroupId("g"); HashMap headers = new HashMap<>(); headers.put(DistributeConstants.HEADER_API_TOKEN, "some token"); @@ -294,7 +294,7 @@ public void storePrivateRedirectionBeforeStart() throws Exception { public void storePublicRedirectionBeforeStart() throws Exception { /* Setup mock. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); @@ -302,22 +302,22 @@ public void storePublicRedirectionBeforeStart() throws Exception { Distribute.getInstance().storeRedirectionParameters("r", "g", null); start(); verifyStatic(never()); - PreferencesStorage.putString(anyString(), anyString()); + SharedPreferencesManager.putString(anyString(), anyString()); verifyStatic(never()); - PreferencesStorage.remove(anyString()); + SharedPreferencesManager.remove(anyString()); /* Unlock the processing by going into foreground. */ Distribute.getInstance().onActivityResumed(mock(Activity.class)); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_TOKEN); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_TOKEN); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_REQUEST_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDistributeInfoTracker).updateDistributionGroupId("g"); HashMap headers = new HashMap<>(); verify(httpClient).callAsync(anyString(), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); @@ -418,7 +418,7 @@ public void handleFailedUpdateSetupDialogReinstallAction() throws URISyntaxExcep verifyStatic(); BrowserUtils.appendUri(anyString(), anyString()); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); } @Test @@ -433,12 +433,12 @@ public void handleFailedUpdateSetupDialogReinstallActionWithException() throws U verifyStatic(); BrowserUtils.appendUri(anyString(), anyString()); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); } @Test public void handleFailedUpdateSetupDialogIgnoreAction() { - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn("failed_message_from_backend"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn("failed_message_from_backend"); when(mDialog.isShowing()).thenReturn(false); when(mDialogBuilder.create()).thenReturn(mDialog); mockStatic(DistributeUtils.class); @@ -459,12 +459,12 @@ public void handleFailedUpdateSetupDialogIgnoreAction() { /* Click. */ clickListener.getValue().onClick(mDialog, DialogInterface.BUTTON_POSITIVE); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY, "some_hash"); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY, "some_hash"); } @Test public void verifyFailedUpdateSetupDialogIsAlreadyShownInSameActivity() { - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn("failed_message_from_backend"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn("failed_message_from_backend"); /* Trigger call. */ start(); @@ -486,7 +486,7 @@ public void testerAppNotInstalled() throws Exception { /* Setup mock. */ UUID requestId = UUID.randomUUID(); when(UUIDUtils.randomUUID()).thenReturn(requestId); - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenThrow(new PackageManager.NameNotFoundException()); /* Mock install id from AppCenter. */ @@ -509,7 +509,7 @@ public void testerAppNotInstalled() throws Exception { url += "&" + PARAMETER_INSTALL_ID + "=" + installId.toString(); BrowserUtils.openBrowser(url, mActivity); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); + SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); } @Test @@ -518,7 +518,7 @@ public void useBrowserUpdateSetupIfAppIsTesterApp() throws Exception { /* Setup mock. */ UUID requestId = UUID.randomUUID(); when(UUIDUtils.randomUUID()).thenReturn(requestId); - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenReturn(mock(PackageInfo.class)); when(mContext.getPackageName()).thenReturn(DistributeUtils.TESTER_APP_PACKAGE_NAME); @@ -540,7 +540,7 @@ public void testerAppUpdateSetupFailed() throws Exception { /* Setup mock. */ UUID requestId = UUID.randomUUID(); when(UUIDUtils.randomUUID()).thenReturn(requestId); - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); String url = "ms-actesterapp://update-setup"; url += "?" + PARAMETER_RELEASE_HASH + "=" + TEST_HASH; url += "&" + PARAMETER_REDIRECT_ID + "=" + mContext.getPackageName(); @@ -562,7 +562,7 @@ public void testerAppUpdateSetupFailed() throws Exception { verify(mActivity).startActivity(intent); /* Start and resume: open browser. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn("true"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn("true"); Distribute.getInstance().onActivityPaused(mActivity); Distribute.getInstance().onActivityResumed(mActivity); url = DistributeConstants.DEFAULT_INSTALL_URL; @@ -578,7 +578,7 @@ public void testerAppUpdateSetupFailed() throws Exception { BrowserUtils.openBrowser(url, mActivity); /* Start and resume: open browser. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn(null); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn(null); Distribute.getInstance().onActivityPaused(mActivity); Distribute.getInstance().onActivityResumed(mActivity); verifyStatic(); @@ -593,8 +593,8 @@ public void happyPathUsingTesterAppUpdateSetup() throws Exception { whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); UUID requestId = UUID.randomUUID(); when(UUIDUtils.randomUUID()).thenReturn(requestId); - when(PreferencesStorage.getString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn(null); - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn(null); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); String url = "ms-actesterapp://update-setup"; url += "?" + PARAMETER_RELEASE_HASH + "=" + TEST_HASH; url += "&" + PARAMETER_REDIRECT_ID + "=" + mContext.getPackageName(); @@ -610,22 +610,22 @@ public void happyPathUsingTesterAppUpdateSetup() throws Exception { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); verify(mActivity).startActivity(intent); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); + SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); /* Store token. */ Distribute.getInstance().storeRedirectionParameters(requestId.toString(), "g", "some token"); /* Verify behavior. */ verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_REQUEST_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); HashMap headers = new HashMap<>(); headers.put(DistributeConstants.HEADER_API_TOKEN, "some token"); verify(httpClient).callAsync(argThat(new ArgumentMatcher() { @@ -642,15 +642,15 @@ public boolean matches(Object argument) { /* Verify behavior. */ verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_REQUEST_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(httpClient).callAsync(argThat(new ArgumentMatcher() { @Override @@ -665,15 +665,15 @@ public boolean matches(Object argument) { /* Verify behavior not changed. */ verify(mActivity).startActivity(intent); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_REQUEST_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(httpClient).callAsync(argThat(new ArgumentMatcher() { @Override @@ -683,8 +683,8 @@ public boolean matches(Object argument) { }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* If process is restarted, a new call will be made. Need to mock storage for that. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("g"); - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("g"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); restartProcessAndSdk(); Distribute.getInstance().onActivityResumed(mActivity); verify(httpClient, times(2)).callAsync(argThat(new ArgumentMatcher() { @@ -704,7 +704,7 @@ public void happyPathUntilHangingCallWithToken() throws Exception { whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); UUID requestId = UUID.randomUUID(); when(UUIDUtils.randomUUID()).thenReturn(requestId); - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenThrow(new PackageManager.NameNotFoundException()); /* Mock install id from AppCenter. */ @@ -727,7 +727,7 @@ public void happyPathUntilHangingCallWithToken() throws Exception { url += "&" + PARAMETER_INSTALL_ID + "=" + installId.toString(); BrowserUtils.openBrowser(url, mActivity); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); + SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); /* If browser already opened, activity changed must not recall it. */ Distribute.getInstance().onActivityPaused(mActivity); @@ -735,22 +735,22 @@ public void happyPathUntilHangingCallWithToken() throws Exception { verifyStatic(); BrowserUtils.openBrowser(url, mActivity); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); + SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); /* Store token. */ Distribute.getInstance().storeRedirectionParameters(requestId.toString(), "g", "some token"); /* Verify behavior. */ verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_REQUEST_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDistributeInfoTracker).updateDistributionGroupId("g"); HashMap headers = new HashMap<>(); headers.put(DistributeConstants.HEADER_API_TOKEN, "some token"); @@ -768,15 +768,15 @@ public boolean matches(Object argument) { /* Verify behavior. */ verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_REQUEST_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDistributeInfoTracker).updateDistributionGroupId("g"); verify(httpClient).callAsync(argThat(new ArgumentMatcher() { @@ -793,15 +793,15 @@ public boolean matches(Object argument) { verifyStatic(); BrowserUtils.openBrowser(url, mActivity); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_REQUEST_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDistributeInfoTracker).updateDistributionGroupId("g"); verify(httpClient).callAsync(argThat(new ArgumentMatcher() { @@ -812,8 +812,8 @@ public boolean matches(Object argument) { }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* If process is restarted, a new call will be made. Need to mock storage for that. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("g"); - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("g"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); restartProcessAndSdk(); Distribute.getInstance().onActivityResumed(mActivity); verify(httpClient, times(2)).callAsync(argThat(new ArgumentMatcher() { @@ -833,7 +833,7 @@ public void happyPathUntilHangingCallWithoutToken() throws Exception { whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); UUID requestId = UUID.randomUUID(); when(UUIDUtils.randomUUID()).thenReturn(requestId); - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenThrow(new PackageManager.NameNotFoundException()); /* Mock install id from AppCenter. */ @@ -856,7 +856,7 @@ public void happyPathUntilHangingCallWithoutToken() throws Exception { url += "&" + PARAMETER_INSTALL_ID + "=" + installId.toString(); BrowserUtils.openBrowser(url, mActivity); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); + SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); /* If browser already opened, activity changed must not recall it. */ Distribute.getInstance().onActivityPaused(mActivity); @@ -864,22 +864,22 @@ public void happyPathUntilHangingCallWithoutToken() throws Exception { verifyStatic(); BrowserUtils.openBrowser(url, mActivity); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); + SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); /* Store token. */ Distribute.getInstance().storeRedirectionParameters(requestId.toString(), "g", null); /* Verify behavior. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_TOKEN); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_TOKEN); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_REQUEST_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDistributeInfoTracker).updateDistributionGroupId("g"); HashMap headers = new HashMap<>(); verify(httpClient).callAsync(argThat(new ArgumentMatcher() { @@ -896,15 +896,15 @@ public boolean matches(Object argument) { /* Verify behavior. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_TOKEN); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_TOKEN); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_REQUEST_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDistributeInfoTracker).updateDistributionGroupId("g"); verify(httpClient).callAsync(argThat(new ArgumentMatcher() { @@ -921,15 +921,15 @@ public boolean matches(Object argument) { verifyStatic(); BrowserUtils.openBrowser(url, mActivity); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_TOKEN); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_TOKEN); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_REQUEST_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDistributeInfoTracker).updateDistributionGroupId("g"); verify(httpClient).callAsync(argThat(new ArgumentMatcher() { @@ -940,7 +940,7 @@ public boolean matches(Object argument) { }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* If process is restarted, a new call will be made. Need to mock storage for that. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("g"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("g"); restartProcessAndSdk(); Distribute.getInstance().onActivityResumed(mActivity); verify(httpClient, times(2)).callAsync(argThat(new ArgumentMatcher() { @@ -962,7 +962,7 @@ public void setUrls() throws Exception { whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); UUID requestId = UUID.randomUUID(); when(UUIDUtils.randomUUID()).thenReturn(requestId); - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenThrow(new PackageManager.NameNotFoundException()); /* Mock install id from AppCenter. */ @@ -985,7 +985,7 @@ public void setUrls() throws Exception { url += "&" + PARAMETER_INSTALL_ID + "=" + installId.toString(); BrowserUtils.openBrowser(url, mActivity); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); + SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); /* Store token. */ Distribute.getInstance().storeRedirectionParameters(requestId.toString(), "g", "some token"); @@ -1022,7 +1022,7 @@ public void computeHashFailsWhenOpeningBrowser() throws Exception { verifyStatic(never()); BrowserUtils.openBrowser(anyString(), any(Activity.class)); verifyStatic(never()); - PreferencesStorage.putString(anyString(), anyString()); + SharedPreferencesManager.putString(anyString(), anyString()); } @Test @@ -1053,7 +1053,7 @@ public void disableBeforeStoreToken() throws Exception { url += "&" + PARAMETER_INSTALL_ID + "=" + installId.toString(); BrowserUtils.openBrowser(url, mActivity); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); + SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); /* Disable. */ Distribute.setEnabled(false); @@ -1064,13 +1064,13 @@ public void disableBeforeStoreToken() throws Exception { /* Verify behavior. */ verifyStatic(never()); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_REQUEST_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Since after disabling once, the request id was deleted we can enable/disable it will also ignore the request. */ Distribute.setEnabled(true); @@ -1081,14 +1081,14 @@ public void disableBeforeStoreToken() throws Exception { /* Verify behavior. */ verifyStatic(never()); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); } @Test public void disableWhileCheckingRelease() throws Exception { /* Mock we already have token. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); ServiceCall firstCall = mock(ServiceCall.class); @@ -1116,7 +1116,7 @@ public void disableWhileCheckingRelease() throws Exception { private void checkReleaseFailure(final Exception exception, VerificationMode deleteTokenVerificationMode) throws Exception { /* Mock we already have token. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); when(httpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @@ -1137,11 +1137,11 @@ public ServiceCall answer(InvocationOnMock invocation) { /* Verify on failure we complete workflow. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Check token kept or not depending on the test. */ verifyStatic(deleteTokenVerificationMode); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_TOKEN); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_TOKEN); /* After that if we resume app nothing happens. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); @@ -1193,7 +1193,7 @@ public void checkReleaseFailsWithSome404noRelease() throws Exception { public void checkReleaseFailsParsing() throws Exception { /* Mock we already have token. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); when(httpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @@ -1215,7 +1215,7 @@ public ServiceCall answer(InvocationOnMock invocation) { /* Verify on failure we complete workflow. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* After that if we resume app nothing happens. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); @@ -1227,7 +1227,7 @@ public ServiceCall answer(InvocationOnMock invocation) { public void disableBeforeCheckReleaseFails() throws Exception { /* Mock we already have token. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); final Semaphore beforeSemaphore = new Semaphore(0); @@ -1259,15 +1259,15 @@ public void run() { /* Disable before it fails. */ Distribute.setEnabled(false); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verifyStatic(never()); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_TOKEN); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_TOKEN); beforeSemaphore.release(); afterSemaphore.acquireUninterruptibly(); /* Verify complete workflow call ignored. i.e. no more call to delete the state. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* After that if we resume app nothing happens. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); @@ -1279,7 +1279,7 @@ public void run() { public void disableBeforeCheckReleaseSucceed() throws Exception { /* Mock we already have token. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); final Semaphore beforeSemaphore = new Semaphore(0); @@ -1311,15 +1311,15 @@ public void run() { /* Disable before it succeeds. */ Distribute.setEnabled(false); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verifyStatic(never()); - PreferencesStorage.remove(PREFERENCE_KEY_UPDATE_TOKEN); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_TOKEN); beforeSemaphore.release(); afterSemaphore.acquireUninterruptibly(); /* Verify complete workflow call skipped. i.e. no more call to delete the state. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* After that if we resume app nothing happens. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); @@ -1332,7 +1332,7 @@ public void run() { public void storeBetterEncryptedToken() throws Exception { /* Mock we already have token. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some encrypted token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some encrypted token"); when(mCryptoUtils.decrypt(eq("some encrypted token"), anyBoolean())).thenReturn(new CryptoUtils.DecryptedData("some token", "some better encrypted token")); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); @@ -1346,7 +1346,7 @@ public void storeBetterEncryptedToken() throws Exception { /* Verify storage was updated with new encrypted value. */ verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some better encrypted token"); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some better encrypted token"); } @Test @@ -1363,11 +1363,11 @@ public void useMobileCenterFailOverForDecryptAndReleaseDetailsPrivate() throws E Distribute.getInstance().onActivityResumed(mActivity); verify(httpClient).callAsync(anyString(), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); - /* Verify the new strings were put into PreferencesStorage */ + /* Verify the new strings were put into SharedPreferencesManager */ verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token MC"); + SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token MC"); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "some group MC"); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "some group MC"); verify(mDistributeInfoTracker).updateDistributionGroupId("some group MC"); } @@ -1386,15 +1386,15 @@ public void useMobileCenterFailOverForDecryptAndReleaseDetailsPublic() throws Ex /* Verify the group was saved into new storage. */ verifyStatic(never()); - PreferencesStorage.putString(eq(PREFERENCE_KEY_UPDATE_TOKEN), anyString()); + SharedPreferencesManager.putString(eq(PREFERENCE_KEY_UPDATE_TOKEN), anyString()); verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "some group MC"); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "some group MC"); verify(mDistributeInfoTracker).updateDistributionGroupId("some group MC"); } @Test public void willNotReportReleaseInstallForPrivateGroupWithoutStoredReleaseHash() throws Exception { - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some encrypted token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some encrypted token"); when(mCryptoUtils.decrypt(eq("some encrypted token"), anyBoolean())).thenReturn(new CryptoUtils.DecryptedData("some token", "some better encrypted token")); /* Mock httpClient. */ @@ -1420,7 +1420,7 @@ public boolean matches(Object argument) { public void willNotReportReleaseInstallForPrivateGroupWhenReleaseHashesDontMatch() throws Exception { when(mMobileCenterPreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN, null)).thenReturn("some token MC"); when(mMobileCenterPreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, null)).thenReturn("fake-distribution-id"); - when(PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-release-hash"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-release-hash"); /* Mock httpClient. */ HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); @@ -1446,8 +1446,8 @@ public void reportReleaseInstallForPrivateGroupWhenReleaseHashesMatch() throws E final String distributionGroupId = "fake-distribution-id"; when(mMobileCenterPreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN, null)).thenReturn("some token MC"); when(mMobileCenterPreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, null)).thenReturn(distributionGroupId); - when(PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn(TEST_HASH); - when(PreferencesStorage.getInt(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID)).thenReturn(4); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn(TEST_HASH); + when(SharedPreferencesManager.getInt(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID)).thenReturn(4); /* Mock install id from AppCenter. */ final UUID installId = UUID.randomUUID(); @@ -1477,8 +1477,8 @@ public boolean matches(Object argument) { public void reportReleaseInstallForPublicGroupWhenReleaseHashesMatch() throws Exception { when(mMobileCenterPreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN, null)).thenReturn(null); when(mMobileCenterPreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, null)).thenReturn("fake-distribution-id"); - when(PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn(TEST_HASH); - when(PreferencesStorage.getInt(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID)).thenReturn(4); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn(TEST_HASH); + when(SharedPreferencesManager.getInt(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID)).thenReturn(4); /* Mock install id from AppCenter. */ final UUID installId = UUID.randomUUID(); @@ -1513,7 +1513,7 @@ public void enqueueDistributionStartSessionLogAfterEnablingUpdates() { SessionContext.SessionInfo sessionInfo = mock(SessionContext.SessionInfo.class); when(sessionContext.getSessionAt(anyLong())).thenReturn(sessionInfo); when(sessionInfo.getSessionId()).thenReturn(UUID.randomUUID()); - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); /* Enable in-app updates. */ start(); @@ -1533,7 +1533,7 @@ public void dontEnqueueDistributionStartSessionLogIfLastSessionIdIsNull() { SessionContext.SessionInfo sessionInfo = mock(SessionContext.SessionInfo.class); when(sessionContext.getSessionAt(anyLong())).thenReturn(sessionInfo); when(sessionInfo.getSessionId()).thenReturn(null); - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); /* Enable in-app updates. */ start(); @@ -1551,7 +1551,7 @@ public void dontEnqueueDistributionStartSessionLogIfNoSessionsWereLoggedBefore() SessionContext sessionContext = mock(SessionContext.class); when(SessionContext.getInstance()).thenReturn(sessionContext); when(sessionContext.getSessionAt(anyLong())).thenReturn(null); - when(PreferencesStorage.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); /* Enable in-app updates. */ start(); @@ -1568,18 +1568,18 @@ public void shouldChangeDistributionGroupIdIfStoredIdDoesntMatchDownloadedId() { String downloadedDistributionGroupId = "fake-downloaded-id"; mockStatic(DistributeUtils.class); when(DistributeUtils.computeReleaseHash(any(PackageInfo.class))).thenReturn("fake-hash"); - when(PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-hash"); - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("fake-id"); - when(PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID)).thenReturn(downloadedDistributionGroupId); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-hash"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("fake-id"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID)).thenReturn(downloadedDistributionGroupId); /* Trigger call. */ start(); /* Verify group ID. */ verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, downloadedDistributionGroupId); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, downloadedDistributionGroupId); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); } @Test @@ -1589,18 +1589,18 @@ public void shouldChangeDistributionGroupIdIfStoredIdIsNull() { String downloadedDistributionGroupId = "fake-downloaded-id"; mockStatic(DistributeUtils.class); when(DistributeUtils.computeReleaseHash(any(PackageInfo.class))).thenReturn("fake-hash"); - when(PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-hash"); - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn(null); - when(PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID)).thenReturn(downloadedDistributionGroupId); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-hash"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn(null); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID)).thenReturn(downloadedDistributionGroupId); /* Trigger call. */ start(); /* Verify group ID. */ verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, downloadedDistributionGroupId); + SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, downloadedDistributionGroupId); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); } @Test @@ -1609,18 +1609,18 @@ public void shouldNotChangeDistributionGroupIdIfStoredIdMatchDownloadedId() { /* Mock release details. */ mockStatic(DistributeUtils.class); when(DistributeUtils.computeReleaseHash(any(PackageInfo.class))).thenReturn("fake-hash"); - when(PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-hash"); - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("fake-id"); - when(PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID)).thenReturn("fake-id"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-hash"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("fake-id"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID)).thenReturn("fake-id"); /* Trigger call. */ start(); /* Verify group ID. */ verifyStatic(never()); - PreferencesStorage.putString(eq(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID), anyString()); + SharedPreferencesManager.putString(eq(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID), anyString()); verifyStatic(never()); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); } @Test @@ -1629,18 +1629,18 @@ public void shouldNotChangeDistributionGroupIdIfAppWasntUpdated() { /* Mock release details. */ mockStatic(DistributeUtils.class); when(DistributeUtils.computeReleaseHash(any(PackageInfo.class))).thenReturn("fake-hash"); - when(PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn(null); - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("fake-id"); - when(PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID)).thenReturn(null); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn(null); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("fake-id"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID)).thenReturn(null); /* Trigger call. */ start(); /* Verify group ID. */ verifyStatic(never()); - PreferencesStorage.putString(eq(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID), anyString()); + SharedPreferencesManager.putString(eq(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID), anyString()); verifyStatic(never()); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); } @Test @@ -1649,12 +1649,12 @@ public void shouldNotChangeDistributionGroupIdIfCurrentPackageInfoIsNull() throw /* Mock release details. */ mockStatic(DistributeUtils.class); when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn(null); - when(PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-hash"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-hash"); /* Verify group ID. */ verifyStatic(never()); - PreferencesStorage.putString(eq(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID), anyString()); + SharedPreferencesManager.putString(eq(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID), anyString()); verifyStatic(never()); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeDownloadTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeDownloadTest.java index 6dda999712..2421cd405a 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeDownloadTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeDownloadTest.java @@ -19,6 +19,7 @@ import com.microsoft.appcenter.utils.AppNameHelper; import com.microsoft.appcenter.utils.AsyncTaskUtils; import com.microsoft.appcenter.utils.async.AppCenterFuture; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.After; import org.junit.Test; @@ -45,7 +46,6 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_POSTPONE_TIME; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_RELEASE_DETAILS; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; -import static com.microsoft.appcenter.utils.storage.StorageHelper.PreferencesStorage; import static org.junit.Assert.assertNotNull; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; @@ -77,8 +77,8 @@ public void moreRecentWithIncompatibleMinApiLevel() throws Exception { /* Mock we already have redirection parameters. */ TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.JELLY_BEAN_MR2); - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); when(httpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @@ -104,9 +104,9 @@ public ServiceCall answer(InvocationOnMock invocation) { /* Verify on incompatible version we complete workflow. */ verifyStatic(never()); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDialogBuilder, never()).create(); verify(mDialog, never()).show(); @@ -120,7 +120,7 @@ public ServiceCall answer(InvocationOnMock invocation) { public void olderVersionCode() throws Exception { /* Mock we already have public group, no token. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); when(httpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @@ -146,7 +146,7 @@ public ServiceCall answer(InvocationOnMock invocation) { /* Verify on failure we complete workflow. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDialogBuilder, never()).create(); verify(mDialog, never()).show(); @@ -164,7 +164,7 @@ public ServiceCall answer(InvocationOnMock invocation) { public void sameVersionCodeSameHash() throws Exception { /* Mock we already have token and no group. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); when(httpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @@ -190,7 +190,7 @@ public ServiceCall answer(InvocationOnMock invocation) { /* Verify on failure we complete workflow. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDialogBuilder, never()).create(); verify(mDialog, never()).show(); @@ -204,7 +204,7 @@ public ServiceCall answer(InvocationOnMock invocation) { public void moreRecentVersionCode() throws Exception { /* Mock we already have public group, no token. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); when(httpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @@ -263,8 +263,8 @@ public ServiceCall answer(InvocationOnMock invocation) { public void sameVersionDifferentHashWithHardcodedAppName() throws Exception { /* Mock we already have redirection parameters. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); when(httpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @@ -304,8 +304,8 @@ public ServiceCall answer(InvocationOnMock invocation) { public void dialogActivityStateChanges() throws Exception { /* Mock we already have redirection parameters. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); final Semaphore beforeSemaphore = new Semaphore(0); @@ -377,8 +377,8 @@ public void run() { public void postponeDialog() throws Exception { /* Mock we already have redirection parameters. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); when(httpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @@ -411,21 +411,21 @@ public ServiceCall answer(InvocationOnMock invocation) { @Override public Void answer(InvocationOnMock invocation) { - when(PreferencesStorage.getLong(invocation.getArguments()[0].toString(), 0)).thenReturn((Long) invocation.getArguments()[1]); + when(SharedPreferencesManager.getLong(invocation.getArguments()[0].toString(), 0)).thenReturn((Long) invocation.getArguments()[1]); return null; } - }).when(PreferencesStorage.class); - PreferencesStorage.putLong(eq(PREFERENCE_KEY_POSTPONE_TIME), anyLong()); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_POSTPONE_TIME), anyLong()); clickListener.getValue().onClick(mDialog, DialogInterface.BUTTON_NEGATIVE); when(mDialog.isShowing()).thenReturn(false); /* Verify. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_RELEASE_DETAILS); + SharedPreferencesManager.remove(PREFERENCE_KEY_RELEASE_DETAILS); verifyStatic(); - PreferencesStorage.putLong(eq(PREFERENCE_KEY_POSTPONE_TIME), eq(now)); + SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_POSTPONE_TIME), eq(now)); /* Verify no more calls, e.g. happened only once. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); @@ -465,7 +465,7 @@ public Void answer(InvocationOnMock invocation) { /* Set back in time to make SDK clean state and force update. */ verifyStatic(never()); - PreferencesStorage.remove(PREFERENCE_KEY_POSTPONE_TIME); + SharedPreferencesManager.remove(PREFERENCE_KEY_POSTPONE_TIME); when(releaseDetails.isMandatoryUpdate()).thenReturn(false); now = 1; when(System.currentTimeMillis()).thenReturn(now); @@ -473,15 +473,15 @@ public Void answer(InvocationOnMock invocation) { Distribute.getInstance().onActivityResumed(mock(Activity.class)); verify(mDialog, times(4)).show(); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_POSTPONE_TIME); + SharedPreferencesManager.remove(PREFERENCE_KEY_POSTPONE_TIME); } @Test public void disableBeforePostponeDialog() throws Exception { /* Mock we already have redirection parameters. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); when(httpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @@ -509,7 +509,7 @@ public ServiceCall answer(InvocationOnMock invocation) { /* Disable. */ Distribute.setEnabled(false); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Postpone it. */ clickListener.getValue().onClick(mDialog, DialogInterface.BUTTON_NEGATIVE); @@ -524,9 +524,9 @@ public ServiceCall answer(InvocationOnMock invocation) { verify(mDialog).show(); verify(httpClient).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verifyStatic(never()); - PreferencesStorage.putLong(eq(PREFERENCE_KEY_POSTPONE_TIME), anyLong()); + SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_POSTPONE_TIME), anyLong()); } @Test @@ -534,8 +534,8 @@ public ServiceCall answer(InvocationOnMock invocation) { public void disableBeforeDownload() throws Exception { /* Mock we already have redirection parameters. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); when(httpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @@ -565,7 +565,7 @@ public ServiceCall answer(InvocationOnMock invocation) { /* Disable. */ Distribute.setEnabled(false); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Click on download. */ clickListener.getValue().onClick(mDialog, DialogInterface.BUTTON_POSITIVE); @@ -580,7 +580,7 @@ public ServiceCall answer(InvocationOnMock invocation) { verify(mDialog).show(); verify(httpClient).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no download scheduled. */ verifyStatic(never()); @@ -594,8 +594,8 @@ public void mandatoryUpdateDialogAndCacheTests() throws Exception { mockSomeStorage(); /* Mock we already have redirection parameters. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); final AtomicReference serviceCallbackRef = new AtomicReference<>(); @@ -626,9 +626,9 @@ public ServiceCall answer(InvocationOnMock invocation) { /* Verify release notes persisted. */ verifyStatic(); - PreferencesStorage.putString(PREFERENCE_KEY_RELEASE_DETAILS, "mock"); + SharedPreferencesManager.putString(PREFERENCE_KEY_RELEASE_DETAILS, "mock"); verifyStatic(); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); /* Verify dialog. */ verify(mDialogBuilder, never()).setNegativeButton(anyInt(), any(DialogInterface.OnClickListener.class)); @@ -649,7 +649,7 @@ public ServiceCall answer(InvocationOnMock invocation) { /* Check we didn't change state, e.g. happened only once. */ verifyStatic(); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); /* Restart and this time we will detect a more recent optional release. */ restartProcessAndSdk(); @@ -670,7 +670,7 @@ public ServiceCall answer(InvocationOnMock invocation) { /* Check state updated again when we detect it. */ verifyStatic(times(2)); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); /* Restart SDK, even offline, should show optional dialog. */ when(mNetworkStateHelper.isNetworkConnected()).thenReturn(false); @@ -712,8 +712,8 @@ public void cancelGetReleaseCallIfDownloadingCachedDialogAfterRestart() throws E mockSomeStorage(); /* Mock we already have redirection parameters. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); final AtomicReference serviceCallbackRef = new AtomicReference<>(); @@ -763,8 +763,8 @@ public ServiceCall answer(InvocationOnMock invocation) { public void releaseNotes() throws Exception { /* Mock we already have redirection parameters. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); when(httpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @@ -850,7 +850,7 @@ public ServiceCall answer(InvocationOnMock invocation) { public void shouldRemoveReleaseHashStorageIfReportedSuccessfully() throws Exception { /* Mock release hash storage. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-hash"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-hash"); mockStatic(DistributeUtils.class); when(DistributeUtils.computeReleaseHash(any(PackageInfo.class))).thenReturn("fake-hash"); @@ -860,7 +860,7 @@ public void shouldRemoveReleaseHashStorageIfReportedSuccessfully() throws Except when(AppCenter.getInstallId()).thenReturn(mAppCenterFuture); /* Mock we already have token and no group. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); when(httpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @@ -882,16 +882,16 @@ public ServiceCall answer(InvocationOnMock invocation) { Distribute.getInstance().onActivityResumed(mock(Activity.class)); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID); } @Test public void shouldNotRemoveReleaseHashStorageIfHashesDontMatch() throws Exception { /* Mock release hash storage. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-hash"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-hash"); mockStatic(DistributeUtils.class); when(DistributeUtils.computeReleaseHash(any(PackageInfo.class))).thenReturn("fake-old-hash"); @@ -901,7 +901,7 @@ public void shouldNotRemoveReleaseHashStorageIfHashesDontMatch() throws Exceptio when(AppCenter.getInstallId()).thenReturn(mAppCenterFuture); /* Mock we already have token and no group. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); when(httpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @@ -923,9 +923,9 @@ public ServiceCall answer(InvocationOnMock invocation) { Distribute.getInstance().onActivityResumed(mock(Activity.class)); verifyStatic(never()); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); verifyStatic(never()); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID); } /** @@ -936,38 +936,38 @@ private void mockSomeStorage() { @Override public Void answer(InvocationOnMock invocation) { - PowerMockito.when(PreferencesStorage.getInt(invocation.getArguments()[0].toString(), DOWNLOAD_STATE_COMPLETED)).thenReturn((Integer) invocation.getArguments()[1]); + PowerMockito.when(SharedPreferencesManager.getInt(invocation.getArguments()[0].toString(), DOWNLOAD_STATE_COMPLETED)).thenReturn((Integer) invocation.getArguments()[1]); return null; } - }).when(PreferencesStorage.class); - PreferencesStorage.putInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), anyInt()); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), anyInt()); doAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) { - PowerMockito.when(PreferencesStorage.getInt(invocation.getArguments()[0].toString(), DOWNLOAD_STATE_COMPLETED)).thenReturn(DOWNLOAD_STATE_COMPLETED); + PowerMockito.when(SharedPreferencesManager.getInt(invocation.getArguments()[0].toString(), DOWNLOAD_STATE_COMPLETED)).thenReturn(DOWNLOAD_STATE_COMPLETED); return null; } - }).when(PreferencesStorage.class); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); doAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) { - PowerMockito.when(PreferencesStorage.getString(invocation.getArguments()[0].toString())).thenReturn(invocation.getArguments()[1].toString()); + PowerMockito.when(SharedPreferencesManager.getString(invocation.getArguments()[0].toString())).thenReturn(invocation.getArguments()[1].toString()); return null; } - }).when(PreferencesStorage.class); - PreferencesStorage.putString(eq(PREFERENCE_KEY_RELEASE_DETAILS), anyString()); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putString(eq(PREFERENCE_KEY_RELEASE_DETAILS), anyString()); doAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) { - PowerMockito.when(PreferencesStorage.getString(invocation.getArguments()[0].toString())).thenReturn(null); + PowerMockito.when(SharedPreferencesManager.getString(invocation.getArguments()[0].toString())).thenReturn(null); return null; } - }).when(PreferencesStorage.class); - PreferencesStorage.remove(PREFERENCE_KEY_RELEASE_DETAILS); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.remove(PREFERENCE_KEY_RELEASE_DETAILS); } @After diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java index f17e0d04a2..49622cbeb7 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java @@ -9,7 +9,7 @@ import com.microsoft.appcenter.http.ServiceCall; import com.microsoft.appcenter.http.ServiceCallback; import com.microsoft.appcenter.utils.AppCenterLog; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; @@ -249,7 +249,7 @@ public void handleUserUpdateActionPostponeForOptionalUpdate() throws Exception { /* Verify POSTPONE has been processed. */ verify(distribute).completeWorkflow(); - StorageHelper.PreferencesStorage.putLong(eq(PREFERENCE_KEY_POSTPONE_TIME), anyLong()); + SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_POSTPONE_TIME), anyLong()); } @Test @@ -308,7 +308,7 @@ public void handleUserUpdateActionPostponeForMandatoryUpdate() throws Exception /* Verify POSTPONE has NOT been processed. */ verify(distribute, never()).completeWorkflow(); verifyStatic(never()); - StorageHelper.PreferencesStorage.putLong(eq(PREFERENCE_KEY_POSTPONE_TIME), anyLong()); + SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_POSTPONE_TIME), anyLong()); } @Test @@ -425,7 +425,7 @@ public ServiceCall answer(InvocationOnMock invocation) { when(ReleaseDetails.parse(anyString())).thenReturn(details); /* Mock update token. */ - when(StorageHelper.PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); return details; } @@ -440,8 +440,8 @@ public Void answer(InvocationOnMock invocation) { currentDownloadState[0] = (Integer) invocation.getArguments()[1]; return null; } - }).when(StorageHelper.PreferencesStorage.class); - StorageHelper.PreferencesStorage.putInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), anyInt()); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), anyInt()); doAnswer(new Answer() { @Override @@ -449,9 +449,9 @@ public Void answer(InvocationOnMock invocation) { currentDownloadState[0] = DOWNLOAD_STATE_COMPLETED; return null; } - }).when(StorageHelper.PreferencesStorage.class); - StorageHelper.PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); - when(StorageHelper.PreferencesStorage.getInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), anyInt())) + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + when(SharedPreferencesManager.getInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), anyInt())) .thenAnswer(new Answer() { @Override diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDownloadTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDownloadTest.java index 1cad2df084..30677f1459 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDownloadTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDownloadTest.java @@ -18,7 +18,7 @@ import com.microsoft.appcenter.test.TestUtils; import com.microsoft.appcenter.utils.AsyncTaskUtils; -import com.microsoft.appcenter.utils.storage.StorageHelper.PreferencesStorage; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.Before; import org.junit.Test; @@ -81,9 +81,9 @@ public void startDownloadThenDisable() throws Exception { verify(mDownloadManager).enqueue(mDownloadRequest); verifyNew(DownloadManager.Request.class).withArguments(mDownloadUrl); verifyStatic(); - PreferencesStorage.putLong(PREFERENCE_KEY_DOWNLOAD_ID, DOWNLOAD_ID); + SharedPreferencesManager.putLong(PREFERENCE_KEY_DOWNLOAD_ID, DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_ENQUEUED); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_ENQUEUED); /* Pause/resume should do nothing excepting mentioning progress. */ verify(mDialog).show(); @@ -94,9 +94,9 @@ public void startDownloadThenDisable() throws Exception { /* Cancel download by disabling. */ Distribute.setEnabled(false); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDownloadTask.get()).cancel(true); verify(mDownloadManager).remove(DOWNLOAD_ID); verify(mNotificationManager, never()).notify(anyInt(), any(Notification.class)); @@ -111,9 +111,9 @@ public void disableWhileStartingDownload() throws Exception { /* Verify. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDownloadTask.get()).cancel(true); verify(mDownloadManager).enqueue(mDownloadRequest); verifyNew(DownloadManager.Request.class).withArguments(mDownloadUrl); @@ -121,9 +121,9 @@ public void disableWhileStartingDownload() throws Exception { /* And that we didn't persist the state. */ verifyStatic(never()); - PreferencesStorage.putLong(PREFERENCE_KEY_DOWNLOAD_ID, DOWNLOAD_ID); + SharedPreferencesManager.putLong(PREFERENCE_KEY_DOWNLOAD_ID, DOWNLOAD_ID); verifyStatic(never()); - PreferencesStorage.putString(PREFERENCE_KEY_DOWNLOAD_STATE, ""); + SharedPreferencesManager.putString(PREFERENCE_KEY_DOWNLOAD_STATE, ""); verifyZeroInteractions(mNotificationManager); } @@ -139,7 +139,7 @@ public void disableWhileProcessingCompletion() { /* Disable before completion. */ Distribute.setEnabled(false); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); waitCheckDownloadTask(); /* Verify cancellation. */ @@ -149,7 +149,7 @@ public void disableWhileProcessingCompletion() { /* Check cleaned state only once, the completeWorkflow on failed download has to be ignored. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); } @Test @@ -166,7 +166,7 @@ public void failDownloadRestartNoLauncher() { /* Check failure processing. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Nothing should happen if just changing activities. */ Activity activity = mock(Activity.class); @@ -204,7 +204,7 @@ public void downloadCursorNull() { /* Check we completed workflow without starting activity because installer not found. */ verify(mContext, never()).startActivity(any(Intent.class)); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); } @Test @@ -225,7 +225,7 @@ public void downloadCursorEmpty() { verify(cursor).close(); verify(mContext, never()).startActivity(any(Intent.class)); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); } @Test @@ -246,7 +246,7 @@ public void successDownloadInstallerNotFoundEvenWithLocalFile() throws Exception verify(cursor).close(); verify(mContext, never()).startActivity(any(Intent.class)); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); } @Test @@ -268,7 +268,7 @@ public void successDownloadInstallerNotFoundAfterNougat() throws Exception { verify(cursor).close(); verify(mContext, never()).startActivity(any(Intent.class)); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); } @Test @@ -283,7 +283,7 @@ public void disableDuringDownload() { /* We receive intent from download manager when we remove download. */ verify(mDownloadManager).remove(DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); completeDownload(); /* Simulate task. */ @@ -295,15 +295,15 @@ public void disableDuringDownload() { /* Verify state deleted only at disable time. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no release hash+id were saved. */ verifyStatic(never()); - PreferencesStorage.putString(eq(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH), anyString()); + SharedPreferencesManager.putString(eq(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH), anyString()); verifyStatic(never()); - PreferencesStorage.putInt(eq(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID), anyInt()); + SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID), anyInt()); verifyStatic(never()); - PreferencesStorage.putString(eq(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID), anyString()); + SharedPreferencesManager.putString(eq(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID), anyString()); /* Verify enabling triggers update dialog again. */ verify(mDialog).show(); @@ -328,15 +328,15 @@ public void successInForeground() throws Exception { /* Verify start activity and complete workflow. */ verify(mContext).startActivity(installIntent); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify release hash+id were saved. */ verifyStatic(); - PreferencesStorage.putString(eq(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH), anyString()); + SharedPreferencesManager.putString(eq(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH), anyString()); verifyStatic(); - PreferencesStorage.putInt(eq(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID), anyInt()); + SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID), anyInt()); verifyStatic(); - PreferencesStorage.putString(eq(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID), anyString()); + SharedPreferencesManager.putString(eq(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID), anyString()); verifyNoMoreInteractions(mNotificationManager); verify(cursor).close(); } @@ -394,15 +394,15 @@ public void longFailingDownloadForOptionalDownload() { /* Verify we complete workflow on failure. */ verify(mContext, never()).startActivity(any(Intent.class)); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no release hash+id were saved. */ verifyStatic(never()); - PreferencesStorage.putString(eq(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH), anyString()); + SharedPreferencesManager.putString(eq(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH), anyString()); verifyStatic(never()); - PreferencesStorage.putInt(eq(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID), anyInt()); + SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID), anyInt()); verifyStatic(never()); - PreferencesStorage.putString(eq(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID), anyString()); + SharedPreferencesManager.putString(eq(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID), anyString()); verifyZeroInteractions(mNotificationManager); } @@ -428,11 +428,11 @@ public void disabledWhileCheckingDownloadOnRestart() { final Semaphore afterDisabledSemaphore = new Semaphore(0); /* Call get to execute last when so that we can override the answer for next calls. */ - final long downloadId = PreferencesStorage.getLong(PREFERENCE_KEY_DOWNLOAD_ID, INVALID_DOWNLOAD_IDENTIFIER); + final long downloadId = SharedPreferencesManager.getLong(PREFERENCE_KEY_DOWNLOAD_ID, INVALID_DOWNLOAD_IDENTIFIER); /* Overwrite next answer. */ final Thread testThread = Thread.currentThread(); - when(PreferencesStorage.getLong(PREFERENCE_KEY_DOWNLOAD_ID, INVALID_DOWNLOAD_IDENTIFIER)).then(new Answer() { + when(SharedPreferencesManager.getLong(PREFERENCE_KEY_DOWNLOAD_ID, INVALID_DOWNLOAD_IDENTIFIER)).then(new Answer() { @Override public Long answer(InvocationOnMock invocation) { @@ -474,11 +474,11 @@ public void disabledBeforeNotifying() throws Exception { final Semaphore afterDisabledSemaphore = new Semaphore(0); /* Call get to execute last when so that we can override the answer for next calls. */ - final long downloadId = PreferencesStorage.getLong(PREFERENCE_KEY_DOWNLOAD_ID, INVALID_DOWNLOAD_IDENTIFIER); + final long downloadId = SharedPreferencesManager.getLong(PREFERENCE_KEY_DOWNLOAD_ID, INVALID_DOWNLOAD_IDENTIFIER); /* Overwrite next answer. */ final Thread testThread = Thread.currentThread(); - when(PreferencesStorage.getLong(PREFERENCE_KEY_DOWNLOAD_ID, INVALID_DOWNLOAD_IDENTIFIER)).then(new Answer() { + when(SharedPreferencesManager.getLong(PREFERENCE_KEY_DOWNLOAD_ID, INVALID_DOWNLOAD_IDENTIFIER)).then(new Answer() { @Override public Long answer(InvocationOnMock invocation) { @@ -505,7 +505,7 @@ public Long answer(InvocationOnMock invocation) { /* Disable now. */ Distribute.setEnabled(false); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Release task. */ afterDisabledSemaphore.release(); @@ -517,7 +517,7 @@ public Long answer(InvocationOnMock invocation) { verify(mContext, never()).startActivity(any(Intent.class)); verifyZeroInteractions(mNotificationManager); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); } @Test @@ -554,7 +554,7 @@ public Void answer(InvocationOnMock invocation) { /* Verify start activity and complete workflow skipped, e.g. clean behavior happened only once. */ verify(mContext).startActivity(installIntent); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verifyZeroInteractions(mNotificationManager); verify(cursor).close(); } @@ -595,7 +595,7 @@ public void notifyThenRestartAppTwice() throws Exception { /* Verify notification. */ verify(mContext, never()).startActivity(installIntent); verifyStatic(); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_NOTIFIED); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_NOTIFIED); verify(notificationBuilder).build(); verify(mNotificationManager).notify(eq(DistributeUtils.getNotificationId()), any(Notification.class)); verifyNoMoreInteractions(mNotificationManager); @@ -619,11 +619,11 @@ public void notifyThenRestartAppTwice() throws Exception { verifyStatic(); /* Verify workflow completed. */ - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify however downloaded file was kept. */ verifyStatic(never()); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); verify(mDownloadManager, never()).remove(DOWNLOAD_ID); /* Verify second download (restart app again) cleans first one. */ @@ -636,7 +636,7 @@ public void notifyThenRestartAppTwice() throws Exception { /* Verify new download id in storage. */ verifyStatic(); - PreferencesStorage.putLong(PREFERENCE_KEY_DOWNLOAD_ID, DOWNLOAD_ID + 1); + SharedPreferencesManager.putLong(PREFERENCE_KEY_DOWNLOAD_ID, DOWNLOAD_ID + 1); /* Verify previous download removed. */ verify(mDownloadManager).remove(DOWNLOAD_ID); @@ -676,7 +676,7 @@ public void notifyThenRestartThenInstallerFails() throws Exception { /* Verify notification. */ verify(mContext, never()).startActivity(installIntent); verifyStatic(); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_NOTIFIED); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_NOTIFIED); verify(mNotificationManager).notify(eq(DistributeUtils.getNotificationId()), any(Notification.class)); verifyNoMoreInteractions(mNotificationManager); verify(cursor).getString(2); @@ -694,9 +694,9 @@ public void notifyThenRestartThenInstallerFails() throws Exception { verify(mContext).startActivity(installIntent); verify(mNotificationManager).cancel(DistributeUtils.getNotificationId()); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verifyStatic(never()); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_ID); } @Test @@ -741,16 +741,16 @@ public void doNotShowInstallUiIfUpgradedAfterNotification() throws Exception { @Override public Object answer(InvocationOnMock invocation) { - when(PreferencesStorage.getLong(PREFERENCE_KEY_DOWNLOAD_TIME)).thenReturn((Long) invocation.getArguments()[1]); + when(SharedPreferencesManager.getLong(PREFERENCE_KEY_DOWNLOAD_TIME)).thenReturn((Long) invocation.getArguments()[1]); return null; } - }).when(PreferencesStorage.class); - PreferencesStorage.putLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME), anyLong()); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME), anyLong()); /* Simulate async task. */ waitDownloadTask(); verifyStatic(); - PreferencesStorage.putLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME), anyLong()); + SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME), anyLong()); /* Mock download completion to notify. */ mockSuccessCursor(); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeMandatoryDownloadTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeMandatoryDownloadTest.java index b7e7163f1e..9e27096bba 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeMandatoryDownloadTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeMandatoryDownloadTest.java @@ -10,7 +10,7 @@ import android.support.annotation.NonNull; import com.microsoft.appcenter.utils.HandlerUtils; -import com.microsoft.appcenter.utils.storage.StorageHelper.PreferencesStorage; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.json.JSONException; import org.junit.Before; @@ -192,7 +192,7 @@ public Boolean answer(InvocationOnMock invocation) { waitCheckDownloadTask(); verify(mContext).startActivity(installIntent); verifyStatic(); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); verifyNoMoreInteractions(mNotificationManager); /* Start activity paused the app. */ @@ -268,7 +268,7 @@ public void disabledBeforeClickOnDialogInstall() throws Exception { /* Verify disabled. */ verify(mDownloadManager).remove(DOWNLOAD_ID); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); } @Test @@ -324,9 +324,9 @@ public Void answer(InvocationOnMock invocation) { /* Verify start activity and complete workflow skipped, e.g. clean behavior happened only once. */ verify(mContext).startActivity(installIntent); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verifyStatic(never()); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); verifyZeroInteractions(mNotificationManager); /* Check semaphores. */ @@ -352,7 +352,7 @@ public void jsonMissingWhenRestarting() throws Exception { waitDownloadTask(); /* Make JSON disappear for some reason (should not happen for real). */ - PreferencesStorage.remove(PREFERENCE_KEY_RELEASE_DETAILS); + SharedPreferencesManager.remove(PREFERENCE_KEY_RELEASE_DETAILS); verifyWithInvalidOrMissingCachedJson(); } @@ -369,7 +369,7 @@ private void verifyWithInvalidOrMissingCachedJson() throws Exception { /* Verify JSON removed. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_RELEASE_DETAILS); + SharedPreferencesManager.remove(PREFERENCE_KEY_RELEASE_DETAILS); /* In that case the SDK will think its not mandatory but anyway this case never happens. */ mockSuccessCursor(); @@ -378,7 +378,7 @@ private void verifyWithInvalidOrMissingCachedJson() throws Exception { waitCheckDownloadTask(); verify(mContext).startActivity(intent); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); } @Test @@ -393,9 +393,9 @@ public void newOptionalUpdateWhileInstallingMandatory() throws Exception { waitCheckDownloadTask(); verify(mContext).startActivity(installIntent); verifyStatic(); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); verifyStatic(); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); verifyNoMoreInteractions(mNotificationManager); /* Restart will restore install. */ @@ -404,7 +404,7 @@ public void newOptionalUpdateWhileInstallingMandatory() throws Exception { waitCheckDownloadTask(); verify(mContext, times(2)).startActivity(installIntent); verifyStatic(times(2)); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); Distribute.getInstance().onActivityPaused(mActivity); /* Change what the next detected release will be: a more recent mandatory one. */ @@ -416,11 +416,11 @@ public void newOptionalUpdateWhileInstallingMandatory() throws Exception { when(ReleaseDetails.parse(anyString())).thenReturn(releaseDetails); Distribute.getInstance().onActivityResumed(mActivity); verifyStatic(times(2)); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); /* Check no more going to installed state. I.e. still happened twice. */ verifyStatic(times(2)); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); /* Check next restart will use new release. */ restartProcessAndSdk(); @@ -442,9 +442,9 @@ public void newMandatoryUpdateWhileInstallingMandatory() throws Exception { waitCheckDownloadTask(); verify(mContext).startActivity(installIntent); verifyStatic(); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); verifyStatic(); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); verifyNoMoreInteractions(mNotificationManager); /* Restart will restore install. */ @@ -453,7 +453,7 @@ public void newMandatoryUpdateWhileInstallingMandatory() throws Exception { waitCheckDownloadTask(); verify(mContext, times(2)).startActivity(installIntent); verifyStatic(times(2)); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); Distribute.getInstance().onActivityPaused(mActivity); /* Change what the next detected release will be: a more recent mandatory one. */ @@ -466,11 +466,11 @@ public void newMandatoryUpdateWhileInstallingMandatory() throws Exception { when(ReleaseDetails.parse(anyString())).thenReturn(releaseDetails); Distribute.getInstance().onActivityResumed(mActivity); verifyStatic(times(2)); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); /* Check no more going to installed state. I.e. still happened twice. */ verifyStatic(times(2)); - PreferencesStorage.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); /* Check next restart will use new release. */ restartProcessAndSdk(); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributePlusDownloadReceiverTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributePlusDownloadReceiverTest.java index bb23c5c5d9..f812c06ee3 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributePlusDownloadReceiverTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributePlusDownloadReceiverTest.java @@ -3,7 +3,7 @@ import android.app.Activity; import android.content.Intent; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.Test; @@ -43,7 +43,7 @@ public void resumeAfterBeforeStartButBackground() throws Exception { @Test public void resumeForegroundThenPause() throws Exception { - when(StorageHelper.PreferencesStorage.getString(eq(PREFERENCE_KEY_UPDATE_TOKEN))).thenReturn("mock"); + when(SharedPreferencesManager.getString(eq(PREFERENCE_KEY_UPDATE_TOKEN))).thenReturn("mock"); Intent clickIntent = mock(Intent.class); when(clickIntent.getAction()).thenReturn(ACTION_NOTIFICATION_CLICKED); start(); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java index 36069f3ebe..4e8aa4bb3b 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java @@ -6,7 +6,7 @@ import com.microsoft.appcenter.distribute.ingestion.models.DistributionStartSessionLog; import com.microsoft.appcenter.distribute.ingestion.models.json.DistributionStartSessionLogFactory; import com.microsoft.appcenter.ingestion.models.json.LogFactory; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.Test; @@ -38,8 +38,8 @@ public void checkFactories() { @Test public void recreateLauncherActivityBeforeFullInitialization() { - /* PreferencesStorage isn't initialized yet. */ - when(StorageHelper.PreferencesStorage.getInt(anyString(), anyInt())) + /* SharedPreferencesManager isn't initialized yet. */ + when(SharedPreferencesManager.getInt(anyString(), anyInt())) .thenThrow(new NullPointerException()); /* Our activity is launch one. */ diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java index 8bb60a7897..118ad9fb7a 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java @@ -16,6 +16,7 @@ import com.microsoft.appcenter.http.ServiceCallback; import com.microsoft.appcenter.test.TestUtils; import com.microsoft.appcenter.utils.AsyncTaskUtils; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.After; import org.junit.Assume; @@ -36,7 +37,6 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DISTRIBUTION_GROUP_ID; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; -import static com.microsoft.appcenter.utils.storage.StorageHelper.PreferencesStorage; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyMapOf; import static org.mockito.Matchers.anyString; @@ -77,8 +77,8 @@ public static Collection data() { public void setUpDialog() throws Exception { /* Mock we already have redirection parameters. */ - when(PreferencesStorage.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); - when(PreferencesStorage.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); HttpClientNetworkStateHandler httpClient = mock(HttpClientNetworkStateHandler.class); whenNew(HttpClientNetworkStateHandler.class).withAnyArguments().thenReturn(httpClient); when(httpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @@ -152,7 +152,7 @@ public void cancelDialogWithBack() { /* Verify. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no more calls, e.g. happened only once. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); @@ -175,7 +175,7 @@ public void cancelDialogWithButton() { /* Verify. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no more calls, e.g. happened only once. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); @@ -193,7 +193,7 @@ public void disableBeforeCancelWithBack() { /* Disable. */ Distribute.setEnabled(false); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Cancel. */ ArgumentCaptor clickListener = ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); @@ -203,7 +203,7 @@ public void disableBeforeCancelWithBack() { /* Verify cancel did nothing more. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no more calls, e.g. happened only once. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); @@ -221,7 +221,7 @@ public void disableBeforeCancelWithButton() { /* Disable. */ Distribute.setEnabled(false); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Cancel. */ ArgumentCaptor cancelListener = ArgumentCaptor.forClass(DialogInterface.OnCancelListener.class); @@ -231,7 +231,7 @@ public void disableBeforeCancelWithButton() { /* Verify cancel did nothing more. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no more calls, e.g. happened only once. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); @@ -367,7 +367,7 @@ public void clickSettingsFailsToNavigate() throws Exception { /* Verify failure is treated as a cancel dialog. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no more calls, e.g. happened only once. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); @@ -382,7 +382,7 @@ public void disableThenClickSettingsThenFailsToNavigate() throws Exception { /* Disable. */ Distribute.setEnabled(false); verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Click settings. */ Intent intent = mock(Intent.class); @@ -398,7 +398,7 @@ public void disableThenClickSettingsThenFailsToNavigate() throws Exception { /* Verify cleaning behavior happened only once, e.g. completeWorkflow skipped. */ verifyStatic(); - PreferencesStorage.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no more calls, e.g. happened only once. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); diff --git a/sdk/appcenter-push/src/test/java/com/microsoft/appcenter/push/PushTest.java b/sdk/appcenter-push/src/test/java/com/microsoft/appcenter/push/PushTest.java index fd68aebdf9..5b85ecdfc3 100644 --- a/sdk/appcenter-push/src/test/java/com/microsoft/appcenter/push/PushTest.java +++ b/sdk/appcenter-push/src/test/java/com/microsoft/appcenter/push/PushTest.java @@ -21,7 +21,7 @@ import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.async.AppCenterConsumer; import com.microsoft.appcenter.utils.async.AppCenterFuture; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.Before; import org.junit.Test; @@ -72,7 +72,7 @@ PushIntentUtils.class, AppCenterLog.class, AppCenter.class, - StorageHelper.PreferencesStorage.class, + SharedPreferencesManager.class, FirebaseInstanceId.class, FirebaseAnalytics.class, HandlerUtils.class @@ -124,8 +124,8 @@ public Void answer(InvocationOnMock invocation) { }).when(mAppCenterHandler).post(any(Runnable.class), any(Runnable.class)); /* First call to com.microsoft.appcenter.AppCenter.isEnabled shall return true, initial state. */ - mockStatic(StorageHelper.PreferencesStorage.class); - when(StorageHelper.PreferencesStorage.getBoolean(PUSH_ENABLED_KEY, true)).thenReturn(true); + mockStatic(SharedPreferencesManager.class); + when(SharedPreferencesManager.getBoolean(PUSH_ENABLED_KEY, true)).thenReturn(true); /* Then simulate further changes to state. */ doAnswer(new Answer() { @@ -134,11 +134,11 @@ public Object answer(InvocationOnMock invocation) { /* Whenever the new state is persisted, make further calls return the new state. */ boolean enabled = (Boolean) invocation.getArguments()[1]; - when(StorageHelper.PreferencesStorage.getBoolean(PUSH_ENABLED_KEY, true)).thenReturn(enabled); + when(SharedPreferencesManager.getBoolean(PUSH_ENABLED_KEY, true)).thenReturn(enabled); return null; } - }).when(StorageHelper.PreferencesStorage.class); - StorageHelper.PreferencesStorage.putBoolean(eq(PUSH_ENABLED_KEY), anyBoolean()); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putBoolean(eq(PUSH_ENABLED_KEY), anyBoolean()); /* Mock Firebase instance. */ mockStatic(FirebaseInstanceId.class); diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/AppCenterAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/AppCenterAndroidTest.java index cde8edf952..a9d1baf4ab 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/AppCenterAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/AppCenterAndroidTest.java @@ -14,7 +14,7 @@ import com.microsoft.appcenter.utils.async.AppCenterConsumer; import com.microsoft.appcenter.utils.async.AppCenterFuture; import com.microsoft.appcenter.utils.async.DefaultAppCenterFuture; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.After; import org.junit.Before; @@ -49,14 +49,14 @@ public void tearDown() { @Test public void getInstallId() { assertNull(AppCenter.getInstallId().get()); - StorageHelper.initialize(mApplication); - StorageHelper.PreferencesStorage.remove(PrefStorageConstants.KEY_INSTALL_ID); + SharedPreferencesManager.initialize(mApplication); + SharedPreferencesManager.remove(PrefStorageConstants.KEY_INSTALL_ID); AppCenter.start(mApplication, UUIDUtils.randomUUID().toString(), DummyService.class); UUID installId = AppCenter.getInstallId().get(); assertNotNull(installId); assertEquals(installId, AppCenter.getInstallId().get()); assertEquals(installId, DummyService.getInstallId().get()); - StorageHelper.PreferencesStorage.remove(PrefStorageConstants.KEY_INSTALL_ID); + SharedPreferencesManager.remove(PrefStorageConstants.KEY_INSTALL_ID); final UUID installId2 = AppCenter.getInstallId().get(); assertNotNull(installId2); assertNotEquals(installId2, installId); diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index 003ad5dde8..bd0762e756 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -24,6 +24,7 @@ import com.microsoft.appcenter.persistence.Persistence.PersistenceException; import com.microsoft.appcenter.utils.crypto.CryptoUtils; import com.microsoft.appcenter.utils.storage.DatabaseManager; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import com.microsoft.appcenter.utils.storage.StorageHelper; import org.json.JSONException; @@ -78,6 +79,7 @@ public static void setUpClass() { AppCenter.setLogLevel(android.util.Log.VERBOSE); sContext = InstrumentationRegistry.getTargetContext(); StorageHelper.initialize(sContext); + SharedPreferencesManager.initialize(sContext); Constants.loadFromContext(sContext); } diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java index 15eff45911..c96574ab2b 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java @@ -7,7 +7,7 @@ import android.support.test.runner.AndroidJUnit4; import com.microsoft.appcenter.ingestion.models.json.MockLog; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -28,7 +28,7 @@ public class PersistenceAndroidTest { @BeforeClass public static void setUpClass() { sContext = InstrumentationRegistry.getTargetContext(); - StorageHelper.initialize(sContext); + SharedPreferencesManager.initialize(sContext); /* Clean up database. */ sContext.deleteDatabase("test-persistence"); diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/IdHelperAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/IdHelperAndroidTest.java index 064aa2dbca..c029ff3aaa 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/IdHelperAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/IdHelperAndroidTest.java @@ -3,7 +3,7 @@ import android.support.test.InstrumentationRegistry; import android.util.Log; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.BeforeClass; import org.junit.Test; @@ -22,7 +22,7 @@ public class IdHelperAndroidTest { @BeforeClass public static void setUpClass() { - StorageHelper.initialize(InstrumentationRegistry.getTargetContext()); + SharedPreferencesManager.initialize(InstrumentationRegistry.getTargetContext()); } @@ -30,13 +30,13 @@ public static void setUpClass() { public void getInstallId() { Log.i(TAG, "Testing installId-shortcut"); UUID expected = UUIDUtils.randomUUID(); - StorageHelper.PreferencesStorage.putString(PrefStorageConstants.KEY_INSTALL_ID, expected.toString()); + SharedPreferencesManager.putString(PrefStorageConstants.KEY_INSTALL_ID, expected.toString()); UUID actual = IdHelper.getInstallId(); assertEquals(expected, actual); String wrongUUID = "1234567"; - StorageHelper.PreferencesStorage.putString(PrefStorageConstants.KEY_INSTALL_ID, expected.toString()); + SharedPreferencesManager.putString(PrefStorageConstants.KEY_INSTALL_ID, expected.toString()); actual = IdHelper.getInstallId(); assertNotEquals(wrongUUID, actual); diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java index 4e6613d37d..72d2a2235a 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java @@ -27,7 +27,6 @@ import java.util.concurrent.TimeUnit; import static com.microsoft.appcenter.utils.storage.StorageHelper.InternalStorage; -import static com.microsoft.appcenter.utils.storage.StorageHelper.PreferencesStorage; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -65,6 +64,7 @@ public class StorageHelperAndroidTest { public static void setUpClass() { sContext = InstrumentationRegistry.getTargetContext(); StorageHelper.initialize(sContext); + SharedPreferencesManager.initialize(sContext); sAndroidFilesPath = sContext.getFilesDir().getAbsolutePath() + "/test/"; /* Create a test directory. */ @@ -78,7 +78,7 @@ public static void tearDownClass() { try { for (SharedPreferencesTestData data : generateSharedPreferenceData()) { String key = data.value.getClass().getCanonicalName(); - PreferencesStorage.remove(key); + SharedPreferencesManager.remove(key); } } catch (NoSuchMethodException ignored) { /* Ignore exception. */ @@ -109,41 +109,41 @@ private static SharedPreferencesTestData[] generateSharedPreferenceData() throws testData[0] = new SharedPreferencesTestData(); testData[0].value = true; testData[0].defaultValue = false; - testData[0].getMethod1 = PreferencesStorage.class.getDeclaredMethod("getBoolean", String.class); - testData[0].getMethod2 = PreferencesStorage.class.getDeclaredMethod("getBoolean", String.class, boolean.class); - testData[0].putMethod = PreferencesStorage.class.getDeclaredMethod("putBoolean", String.class, boolean.class); + testData[0].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getBoolean", String.class); + testData[0].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getBoolean", String.class, boolean.class); + testData[0].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putBoolean", String.class, boolean.class); /* float */ testData[1] = new SharedPreferencesTestData(); testData[1].value = 111.22f; testData[1].defaultValue = 0.01f; - testData[1].getMethod1 = PreferencesStorage.class.getDeclaredMethod("getFloat", String.class); - testData[1].getMethod2 = PreferencesStorage.class.getDeclaredMethod("getFloat", String.class, float.class); - testData[1].putMethod = PreferencesStorage.class.getDeclaredMethod("putFloat", String.class, float.class); + testData[1].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getFloat", String.class); + testData[1].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getFloat", String.class, float.class); + testData[1].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putFloat", String.class, float.class); /* int */ testData[2] = new SharedPreferencesTestData(); testData[2].value = 123; testData[2].defaultValue = -1; - testData[2].getMethod1 = PreferencesStorage.class.getDeclaredMethod("getInt", String.class); - testData[2].getMethod2 = PreferencesStorage.class.getDeclaredMethod("getInt", String.class, int.class); - testData[2].putMethod = PreferencesStorage.class.getDeclaredMethod("putInt", String.class, int.class); + testData[2].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getInt", String.class); + testData[2].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getInt", String.class, int.class); + testData[2].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putInt", String.class, int.class); /* long */ testData[3] = new SharedPreferencesTestData(); testData[3].value = 123456789000L; testData[3].defaultValue = 345L; - testData[3].getMethod1 = PreferencesStorage.class.getDeclaredMethod("getLong", String.class); - testData[3].getMethod2 = PreferencesStorage.class.getDeclaredMethod("getLong", String.class, long.class); - testData[3].putMethod = PreferencesStorage.class.getDeclaredMethod("putLong", String.class, long.class); + testData[3].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getLong", String.class); + testData[3].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getLong", String.class, long.class); + testData[3].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putLong", String.class, long.class); /* String */ testData[4] = new SharedPreferencesTestData(); testData[4].value = "Hello World"; testData[4].defaultValue = "Empty"; - testData[4].getMethod1 = PreferencesStorage.class.getDeclaredMethod("getString", String.class); - testData[4].getMethod2 = PreferencesStorage.class.getDeclaredMethod("getString", String.class, String.class); - testData[4].putMethod = PreferencesStorage.class.getDeclaredMethod("putString", String.class, String.class); + testData[4].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getString", String.class); + testData[4].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getString", String.class, String.class); + testData[4].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putString", String.class, String.class); /* Set */ Set data = new HashSet<>(); @@ -156,9 +156,9 @@ private static SharedPreferencesTestData[] generateSharedPreferenceData() throws testData[5] = new SharedPreferencesTestData(); testData[5].value = data; testData[5].defaultValue = defaultSet; - testData[5].getMethod1 = PreferencesStorage.class.getDeclaredMethod("getStringSet", String.class); - testData[5].getMethod2 = PreferencesStorage.class.getDeclaredMethod("getStringSet", String.class, Set.class); - testData[5].putMethod = PreferencesStorage.class.getDeclaredMethod("putStringSet", String.class, Set.class); + testData[5].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getStringSet", String.class); + testData[5].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getStringSet", String.class, Set.class); + testData[5].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putStringSet", String.class, Set.class); return testData; } @@ -180,18 +180,18 @@ public void sharedPreferences() throws InvocationTargetException, IllegalAccessE assertEquals(data.value, actual); /* Remove key from shared preferences. */ - PreferencesStorage.remove(key); + SharedPreferencesManager.remove(key); /* Verify the value equals to default value. */ assertEquals(data.defaultValue, data.getMethod2.invoke(null, key, data.defaultValue)); } /* Test clear. */ - PreferencesStorage.putString("test", "someTest"); - PreferencesStorage.putInt("test2", 2); - PreferencesStorage.clear(); - assertNull(PreferencesStorage.getString("test")); - assertEquals(0, PreferencesStorage.getInt("test2")); + SharedPreferencesManager.putString("test", "someTest"); + SharedPreferencesManager.putInt("test2", 2); + SharedPreferencesManager.clear(); + assertNull(SharedPreferencesManager.getString("test")); + assertEquals(0, SharedPreferencesManager.getInt("test2")); } @Test diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AbstractAppCenterService.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AbstractAppCenterService.java index 1e409f706c..e9de5bf3f9 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AbstractAppCenterService.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AbstractAppCenterService.java @@ -11,7 +11,7 @@ import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.async.AppCenterFuture; import com.microsoft.appcenter.utils.async.DefaultAppCenterFuture; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import java.util.Map; @@ -120,7 +120,7 @@ public void run() { @Override public synchronized boolean isInstanceEnabled() { - return StorageHelper.PreferencesStorage.getBoolean(getEnabledPreferenceKey(), true); + return SharedPreferencesManager.getBoolean(getEnabledPreferenceKey(), true); } @Override @@ -149,7 +149,7 @@ public synchronized void setInstanceEnabled(boolean enabled) { } /* Save new state. */ - StorageHelper.PreferencesStorage.putBoolean(getEnabledPreferenceKey(), enabled); + SharedPreferencesManager.putBoolean(getEnabledPreferenceKey(), enabled); AppCenterLog.info(getLoggerTag(), String.format("%s service has been %s.", getServiceName(), enabled ? "enabled" : "disabled")); /* Don't call it before the service starts. */ diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java index 0be7344c91..74a90e5ccc 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java @@ -29,6 +29,7 @@ import com.microsoft.appcenter.utils.PrefStorageConstants; import com.microsoft.appcenter.utils.async.AppCenterFuture; import com.microsoft.appcenter.utils.async.DefaultAppCenterFuture; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import com.microsoft.appcenter.utils.storage.StorageHelper; import java.util.ArrayList; @@ -706,6 +707,7 @@ private void finishConfiguration(boolean configureFromApp) { /* If parameters are valid, init context related resources. */ StorageHelper.initialize(mApplication); + SharedPreferencesManager.initialize(mApplication); /* Initialize session storage. */ SessionContext.getInstance(); @@ -965,7 +967,7 @@ public void run() { * However after that it can be used from U.I. thread without breaking strict mode. */ boolean isInstanceEnabled() { - return StorageHelper.PreferencesStorage.getBoolean(PrefStorageConstants.KEY_ENABLED, true); + return SharedPreferencesManager.getBoolean(PrefStorageConstants.KEY_ENABLED, true); } /** @@ -993,7 +995,7 @@ private void setInstanceEnabled(boolean enabled) { /* Update state now if true, services are checking this. */ if (enabled) { - StorageHelper.PreferencesStorage.putBoolean(PrefStorageConstants.KEY_ENABLED, true); + SharedPreferencesManager.putBoolean(PrefStorageConstants.KEY_ENABLED, true); } /* Send started services. */ @@ -1012,7 +1014,7 @@ private void setInstanceEnabled(boolean enabled) { /* Update state now if false, services are checking if enabled while disabling. */ if (!enabled) { - StorageHelper.PreferencesStorage.putBoolean(PrefStorageConstants.KEY_ENABLED, false); + SharedPreferencesManager.putBoolean(PrefStorageConstants.KEY_ENABLED, false); } /* Log current state. */ diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/SessionContext.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/SessionContext.java index a3906d768b..8be31102ea 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/SessionContext.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/SessionContext.java @@ -4,7 +4,7 @@ import android.support.annotation.WorkerThread; import com.microsoft.appcenter.utils.AppCenterLog; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import java.util.LinkedHashSet; import java.util.Map; @@ -64,7 +64,7 @@ private SessionContext() { /* Try loading past sessions from storage. */ mAppLaunchTimestamp = System.currentTimeMillis(); - Set storedSessions = StorageHelper.PreferencesStorage.getStringSet(STORAGE_KEY); + Set storedSessions = SharedPreferencesManager.getStringSet(STORAGE_KEY); if (storedSessions != null) { for (String session : storedSessions) { String[] split = session.split(STORAGE_KEY_VALUE_SEPARATOR, -1); @@ -130,7 +130,7 @@ public synchronized void addSession(UUID sessionId) { for (SessionInfo session : mSessions.values()) { sessionStorage.add(session.toString()); } - StorageHelper.PreferencesStorage.putStringSet(STORAGE_KEY, sessionStorage); + SharedPreferencesManager.putStringSet(STORAGE_KEY, sessionStorage); } /** @@ -152,7 +152,7 @@ public synchronized SessionInfo getSessionAt(long timestamp) { */ public synchronized void clearSessions() { mSessions.clear(); - StorageHelper.PreferencesStorage.remove(STORAGE_KEY); + SharedPreferencesManager.remove(STORAGE_KEY); } /** diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/IdHelper.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/IdHelper.java index 14a2b92b5b..8e5823cce0 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/IdHelper.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/IdHelper.java @@ -3,7 +3,7 @@ import android.support.annotation.NonNull; import com.microsoft.appcenter.AppCenter; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import java.util.UUID; @@ -18,14 +18,14 @@ public class IdHelper { */ @NonNull public static UUID getInstallId() { - String installIdString = StorageHelper.PreferencesStorage.getString(KEY_INSTALL_ID, ""); + String installIdString = SharedPreferencesManager.getString(KEY_INSTALL_ID, ""); UUID installId; try { installId = UUID.fromString(installIdString); } catch (Exception e) { AppCenterLog.warn(AppCenter.LOG_TAG, "Unable to get installID from Shared Preferences"); installId = UUIDUtils.randomUUID(); - StorageHelper.PreferencesStorage.putString(KEY_INSTALL_ID, installId.toString()); + SharedPreferencesManager.putString(KEY_INSTALL_ID, installId.toString()); } return installId; } diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManager.java new file mode 100644 index 0000000000..e5c0b35734 --- /dev/null +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManager.java @@ -0,0 +1,275 @@ +package com.microsoft.appcenter.utils.storage; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.SharedPreferences; +import android.support.annotation.NonNull; + +import java.util.Set; + +/** + * SharedPreferencesManager Helper class + */ +public class SharedPreferencesManager { + + /** + * Name of preferences. + */ + private static final String PREFERENCES_NAME = "AppCenter"; + + /** + * Application context instance. + */ + @SuppressLint("StaticFieldLeak") + private static Context sContext; + + /** + * Android SharedPreferences instance. + */ + private static SharedPreferences sSharedPreferences; + + /** + * Initializes StorageHelper class. + * + * @param context The context of the application. + */ + public static synchronized void initialize(Context context) { + if (sContext == null) { + sContext = context; + sSharedPreferences = sContext.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE); + } + } + + /** + * Retrieve a boolean value. + * + * @param key The key for which the value is to be retrieved. + * @return The value of {@code key} or false if key is not set. + */ + @SuppressWarnings("unused") + public static boolean getBoolean(@NonNull String key) { + return getBoolean(key, false); + } + + /** + * Retrieve a boolean value and provide a default value. + * + * @param key The key for which the value is to be retrieved. + * @param defValue The default value to return if no value is set for {@code key}. + * @return The value of {@code key} or the default value if key is not set. + */ + public static boolean getBoolean(@NonNull String key, boolean defValue) { + return sSharedPreferences.getBoolean(key, defValue); + } + + /** + * Store a boolean value. + * + * @param key The key to store the value for. + * @param value The value to store for the key. + */ + public static void putBoolean(@NonNull String key, boolean value) { + SharedPreferences.Editor editor = sSharedPreferences.edit(); + editor.putBoolean(key, value); + editor.apply(); + } + + /** + * Retrieve a float value. + * + * @param key The key for which the value is to be retrieved. + * @return The value of {@code key} or 0f if key is not set. + */ + @SuppressWarnings({"WeakerAccess", "unused"}) + public static float getFloat(@NonNull String key) { + return getFloat(key, 0f); + } + + /** + * Retrieve a float value and provide a default value. + * + * @param key The key for which the value is to be retrieved. + * @param defValue The default value to return if no value is set for {@code key}. + * @return The value of {@code key} or the default value if key is not set. + */ + @SuppressWarnings({"SameParameterValue", "WeakerAccess"}) + public static float getFloat(@NonNull String key, float defValue) { + return sSharedPreferences.getFloat(key, defValue); + } + + /** + * Store a float value. + * + * @param key The key to store the value for. + * @param value The value to store for the key. + */ + @SuppressWarnings({"WeakerAccess", "unused"}) + public static void putFloat(@NonNull String key, float value) { + SharedPreferences.Editor editor = sSharedPreferences.edit(); + editor.putFloat(key, value); + editor.apply(); + } + + /** + * Retrieve an int value. + * + * @param key The key for which the value is to be retrieved. + * @return The value of {@code key} or 0 if key is not set. + */ + @SuppressWarnings({"SameParameterValue", "WeakerAccess"}) + public static int getInt(@NonNull String key) { + return getInt(key, 0); + } + + /** + * Retrieve an int value and provide a default value. + * + * @param key The key for which the value is to be retrieved. + * @param defValue The default value to return if no value is set for {@code key}. + * @return The value of {@code key} or the default value if key is not set. + */ + @SuppressWarnings({"SameParameterValue", "WeakerAccess"}) + public static int getInt(@NonNull String key, int defValue) { + return sSharedPreferences.getInt(key, defValue); + } + + /** + * Store an int value. + * + * @param key The key to store the value for. + * @param value The value to store for the key. + */ + @SuppressWarnings({"SameParameterValue", "WeakerAccess"}) + public static void putInt(@NonNull String key, int value) { + SharedPreferences.Editor editor = sSharedPreferences.edit(); + editor.putInt(key, value); + editor.apply(); + } + + /** + * Retrieve a long value. + * + * @param key The key for which the value is to be retrieved. + * @return The value of {@code key} or 0L if key is not set. + */ + @SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue"}) + public static long getLong(@NonNull String key) { + return getLong(key, 0L); + } + + /** + * Retrieve a long value and provide a default value. + * + * @param key The key for which the value is to be retrieved. + * @param defValue The default value to return if no value is set for {@code key}. + * @return The value of {@code key} or the default value if key is not set. + */ + @SuppressWarnings({"SameParameterValue", "WeakerAccess"}) + public static long getLong(@NonNull String key, long defValue) { + return sSharedPreferences.getLong(key, defValue); + } + + /** + * Store a long value. + * + * @param key The key to store the value for. + * @param value The value to store for the key. + */ + @SuppressWarnings({"WeakerAccess", "unused"}) + public static void putLong(@NonNull String key, long value) { + SharedPreferences.Editor editor = sSharedPreferences.edit(); + editor.putLong(key, value); + editor.apply(); + } + + /** + * Retrieve a string value. + * + * @param key The key for which the value is to be retrieved. + * @return The value of {@code key} or {@code null} if key is not set. + */ + @SuppressWarnings("unused") + public static String getString(@NonNull String key) { + return getString(key, null); + } + + /** + * Retrieve a string value and provide a default value. + * + * @param key The key for which the value is to be retrieved. + * @param defValue The default value to return if no value is set for {@code key}. + * @return The value of {@code key} or the default value if key is not set. + */ + public static String getString(@NonNull String key, String defValue) { + return sSharedPreferences.getString(key, defValue); + } + + /** + * Store a string value. + * + * @param key The key to store the value for. + * @param value The value to store for the key. + */ + @SuppressWarnings({"SameParameterValue", "WeakerAccess"}) + public static void putString(@NonNull String key, String value) { + SharedPreferences.Editor editor = sSharedPreferences.edit(); + editor.putString(key, value); + editor.apply(); + } + + /** + * Retrieve a string set. + * + * @param key The key for which the value is to be retrieved. + * @return The value of {@code key} or {@code null} if key is not set. + */ + @SuppressWarnings("unused") + public static Set getStringSet(@NonNull String key) { + return getStringSet(key, null); + } + + /** + * Retrieve a string set and provide a default value. + * + * @param key The key for which the value is to be retrieved. + * @param defValue The default value to return if no value is set for {@code key}. + * @return The value of {@code key} or the default value if key is not set. + */ + @SuppressWarnings({"SameParameterValue", "WeakerAccess"}) + public static Set getStringSet(@NonNull String key, Set defValue) { + return sSharedPreferences.getStringSet(key, defValue); + } + + /** + * Store a string set. + * + * @param key The key to store the value for. + * @param value The value to store for the key. + */ + @SuppressWarnings("unused") + public static void putStringSet(@NonNull String key, Set value) { + SharedPreferences.Editor editor = sSharedPreferences.edit(); + editor.putStringSet(key, value); + editor.apply(); + } + + /** + * Removes a value with the given key. + * + * @param key Key of the value to be removed. + */ + public static void remove(@NonNull String key) { + SharedPreferences.Editor editor = sSharedPreferences.edit(); + editor.remove(key); + editor.apply(); + } + + /** + * Removes all keys and values. + */ + public static void clear() { + SharedPreferences.Editor editor = sSharedPreferences.edit(); + editor.clear(); + editor.apply(); + } +} diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/StorageHelper.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/StorageHelper.java index fa4784ef14..0d748d4b84 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/StorageHelper.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/StorageHelper.java @@ -23,7 +23,6 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; -import java.util.Set; /** * StorageHelper class to access local storage. @@ -58,245 +57,6 @@ public static synchronized void initialize(Context context) { } } - /** - * PreferencesStorage Helper class - */ - public static class PreferencesStorage { - - /** - * Retrieve a boolean value. - * - * @param key The key for which the value is to be retrieved. - * @return The value of {@code key} or false if key is not set. - */ - @SuppressWarnings("unused") - public static boolean getBoolean(@NonNull String key) { - return getBoolean(key, false); - } - - /** - * Retrieve a boolean value and provide a default value. - * - * @param key The key for which the value is to be retrieved. - * @param defValue The default value to return if no value is set for {@code key}. - * @return The value of {@code key} or the default value if key is not set. - */ - public static boolean getBoolean(@NonNull String key, boolean defValue) { - return sSharedPreferences.getBoolean(key, defValue); - } - - /** - * Store a boolean value. - * - * @param key The key to store the value for. - * @param value The value to store for the key. - */ - public static void putBoolean(@NonNull String key, boolean value) { - SharedPreferences.Editor editor = sSharedPreferences.edit(); - editor.putBoolean(key, value); - editor.apply(); - } - - /** - * Retrieve a float value. - * - * @param key The key for which the value is to be retrieved. - * @return The value of {@code key} or 0f if key is not set. - */ - @SuppressWarnings({"WeakerAccess", "unused"}) - public static float getFloat(@NonNull String key) { - return getFloat(key, 0f); - } - - /** - * Retrieve a float value and provide a default value. - * - * @param key The key for which the value is to be retrieved. - * @param defValue The default value to return if no value is set for {@code key}. - * @return The value of {@code key} or the default value if key is not set. - */ - @SuppressWarnings({"SameParameterValue", "WeakerAccess"}) - public static float getFloat(@NonNull String key, float defValue) { - return sSharedPreferences.getFloat(key, defValue); - } - - /** - * Store a float value. - * - * @param key The key to store the value for. - * @param value The value to store for the key. - */ - @SuppressWarnings({"WeakerAccess", "unused"}) - public static void putFloat(@NonNull String key, float value) { - SharedPreferences.Editor editor = sSharedPreferences.edit(); - editor.putFloat(key, value); - editor.apply(); - } - - /** - * Retrieve an int value. - * - * @param key The key for which the value is to be retrieved. - * @return The value of {@code key} or 0 if key is not set. - */ - @SuppressWarnings({"SameParameterValue", "WeakerAccess"}) - public static int getInt(@NonNull String key) { - return getInt(key, 0); - } - - /** - * Retrieve an int value and provide a default value. - * - * @param key The key for which the value is to be retrieved. - * @param defValue The default value to return if no value is set for {@code key}. - * @return The value of {@code key} or the default value if key is not set. - */ - @SuppressWarnings({"SameParameterValue", "WeakerAccess"}) - public static int getInt(@NonNull String key, int defValue) { - return sSharedPreferences.getInt(key, defValue); - } - - /** - * Store an int value. - * - * @param key The key to store the value for. - * @param value The value to store for the key. - */ - @SuppressWarnings({"SameParameterValue", "WeakerAccess"}) - public static void putInt(@NonNull String key, int value) { - SharedPreferences.Editor editor = sSharedPreferences.edit(); - editor.putInt(key, value); - editor.apply(); - } - - /** - * Retrieve a long value. - * - * @param key The key for which the value is to be retrieved. - * @return The value of {@code key} or 0L if key is not set. - */ - @SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue"}) - public static long getLong(@NonNull String key) { - return getLong(key, 0L); - } - - /** - * Retrieve a long value and provide a default value. - * - * @param key The key for which the value is to be retrieved. - * @param defValue The default value to return if no value is set for {@code key}. - * @return The value of {@code key} or the default value if key is not set. - */ - @SuppressWarnings({"SameParameterValue", "WeakerAccess"}) - public static long getLong(@NonNull String key, long defValue) { - return sSharedPreferences.getLong(key, defValue); - } - - /** - * Store a long value. - * - * @param key The key to store the value for. - * @param value The value to store for the key. - */ - @SuppressWarnings({"WeakerAccess", "unused"}) - public static void putLong(@NonNull String key, long value) { - SharedPreferences.Editor editor = sSharedPreferences.edit(); - editor.putLong(key, value); - editor.apply(); - } - - /** - * Retrieve a string value. - * - * @param key The key for which the value is to be retrieved. - * @return The value of {@code key} or {@code null} if key is not set. - */ - @SuppressWarnings("unused") - public static String getString(@NonNull String key) { - return getString(key, null); - } - - /** - * Retrieve a string value and provide a default value. - * - * @param key The key for which the value is to be retrieved. - * @param defValue The default value to return if no value is set for {@code key}. - * @return The value of {@code key} or the default value if key is not set. - */ - public static String getString(@NonNull String key, String defValue) { - return sSharedPreferences.getString(key, defValue); - } - - /** - * Store a string value. - * - * @param key The key to store the value for. - * @param value The value to store for the key. - */ - @SuppressWarnings({"SameParameterValue", "WeakerAccess"}) - public static void putString(@NonNull String key, String value) { - SharedPreferences.Editor editor = sSharedPreferences.edit(); - editor.putString(key, value); - editor.apply(); - } - - /** - * Retrieve a string set. - * - * @param key The key for which the value is to be retrieved. - * @return The value of {@code key} or {@code null} if key is not set. - */ - @SuppressWarnings("unused") - public static Set getStringSet(@NonNull String key) { - return getStringSet(key, null); - } - - /** - * Retrieve a string set and provide a default value. - * - * @param key The key for which the value is to be retrieved. - * @param defValue The default value to return if no value is set for {@code key}. - * @return The value of {@code key} or the default value if key is not set. - */ - @SuppressWarnings({"SameParameterValue", "WeakerAccess"}) - public static Set getStringSet(@NonNull String key, Set defValue) { - return sSharedPreferences.getStringSet(key, defValue); - } - - /** - * Store a string set. - * - * @param key The key to store the value for. - * @param value The value to store for the key. - */ - @SuppressWarnings("unused") - public static void putStringSet(@NonNull String key, Set value) { - SharedPreferences.Editor editor = sSharedPreferences.edit(); - editor.putStringSet(key, value); - editor.apply(); - } - - /** - * Removes a value with the given key. - * - * @param key Key of the value to be removed. - */ - public static void remove(@NonNull String key) { - SharedPreferences.Editor editor = sSharedPreferences.edit(); - editor.remove(key); - editor.apply(); - } - - /** - * Removes all keys and values. - */ - public static void clear() { - SharedPreferences.Editor editor = sSharedPreferences.edit(); - editor.clear(); - editor.apply(); - } - } - /** * InternalStorage Helper class */ diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterServiceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterServiceTest.java index 8f76cebdf6..ecaba03d8c 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterServiceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterServiceTest.java @@ -4,7 +4,7 @@ import com.microsoft.appcenter.channel.Channel; import com.microsoft.appcenter.ingestion.Ingestion; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.Assert; import org.junit.Before; @@ -40,7 +40,7 @@ @SuppressWarnings("unused") @RunWith(PowerMockRunner.class) -@PrepareForTest({StorageHelper.PreferencesStorage.class, AppCenter.class}) +@PrepareForTest({SharedPreferencesManager.class, AppCenter.class}) public class AbstractAppCenterServiceTest { private static final String SERVICE_ENABLED_KEY = KEY_ENABLED + "_Test"; @@ -69,8 +69,8 @@ protected String getLoggerTag() { mockStatic(AppCenter.class); /* First call to com.microsoft.appcenter.AppCenter.isEnabled shall return true, initial state. */ - mockStatic(StorageHelper.PreferencesStorage.class); - when(StorageHelper.PreferencesStorage.getBoolean(SERVICE_ENABLED_KEY, true)).thenReturn(true); + mockStatic(SharedPreferencesManager.class); + when(SharedPreferencesManager.getBoolean(SERVICE_ENABLED_KEY, true)).thenReturn(true); /* Then simulate further changes to state. */ PowerMockito.doAnswer(new Answer() { @@ -80,11 +80,11 @@ public Object answer(InvocationOnMock invocation) { /* Whenever the new state is persisted, make further calls return the new state. */ boolean enabled = (Boolean) invocation.getArguments()[1]; - when(StorageHelper.PreferencesStorage.getBoolean(SERVICE_ENABLED_KEY, true)).thenReturn(enabled); + when(SharedPreferencesManager.getBoolean(SERVICE_ENABLED_KEY, true)).thenReturn(enabled); return null; } - }).when(StorageHelper.PreferencesStorage.class); - StorageHelper.PreferencesStorage.putBoolean(eq(SERVICE_ENABLED_KEY), anyBoolean()); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putBoolean(eq(SERVICE_ENABLED_KEY), anyBoolean()); } @Test @@ -142,25 +142,25 @@ public Void answer(InvocationOnMock invocation) { /* Change state to true will have no effect. */ mService.setInstanceEnabledAsync(true); verifyStatic(never()); - StorageHelper.PreferencesStorage.putBoolean(eq(mService.getEnabledPreferenceKey()), anyBoolean()); + SharedPreferencesManager.putBoolean(eq(mService.getEnabledPreferenceKey()), anyBoolean()); /* Disable. */ mService.setInstanceEnabledAsync(false); assertFalse(mService.isInstanceEnabledAsync().get()); verifyStatic(); - StorageHelper.PreferencesStorage.putBoolean(mService.getEnabledPreferenceKey(), false); + SharedPreferencesManager.putBoolean(mService.getEnabledPreferenceKey(), false); /* Disable again will have no effect. */ mService.setInstanceEnabledAsync(false); assertFalse(mService.isInstanceEnabledAsync().get()); verifyStatic(); - StorageHelper.PreferencesStorage.putBoolean(mService.getEnabledPreferenceKey(), false); + SharedPreferencesManager.putBoolean(mService.getEnabledPreferenceKey(), false); /* Enable back. */ mService.setInstanceEnabledAsync(true); assertTrue(mService.isInstanceEnabledAsync().get()); verifyStatic(); - StorageHelper.PreferencesStorage.putBoolean(mService.getEnabledPreferenceKey(), true); + SharedPreferencesManager.putBoolean(mService.getEnabledPreferenceKey(), true); /* Enable again has no effect. */ mService.setInstanceEnabledAsync(true); @@ -168,9 +168,9 @@ public Void answer(InvocationOnMock invocation) { /* Verify only 2 actual changes in storage: false to true, then true to false. */ verifyStatic(); - StorageHelper.PreferencesStorage.putBoolean(mService.getEnabledPreferenceKey(), false); + SharedPreferencesManager.putBoolean(mService.getEnabledPreferenceKey(), false); verifyStatic(); - StorageHelper.PreferencesStorage.putBoolean(mService.getEnabledPreferenceKey(), true); + SharedPreferencesManager.putBoolean(mService.getEnabledPreferenceKey(), true); } @Test @@ -197,7 +197,7 @@ public Void answer(InvocationOnMock invocation) { mService.setInstanceEnabledAsync(false); assertFalse(mService.isInstanceEnabledAsync().get()); verifyStatic(never()); - StorageHelper.PreferencesStorage.putBoolean(eq(mService.getEnabledPreferenceKey()), anyBoolean()); + SharedPreferencesManager.putBoolean(eq(mService.getEnabledPreferenceKey()), anyBoolean()); } @Test diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java index a80766eec6..d358cce702 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java @@ -20,6 +20,7 @@ import com.microsoft.appcenter.utils.ShutdownHelper; import com.microsoft.appcenter.utils.async.AppCenterFuture; import com.microsoft.appcenter.utils.storage.DatabaseManager; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import com.microsoft.appcenter.utils.storage.StorageHelper; import org.junit.After; @@ -59,7 +60,7 @@ AppCenterLog.class, StartServiceLog.class, StorageHelper.class, - StorageHelper.PreferencesStorage.class, + SharedPreferencesManager.class, IdHelper.class, DeviceInfoHelper.class, Thread.class, @@ -119,7 +120,7 @@ public void setUp() throws Exception { mockStatic(Constants.class); mockStatic(AppCenterLog.class); mockStatic(StorageHelper.class); - mockStatic(StorageHelper.PreferencesStorage.class); + mockStatic(SharedPreferencesManager.class); mockStatic(IdHelper.class); mockStatic(Thread.class); mockStatic(ShutdownHelper.class); @@ -144,7 +145,7 @@ public Void answer(InvocationOnMock invocation) { addArgumentToRegistry(ServiceInstrumentationUtils.DISABLE_SERVICES, null); /* First call to com.microsoft.appcenter.AppCenter.isEnabled shall return true, initial state. */ - when(StorageHelper.PreferencesStorage.getBoolean(anyString(), eq(true))).thenReturn(true); + when(SharedPreferencesManager.getBoolean(anyString(), eq(true))).thenReturn(true); /* Then simulate further changes to state. */ PowerMockito.doAnswer(new Answer() { @@ -155,11 +156,11 @@ public Void answer(InvocationOnMock invocation) { /* Whenever the new state is persisted, make further calls return the new state. */ String key = (String) invocation.getArguments()[0]; boolean enabled = (Boolean) invocation.getArguments()[1]; - when(StorageHelper.PreferencesStorage.getBoolean(key, true)).thenReturn(enabled); + when(SharedPreferencesManager.getBoolean(key, true)).thenReturn(enabled); return null; } - }).when(StorageHelper.PreferencesStorage.class); - StorageHelper.PreferencesStorage.putBoolean(anyString(), anyBoolean()); + }).when(SharedPreferencesManager.class); + SharedPreferencesManager.putBoolean(anyString(), anyBoolean()); /* Mock empty database. */ DatabaseManager databaseManager = mock(DatabaseManager.class); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java index 22c1000f8d..73fc954b4e 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java @@ -10,7 +10,7 @@ import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.DeviceInfoHelper; import com.microsoft.appcenter.utils.ShutdownHelper; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.Test; import org.mockito.ArgumentMatcher; @@ -98,7 +98,7 @@ public void useDummyServiceTest() { @Test public void useDummyServiceWhenDisablePersisted() { - when(StorageHelper.PreferencesStorage.getBoolean(KEY_ENABLED, true)).thenReturn(false); + when(SharedPreferencesManager.getBoolean(KEY_ENABLED, true)).thenReturn(false); AppCenter appCenter = AppCenter.getInstance(); DummyService service = DummyService.getInstance(); AnotherDummyService anotherService = AnotherDummyService.getInstance(); @@ -528,8 +528,8 @@ public void enableBeforeConfiguredTest() { @Test public void disablePersisted() { - when(StorageHelper.PreferencesStorage.getBoolean(KEY_ENABLED, true)).thenReturn(false); - when(StorageHelper.PreferencesStorage.getBoolean(AnotherDummyService.getInstance().getEnabledPreferenceKey(), true)).thenReturn(false); + when(SharedPreferencesManager.getBoolean(KEY_ENABLED, true)).thenReturn(false); + when(SharedPreferencesManager.getBoolean(AnotherDummyService.getInstance().getEnabledPreferenceKey(), true)).thenReturn(false); AppCenter.start(mApplication, DUMMY_APP_SECRET, DummyService.class, AnotherDummyService.class); AppCenter appCenter = AppCenter.getInstance(); @@ -555,7 +555,7 @@ public void disablePersisted() { @Test public void disabledBeforeStart() { - when(StorageHelper.PreferencesStorage.getBoolean(KEY_ENABLED, true)).thenReturn(true); + when(SharedPreferencesManager.getBoolean(KEY_ENABLED, true)).thenReturn(true); /* Verify services are disabled if called before start (no access to storage). */ assertFalse(AppCenter.isEnabled().get()); @@ -569,7 +569,7 @@ public void disabledBeforeStart() { @Test public void disablePersistedAndDisable() { - when(StorageHelper.PreferencesStorage.getBoolean(KEY_ENABLED, true)).thenReturn(false); + when(SharedPreferencesManager.getBoolean(KEY_ENABLED, true)).thenReturn(false); AppCenter.start(mApplication, DUMMY_APP_SECRET, DummyService.class, AnotherDummyService.class); AppCenter appCenter = AppCenter.getInstance(); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java index 33042bbfaf..0ae4b52aac 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java @@ -5,6 +5,7 @@ import com.microsoft.appcenter.utils.DeviceInfoHelper; import com.microsoft.appcenter.utils.IdHelper; import com.microsoft.appcenter.utils.PrefStorageConstants; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import com.microsoft.appcenter.utils.storage.StorageHelper; import org.junit.Test; @@ -18,8 +19,8 @@ public class InstantiationTest { @Test public void storageHelper() { new StorageHelper(); - new StorageHelper.PreferencesStorage(); new StorageHelper.InternalStorage(); + new SharedPreferencesManager(); } @Test From fe00dc1e9f12652fba29d53817cfe19ff5563946 Mon Sep 17 00:00:00 2001 From: Jae Lim Date: Thu, 25 Oct 2018 12:46:34 -0700 Subject: [PATCH 05/68] Refactor InternalStorage helper class --- .../appcenter/analytics/AnalyticsTest.java | 2 +- .../appcenter/crashes/CrashesAndroidTest.java | 10 +- ...WrapperSdkExceptionManagerAndroidTest.java | 2 +- .../utils/ErrorLogHelperAndroidTest.java | 8 +- .../microsoft/appcenter/crashes/Crashes.java | 14 +- .../crashes/WrapperSdkExceptionManager.java | 10 +- .../crashes/utils/ErrorLogHelper.java | 14 +- .../appcenter/crashes/CrashesTest.java | 60 ++-- .../crashes/UncaughtExceptionHandlerTest.java | 10 +- .../WrapperSdkExceptionManagerTest.java | 46 +-- .../DatabasePersistenceAndroidTest.java | 8 +- .../storage/StorageHelperAndroidTest.java | 51 ++- .../com/microsoft/appcenter/AppCenter.java | 5 +- .../persistence/DatabasePersistence.java | 6 +- .../appcenter/utils/storage/FileManager.java | 280 ++++++++++++++++ .../utils/storage/StorageHelper.java | 298 ------------------ .../appcenter/AbstractAppCenterTest.java | 7 +- .../appcenter/InstantiationTest.java | 6 +- .../appcenter/StorageHelperTest.java | 24 +- 19 files changed, 422 insertions(+), 439 deletions(-) create mode 100644 sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/FileManager.java delete mode 100644 sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/StorageHelper.java diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java index f1b08f430c..d7dd73b41b 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java @@ -27,7 +27,7 @@ import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.async.AppCenterConsumer; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.FileManager; import org.junit.Assert; import org.junit.Test; diff --git a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java index 3aae8c1f8e..3cdbc07170 100644 --- a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java +++ b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java @@ -18,7 +18,7 @@ import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.async.AppCenterConsumer; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.FileManager; import org.junit.After; import org.junit.Before; @@ -78,7 +78,7 @@ public boolean accept(File file) { public static void setUpClass() { sDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler(); sApplication = (Application) InstrumentationRegistry.getContext().getApplicationContext(); - StorageHelper.initialize(sApplication); + FileManager.initialize(sApplication); SharedPreferencesManager.initialize(sApplication); Constants.loadFromContext(sApplication); } @@ -175,7 +175,7 @@ public void getLastSessionCrashReportNative() throws Exception { /* Simulate we have a minidump. */ File newMinidumpDirectory = ErrorLogHelper.getNewMinidumpDirectory(); File minidumpFile = new File(newMinidumpDirectory, "minidump.dmp"); - StorageHelper.InternalStorage.write(minidumpFile, "mock minidump"); + FileManager.write(minidumpFile, "mock minidump"); /* Start crashes now. */ startFresh(null); @@ -203,7 +203,7 @@ public void failedToMoveMinidump() throws Exception { /* Simulate we have a minidump. */ File newMinidumpDirectory = ErrorLogHelper.getNewMinidumpDirectory(); File minidumpFile = new File(newMinidumpDirectory, "minidump.dmp"); - StorageHelper.InternalStorage.write(minidumpFile, "mock minidump"); + FileManager.write(minidumpFile, "mock minidump"); /* Make moving fail. */ assertTrue(ErrorLogHelper.getPendingMinidumpDirectory().delete()); @@ -368,7 +368,7 @@ public void processingWithMinidump() throws Exception { /* Simulate we have a minidump. */ File newMinidumpDirectory = ErrorLogHelper.getNewMinidumpDirectory(); File minidumpFile = new File(newMinidumpDirectory, "minidump.dmp"); - StorageHelper.InternalStorage.write(minidumpFile, "mock minidump"); + FileManager.write(minidumpFile, "mock minidump"); /* Set up crash listener. */ CrashesListener crashesListener = mock(CrashesListener.class); diff --git a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerAndroidTest.java b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerAndroidTest.java index 2ac2201eb1..ce710861ba 100644 --- a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerAndroidTest.java +++ b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerAndroidTest.java @@ -11,7 +11,7 @@ import com.microsoft.appcenter.crashes.ingestion.models.Exception; import com.microsoft.appcenter.crashes.utils.ErrorLogHelper; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.FileManager; import org.junit.Assert; import org.junit.Before; diff --git a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperAndroidTest.java b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperAndroidTest.java index 1bdf19a9b9..1b3434c66f 100644 --- a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperAndroidTest.java +++ b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperAndroidTest.java @@ -3,7 +3,7 @@ import android.support.test.InstrumentationRegistry; import com.microsoft.appcenter.Constants; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.FileManager; import org.junit.After; import org.junit.Before; @@ -77,7 +77,7 @@ public void getStoredFile() throws Exception { * file.setLastModified does not work on most devices or emulators and returns false. * So we can only sleep at least 1 second between each to make sure the time is different... */ - StorageHelper.InternalStorage.write(file, "contents"); + FileManager.write(file, "contents"); testFiles[i] = file; Thread.sleep(1000); } @@ -85,7 +85,7 @@ public void getStoredFile() throws Exception { assertEquals(testFiles[2], ErrorLogHelper.getLastErrorLogFile()); testFiles[3] = new File(mErrorDirectory, new UUID(0, 3).toString() + ErrorLogHelper.THROWABLE_FILE_EXTENSION); - StorageHelper.InternalStorage.write(testFiles[3], "contents"); + FileManager.write(testFiles[3], "contents"); /* Get all error logs stored in the file system when logs exist. */ files = ErrorLogHelper.getStoredErrorLogFiles(); @@ -133,6 +133,6 @@ public void getStoredFile() throws Exception { /* Clean up. */ for (int i = 0; i < 2; i++) - StorageHelper.InternalStorage.delete(testFiles[i]); + FileManager.delete(testFiles[i]); } } diff --git a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java index 92f797949d..4a65397d5e 100644 --- a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java +++ b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java @@ -30,8 +30,8 @@ import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.async.AppCenterFuture; import com.microsoft.appcenter.utils.async.DefaultAppCenterFuture; +import com.microsoft.appcenter.utils.storage.FileManager; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import com.microsoft.appcenter.utils.storage.StorageHelper; import org.json.JSONException; @@ -620,7 +620,7 @@ private void initialize() { File logFile = ErrorLogHelper.getLastErrorLogFile(); if (logFile != null) { AppCenterLog.debug(LOG_TAG, "Processing crash report for the last session."); - String logFileContents = StorageHelper.InternalStorage.read(logFile); + String logFileContents = FileManager.read(logFile); if (logFileContents == null) { AppCenterLog.error(LOG_TAG, "Error reading last session error log."); } else { @@ -639,7 +639,7 @@ private void initialize() { private void processPendingErrors() { for (File logFile : ErrorLogHelper.getStoredErrorLogFiles()) { AppCenterLog.debug(LOG_TAG, "Process pending error file: " + logFile); - String logfileContents = StorageHelper.InternalStorage.read(logFile); + String logfileContents = FileManager.read(logFile); if (logfileContents != null) { try { ManagedErrorLog log = (ManagedErrorLog) mLogSerializer.deserializeLog(logfileContents, null); @@ -748,7 +748,7 @@ ErrorReport buildErrorReport(ManagedErrorLog log) { try { Throwable throwable = null; if (file.length() > 0) { - throwable = StorageHelper.InternalStorage.readObject(file); + throwable = FileManager.readObject(file); } ErrorReport report = ErrorLogHelper.getErrorReportFromErrorLog(log, throwable); mErrorReportCache.put(id, new ErrorLogReport(log, report)); @@ -815,7 +815,7 @@ public void run() { Exception exception = errorLogReport.log.getException(); dumpFile = new File(exception.getStackTrace()); exception.setStackTrace(null); - byte[] logfileContents = StorageHelper.InternalStorage.readBytes(dumpFile); + byte[] logfileContents = FileManager.readBytes(dumpFile); dumpAttachment = ErrorAttachmentLog.attachmentWithBinary(logfileContents, "minidump.dmp", "application/octet-stream"); } @@ -933,11 +933,11 @@ private UUID saveErrorLogFiles(Throwable throwable, ManagedErrorLog errorLog) th AppCenterLog.debug(Crashes.LOG_TAG, "Saving uncaught exception."); File errorLogFile = new File(errorStorageDirectory, filename + ErrorLogHelper.ERROR_LOG_FILE_EXTENSION); String errorLogString = mLogSerializer.serializeLog(errorLog); - StorageHelper.InternalStorage.write(errorLogFile, errorLogString); + FileManager.write(errorLogFile, errorLogString); AppCenterLog.debug(Crashes.LOG_TAG, "Saved JSON content for ingestion into " + errorLogFile); File throwableFile = new File(errorStorageDirectory, filename + ErrorLogHelper.THROWABLE_FILE_EXTENSION); if (throwable != null) { - StorageHelper.InternalStorage.writeObject(throwableFile, throwable); + FileManager.writeObject(throwableFile, throwable); AppCenterLog.debug(Crashes.LOG_TAG, "Saved Throwable as is for client side inspection in " + throwableFile + " throwable:", throwable); } else { diff --git a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManager.java b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManager.java index 7d0e3711bf..18768226f7 100644 --- a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManager.java +++ b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManager.java @@ -8,7 +8,7 @@ import com.microsoft.appcenter.crashes.utils.ErrorLogHelper; import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.async.AppCenterFuture; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.FileManager; import java.io.File; import java.io.IOException; @@ -51,7 +51,7 @@ public static UUID saveWrapperException(Thread thread, Throwable throwable, com. if (errorId != null && rawSerializedException != null) { sWrapperExceptionDataContainer.put(errorId.toString(), rawSerializedException); File dataFile = getFile(errorId); - StorageHelper.InternalStorage.writeObject(dataFile, rawSerializedException); + FileManager.writeObject(dataFile, rawSerializedException); AppCenterLog.debug(Crashes.LOG_TAG, "Saved raw wrapper exception data into " + dataFile); } return errorId; @@ -77,7 +77,7 @@ public static void deleteWrapperExceptionData(UUID errorId) { if (loadResult == null) { AppCenterLog.error(Crashes.LOG_TAG, "Failed to delete wrapper exception data: data not found"); } - StorageHelper.InternalStorage.delete(dataFile); + FileManager.delete(dataFile); } } @@ -99,7 +99,7 @@ public static byte[] loadWrapperExceptionData(UUID errorId) { File dataFile = getFile(errorId); if (dataFile.exists()) { try { - dataBytes = StorageHelper.InternalStorage.readObject(dataFile); + dataBytes = FileManager.readObject(dataFile); if (dataBytes != null) { sWrapperExceptionDataContainer.put(errorId.toString(), dataBytes); } @@ -136,7 +136,7 @@ public static void trackException(com.microsoft.appcenter.crashes.ingestion.mode * Send an handled exception (used by wrapper SDKs). * * @param modelException An handled exception already in JSON model form. - * @param properties optional properties. + * @param properties optional properties. */ public static void trackException(com.microsoft.appcenter.crashes.ingestion.models.Exception modelException, Map properties) { Map validatedProperties = ErrorLogHelper.validateProperties(properties, "HandledError"); diff --git a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelper.java b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelper.java index 9ccb84af91..4556805a8f 100644 --- a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelper.java +++ b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelper.java @@ -19,7 +19,7 @@ import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.DeviceInfoHelper; import com.microsoft.appcenter.utils.UUIDUtils; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.FileManager; import java.io.File; import java.io.FilenameFilter; @@ -190,7 +190,7 @@ private static String getArchitecture() { public static synchronized File getErrorStorageDirectory() { if (sErrorLogDirectory == null) { sErrorLogDirectory = new File(Constants.FILES_PATH, ERROR_DIRECTORY); - StorageHelper.InternalStorage.mkdir(sErrorLogDirectory.getAbsolutePath()); + FileManager.mkdir(sErrorLogDirectory.getAbsolutePath()); } return sErrorLogDirectory; } @@ -201,7 +201,7 @@ public static synchronized File getNewMinidumpDirectory() { File errorStorageDirectory = getErrorStorageDirectory(); File minidumpDirectory = new File(errorStorageDirectory.getAbsolutePath(), MINIDUMP_DIRECTORY); sNewMinidumpDirectory = new File(minidumpDirectory, NEW_MINIDUMP_DIRECTORY); - StorageHelper.InternalStorage.mkdir(sNewMinidumpDirectory.getPath()); + FileManager.mkdir(sNewMinidumpDirectory.getPath()); } return sNewMinidumpDirectory; } @@ -212,7 +212,7 @@ public static synchronized File getPendingMinidumpDirectory() { File errorStorageDirectory = getErrorStorageDirectory(); File minidumpDirectory = new File(errorStorageDirectory.getAbsolutePath(), MINIDUMP_DIRECTORY); sPendingMinidumpDirectory = new File(minidumpDirectory, PENDING_MINIDUMP_DIRECTORY); - StorageHelper.InternalStorage.mkdir(sPendingMinidumpDirectory.getPath()); + FileManager.mkdir(sPendingMinidumpDirectory.getPath()); } return sPendingMinidumpDirectory; } @@ -236,7 +236,7 @@ public static File[] getNewMinidumpFiles() { @Nullable public static File getLastErrorLogFile() { - return StorageHelper.InternalStorage.lastModifiedFile(getErrorStorageDirectory(), new FilenameFilter() { + return FileManager.lastModifiedFile(getErrorStorageDirectory(), new FilenameFilter() { @Override public boolean accept(File dir, String filename) { return filename.endsWith(ERROR_LOG_FILE_EXTENSION); @@ -253,7 +253,7 @@ public static void removeStoredThrowableFile(@NonNull UUID id) { File file = getStoredThrowableFile(id); if (file != null) { AppCenterLog.info(Crashes.LOG_TAG, "Deleting throwable file " + file.getName()); - StorageHelper.InternalStorage.delete(file); + FileManager.delete(file); } } @@ -266,7 +266,7 @@ public static void removeStoredErrorLogFile(@NonNull UUID id) { File file = getStoredErrorLogFile(id); if (file != null) { AppCenterLog.info(Crashes.LOG_TAG, "Deleting error log file " + file.getName()); - StorageHelper.InternalStorage.delete(file); + FileManager.delete(file); } } diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java index 5f30e1c1fe..1052d3cc9c 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java @@ -32,8 +32,8 @@ import com.microsoft.appcenter.utils.UUIDUtils; import com.microsoft.appcenter.utils.async.AppCenterConsumer; import com.microsoft.appcenter.utils.async.AppCenterFuture; +import com.microsoft.appcenter.utils.storage.FileManager; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import com.microsoft.appcenter.utils.storage.StorageHelper; import org.json.JSONException; import org.junit.Assert; @@ -94,7 +94,7 @@ import static org.powermock.api.mockito.PowerMockito.verifyStatic; @SuppressWarnings("unused") -@PrepareForTest({ErrorLogHelper.class, SystemClock.class, StorageHelper.InternalStorage.class, SharedPreferencesManager.class, AppCenterLog.class, AppCenter.class, Crashes.class, HandlerUtils.class, Looper.class}) +@PrepareForTest({ErrorLogHelper.class, SystemClock.class, FileManager.class, SharedPreferencesManager.class, AppCenterLog.class, AppCenter.class, Crashes.class, HandlerUtils.class, Looper.class}) public class CrashesTest { @SuppressWarnings("ThrowableInstanceNeverThrown") @@ -129,7 +129,7 @@ public void setUp() { Thread.setDefaultUncaughtExceptionHandler(null); Crashes.unsetInstance(); mockStatic(SystemClock.class); - mockStatic(StorageHelper.InternalStorage.class); + mockStatic(FileManager.class); mockStatic(SharedPreferencesManager.class); mockStatic(AppCenterLog.class); when(SystemClock.elapsedRealtime()).thenReturn(System.currentTimeMillis()); @@ -311,8 +311,8 @@ public void queuePendingCrashesShouldProcess() throws IOException, ClassNotFound when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); when(ErrorLogHelper.getStoredThrowableFile(any(UUID.class))).thenReturn(mock(File.class)); when(ErrorLogHelper.getErrorReportFromErrorLog(any(ManagedErrorLog.class), any(Throwable.class))).thenReturn(report); - when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn(""); - when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(new RuntimeException()); + when(FileManager.read(any(File.class))).thenReturn(""); + when(FileManager.readObject(any(File.class))).thenReturn(new RuntimeException()); CrashesListener mockListener = mock(CrashesListener.class); when(mockListener.shouldProcess(report)).thenReturn(true); when(mockListener.shouldAwaitUserConfirmation()).thenReturn(false); @@ -360,8 +360,8 @@ public void queuePendingCrashesShouldNotProcess() throws IOException, ClassNotFo when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); when(ErrorLogHelper.getStoredThrowableFile(any(UUID.class))).thenReturn(mock(File.class)); when(ErrorLogHelper.getErrorReportFromErrorLog(any(ManagedErrorLog.class), any(Throwable.class))).thenReturn(report); - when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn(""); - when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(new RuntimeException()).thenReturn(new byte[]{}); + when(FileManager.read(any(File.class))).thenReturn(""); + when(FileManager.readObject(any(File.class))).thenReturn(new RuntimeException()).thenReturn(new byte[]{}); CrashesListener mockListener = mock(CrashesListener.class); when(mockListener.shouldProcess(report)).thenReturn(false); @@ -403,8 +403,8 @@ public void queuePendingCrashesAlwaysSend() throws IOException, ClassNotFoundExc when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); when(ErrorLogHelper.getStoredThrowableFile(any(UUID.class))).thenReturn(mock(File.class)); when(ErrorLogHelper.getErrorReportFromErrorLog(any(ManagedErrorLog.class), any(Throwable.class))).thenReturn(report); - when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn(""); - when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(new RuntimeException()); + when(FileManager.read(any(File.class))).thenReturn(""); + when(FileManager.readObject(any(File.class))).thenReturn(new RuntimeException()); when(SharedPreferencesManager.getBoolean(eq(Crashes.PREF_KEY_ALWAYS_SEND), anyBoolean())).thenReturn(true); CrashesListener mockListener = mock(CrashesListener.class); @@ -443,7 +443,7 @@ public void processPendingErrorsCorrupted() throws JSONException { mockStatic(ErrorLogHelper.class); when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{mock(File.class)}); when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); - when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn(""); + when(FileManager.read(any(File.class))).thenReturn(""); Crashes crashes = Crashes.getInstance(); @@ -502,7 +502,7 @@ public void printErrorOnJSONException() throws JSONException { mockStatic(ErrorLogHelper.class); when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{mock(File.class)}); when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); - when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn(""); + when(FileManager.read(any(File.class))).thenReturn(""); Crashes crashes = Crashes.getInstance(); LogSerializer logSerializer = mock(LogSerializer.class); @@ -710,8 +710,8 @@ public void getChannelListener() throws IOException, ClassNotFoundException, JSO when(throwableFile.length()).thenReturn(1L); when(ErrorLogHelper.getStoredThrowableFile(any(UUID.class))).thenReturn(throwableFile); when(ErrorLogHelper.getErrorReportFromErrorLog(mErrorLog, EXCEPTION)).thenReturn(errorReport); - when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn(""); - when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(EXCEPTION); + when(FileManager.read(any(File.class))).thenReturn(""); + when(FileManager.readObject(any(File.class))).thenReturn(EXCEPTION); LogSerializer logSerializer = mock(LogSerializer.class); when(logSerializer.deserializeLog(anyString(), anyString())).thenReturn(mErrorLog); @@ -764,7 +764,7 @@ public void getChannelListenerErrors() throws IOException, ClassNotFoundExceptio when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{mock(File.class)}); when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); when(ErrorLogHelper.getStoredThrowableFile(any(UUID.class))).thenReturn(mock(File.class)); - when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(null); + when(FileManager.readObject(any(File.class))).thenReturn(null); CrashesListener mockListener = mock(CrashesListener.class); Crashes crashes = Crashes.getInstance(); @@ -792,8 +792,8 @@ public void handleUserConfirmationDoNotSend() throws IOException, ClassNotFoundE when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); when(ErrorLogHelper.getStoredThrowableFile(any(UUID.class))).thenReturn(mock(File.class)); when(ErrorLogHelper.getErrorReportFromErrorLog(any(ManagedErrorLog.class), any(Throwable.class))).thenReturn(new ErrorReport()); - when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn(""); - when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(null); + when(FileManager.read(any(File.class))).thenReturn(""); + when(FileManager.readObject(any(File.class))).thenReturn(null); CrashesListener mockListener = mock(CrashesListener.class); when(mockListener.shouldProcess(any(ErrorReport.class))).thenReturn(true); @@ -830,7 +830,7 @@ public void handleUserConfirmationAlwaysSend() throws IOException, ClassNotFound when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{mock(File.class)}); when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); when(ErrorLogHelper.getStoredThrowableFile(any(UUID.class))).thenReturn(mock(File.class)); - when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(null); + when(FileManager.readObject(any(File.class))).thenReturn(null); CrashesListener mockListener = mock(CrashesListener.class); when(mockListener.shouldProcess(any(ErrorReport.class))).thenReturn(true); @@ -860,7 +860,7 @@ public void buildErrorReport() throws IOException, ClassNotFoundException { when(throwableFile.length()).thenReturn(1L); when(ErrorLogHelper.getStoredThrowableFile(any(UUID.class))).thenReturn(throwableFile).thenReturn(null); when(ErrorLogHelper.getErrorReportFromErrorLog(mErrorLog, EXCEPTION)).thenReturn(errorReport); - when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(EXCEPTION); + when(FileManager.readObject(any(File.class))).thenReturn(EXCEPTION); Crashes crashes = Crashes.getInstance(); ErrorReport report = crashes.buildErrorReport(mErrorLog); @@ -889,7 +889,7 @@ public void buildErrorReportError() throws IOException, ClassNotFoundException { Exception classNotFoundException = mock(ClassNotFoundException.class); Exception ioException = mock(IOException.class); - when(StorageHelper.InternalStorage.readObject(any(File.class))).thenThrow(classNotFoundException).thenThrow(ioException); + when(FileManager.readObject(any(File.class))).thenThrow(classNotFoundException).thenThrow(ioException); Crashes crashes = Crashes.getInstance(); @@ -995,8 +995,8 @@ public void crashInLastSession() throws JSONException, IOException, ClassNotFoun when(ErrorLogHelper.getErrorReportFromErrorLog(errorLog, throwable)).thenReturn(errorReport); when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{lastErrorLogFile}); when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); - when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn(""); - when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(throwable); + when(FileManager.read(any(File.class))).thenReturn(""); + when(FileManager.readObject(any(File.class))).thenReturn(throwable); Crashes crashes = Crashes.getInstance(); crashes.setLogSerializer(logSerializer); @@ -1063,7 +1063,7 @@ public void failToDeserializeLastSessionCrashReport() throws JSONException, IOEx when(ErrorLogHelper.getLastErrorLogFile()).thenReturn(lastErrorLogFile); when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{lastErrorLogFile}); when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); - when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn(""); + when(FileManager.read(any(File.class))).thenReturn(""); Crashes crashes = Crashes.getInstance(); crashes.setLogSerializer(logSerializer); @@ -1151,8 +1151,8 @@ public void sendMoreThan2ErrorAttachments() throws IOException, ClassNotFoundExc when(ErrorLogHelper.getStoredThrowableFile(any(UUID.class))).thenReturn(mock(File.class)); when(ErrorLogHelper.getErrorReportFromErrorLog(any(ManagedErrorLog.class), any(Throwable.class))).thenReturn(new ErrorReport()); - when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn(""); - when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(mock(Throwable.class)); + when(FileManager.read(any(File.class))).thenReturn(""); + when(FileManager.readObject(any(File.class))).thenReturn(mock(Throwable.class)); Crashes crashes = Crashes.getInstance(); crashes.setInstanceListener(listener); @@ -1180,8 +1180,8 @@ public void manualProcessing() throws Exception { when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); when(ErrorLogHelper.getStoredThrowableFile(any(UUID.class))).thenReturn(mock(File.class)); when(ErrorLogHelper.getErrorReportFromErrorLog(any(ManagedErrorLog.class), any(Throwable.class))).thenReturn(report1).thenReturn(report2); - when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn(""); - when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(new RuntimeException()); + when(FileManager.read(any(File.class))).thenReturn(""); + when(FileManager.readObject(any(File.class))).thenReturn(new RuntimeException()); LogSerializer logSerializer = mock(LogSerializer.class); when(logSerializer.deserializeLog(anyString(), anyString())).thenAnswer(new Answer() { @@ -1288,8 +1288,8 @@ public void manuallyReportNullList() throws Exception { when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); when(ErrorLogHelper.getStoredThrowableFile(any(UUID.class))).thenReturn(mock(File.class)); when(ErrorLogHelper.getErrorReportFromErrorLog(any(ManagedErrorLog.class), any(Throwable.class))).thenReturn(report1).thenReturn(report2); - when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn(""); - when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(new RuntimeException()); + when(FileManager.read(any(File.class))).thenReturn(""); + when(FileManager.readObject(any(File.class))).thenReturn(new RuntimeException()); LogSerializer logSerializer = mock(LogSerializer.class); when(logSerializer.deserializeLog(anyString(), anyString())).thenAnswer(new Answer() { @@ -1354,8 +1354,8 @@ private ManagedErrorLog testNativeCrashLog(long appStartTime, long crashTime, bo when(ErrorLogHelper.getPendingMinidumpDirectory()).thenReturn(pendingDir); when(ErrorLogHelper.getStoredThrowableFile(any(UUID.class))).thenReturn(mock(File.class)); when(ErrorLogHelper.getErrorReportFromErrorLog(any(ManagedErrorLog.class), any(Throwable.class))).thenReturn(report); - when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn(""); - when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(new NativeException()); + when(FileManager.read(any(File.class))).thenReturn(""); + when(FileManager.readObject(any(File.class))).thenReturn(new NativeException()); LogSerializer logSerializer = mock(LogSerializer.class); ArgumentCaptor log = ArgumentCaptor.forClass(Log.class); when(logSerializer.serializeLog(log.capture())).thenReturn("{}"); diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/UncaughtExceptionHandlerTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/UncaughtExceptionHandlerTest.java index eecc78541d..c778b7b735 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/UncaughtExceptionHandlerTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/UncaughtExceptionHandlerTest.java @@ -15,8 +15,8 @@ import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.ShutdownHelper; import com.microsoft.appcenter.utils.async.AppCenterFuture; +import com.microsoft.appcenter.utils.storage.FileManager; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import com.microsoft.appcenter.utils.storage.StorageHelper; import org.json.JSONException; import org.junit.Before; @@ -53,7 +53,7 @@ import static org.powermock.api.mockito.PowerMockito.when; @SuppressWarnings("unused") -@PrepareForTest({SystemClock.class, SharedPreferencesManager.class, StorageHelper.InternalStorage.class, Crashes.class, ErrorLogHelper.class, DeviceInfoHelper.class, ShutdownHelper.class, AppCenterLog.class, AppCenter.class, HandlerUtils.class}) +@PrepareForTest({SystemClock.class, SharedPreferencesManager.class, FileManager.class, Crashes.class, ErrorLogHelper.class, DeviceInfoHelper.class, ShutdownHelper.class, AppCenterLog.class, AppCenter.class, HandlerUtils.class}) public class UncaughtExceptionHandlerTest { private static final String CRASHES_ENABLED_KEY = KEY_ENABLED + "_" + Crashes.getInstance().getServiceName(); @@ -71,7 +71,7 @@ public void setUp() { mockStatic(AppCenter.class); mockStatic(AppCenterLog.class); mockStatic(SystemClock.class); - mockStatic(StorageHelper.InternalStorage.class); + mockStatic(FileManager.class); mockStatic(SharedPreferencesManager.class); mockStatic(ErrorLogHelper.class); mockStatic(DeviceInfoHelper.class); @@ -210,8 +210,8 @@ public void testIOException() throws java.lang.Exception { mExceptionHandler.register(); IOException ioException = new IOException("Fake IO exception"); - PowerMockito.doThrow(ioException).when(StorageHelper.InternalStorage.class); - StorageHelper.InternalStorage.write(any(File.class), anyString()); + PowerMockito.doThrow(ioException).when(FileManager.class); + FileManager.write(any(File.class), anyString()); final Thread thread = Thread.currentThread(); final RuntimeException exception = new RuntimeException(); diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java index 2183425cc0..4205263860 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java @@ -12,8 +12,8 @@ import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.async.AppCenterFuture; +import com.microsoft.appcenter.utils.storage.FileManager; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import com.microsoft.appcenter.utils.storage.StorageHelper; import org.json.JSONException; import org.junit.Before; @@ -53,7 +53,7 @@ import static org.powermock.api.mockito.PowerMockito.when; import static org.powermock.api.mockito.PowerMockito.whenNew; -@PrepareForTest({AppCenter.class, WrapperSdkExceptionManager.class, AppCenterLog.class, SharedPreferencesManager.class, StorageHelper.InternalStorage.class, Crashes.class, ErrorLogHelper.class, HandlerUtils.class}) +@PrepareForTest({AppCenter.class, WrapperSdkExceptionManager.class, AppCenterLog.class, SharedPreferencesManager.class, FileManager.class, Crashes.class, ErrorLogHelper.class, HandlerUtils.class}) public class WrapperSdkExceptionManagerTest { private static final String CRASHES_ENABLED_KEY = KEY_ENABLED + "_" + Crashes.getInstance().getServiceName(); @@ -68,7 +68,7 @@ public class WrapperSdkExceptionManagerTest { public void setUp() { Crashes.unsetInstance(); mockStatic(AppCenter.class); - mockStatic(StorageHelper.InternalStorage.class); + mockStatic(FileManager.class); mockStatic(SharedPreferencesManager.class); mockStatic(AppCenterLog.class); mockStatic(ErrorLogHelper.class); @@ -111,11 +111,11 @@ public void loadWrapperExceptionData() throws java.lang.Exception { File file = mock(File.class); whenNew(File.class).withAnyArguments().thenReturn(file); when(file.exists()).thenReturn(true); - doThrow(new IOException()).when(StorageHelper.InternalStorage.class); - StorageHelper.InternalStorage.readObject(any(File.class)); + doThrow(new IOException()).when(FileManager.class); + FileManager.readObject(any(File.class)); assertNull(WrapperSdkExceptionManager.loadWrapperExceptionData(UUID.randomUUID())); - doThrow(new ClassNotFoundException()).when(StorageHelper.InternalStorage.class); - StorageHelper.InternalStorage.readObject(any(File.class)); + doThrow(new ClassNotFoundException()).when(FileManager.class); + FileManager.readObject(any(File.class)); assertNull(WrapperSdkExceptionManager.loadWrapperExceptionData(UUID.randomUUID())); assertNull(WrapperSdkExceptionManager.loadWrapperExceptionData(null)); } @@ -126,7 +126,7 @@ public void deleteWrapperExceptionDataWithNullId() { /* Delete null does nothing. */ WrapperSdkExceptionManager.deleteWrapperExceptionData(null); verifyStatic(never()); - StorageHelper.InternalStorage.delete(any(File.class)); + FileManager.delete(any(File.class)); verifyStatic(); AppCenterLog.error(eq(Crashes.LOG_TAG), anyString()); } @@ -137,7 +137,7 @@ public void deleteWrapperExceptionDataWithMissingId() { /* Delete with file not found does nothing. */ WrapperSdkExceptionManager.deleteWrapperExceptionData(UUID.randomUUID()); verifyStatic(never()); - StorageHelper.InternalStorage.delete(any(File.class)); + FileManager.delete(any(File.class)); verifyStatic(never()); AppCenterLog.error(eq(Crashes.LOG_TAG), anyString()); } @@ -151,7 +151,7 @@ public void deleteWrapperExceptionDataWithLoadingError() throws java.lang.Except when(file.exists()).thenReturn(true); WrapperSdkExceptionManager.deleteWrapperExceptionData(UUID.randomUUID()); verifyStatic(); - StorageHelper.InternalStorage.delete(any(File.class)); + FileManager.delete(any(File.class)); verifyStatic(); AppCenterLog.error(eq(Crashes.LOG_TAG), anyString()); } @@ -164,13 +164,13 @@ public void saveWrapperSdkCrash() throws JSONException, IOException { byte[] data = new byte[]{'d'}; WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), null, new Exception(), data); verifyStatic(); - StorageHelper.InternalStorage.writeObject(any(File.class), eq(data)); + FileManager.writeObject(any(File.class), eq(data)); /* We can't do it twice in the same process. */ data = new byte[]{'e'}; WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), null, new Exception(), data); verifyStatic(never()); - StorageHelper.InternalStorage.writeObject(any(File.class), eq(data)); + FileManager.writeObject(any(File.class), eq(data)); } @Test @@ -182,17 +182,17 @@ public void saveWrapperSdkCrashWithJavaThrowable() throws JSONException, IOExcep Throwable throwable = new Throwable(); WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), throwable, new Exception(), data); verifyStatic(); - StorageHelper.InternalStorage.writeObject(any(File.class), eq(data)); + FileManager.writeObject(any(File.class), eq(data)); verifyStatic(); - StorageHelper.InternalStorage.writeObject(any(File.class), eq(throwable)); + FileManager.writeObject(any(File.class), eq(throwable)); /* We can't do it twice in the same process. */ data = new byte[]{'e'}; WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), throwable, new Exception(), data); verifyStatic(never()); - StorageHelper.InternalStorage.writeObject(any(File.class), eq(data)); + FileManager.writeObject(any(File.class), eq(data)); verifyStatic(); - StorageHelper.InternalStorage.writeObject(any(File.class), eq(throwable)); + FileManager.writeObject(any(File.class), eq(throwable)); } @Test @@ -203,14 +203,14 @@ public void saveWrapperSdkCrashWithOnlyJavaThrowable() throws JSONException, IOE Throwable throwable = new Throwable(); WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), throwable, new Exception(), null); verifyStatic(never()); - StorageHelper.InternalStorage.writeObject(any(File.class), isNull(byte[].class)); + FileManager.writeObject(any(File.class), isNull(byte[].class)); verifyStatic(); - StorageHelper.InternalStorage.writeObject(any(File.class), eq(throwable)); + FileManager.writeObject(any(File.class), eq(throwable)); /* We can't do it twice in the same process. */ WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), throwable, new Exception(), null); verifyStatic(); - StorageHelper.InternalStorage.writeObject(any(File.class), eq(throwable)); + FileManager.writeObject(any(File.class), eq(throwable)); } @Test @@ -284,8 +284,8 @@ public boolean matches(Object argument) { @Test public void saveWrapperSdkCrashFailsWithIOException() throws IOException, JSONException { - doThrow(new IOException()).when(StorageHelper.InternalStorage.class); - StorageHelper.InternalStorage.write(any(File.class), anyString()); + doThrow(new IOException()).when(FileManager.class); + FileManager.write(any(File.class), anyString()); LogSerializer logSerializer = Mockito.mock(LogSerializer.class); when(logSerializer.serializeLog(any(ManagedErrorLog.class))).thenReturn("mock"); Crashes.getInstance().setLogSerializer(logSerializer); @@ -316,8 +316,8 @@ public boolean matches(Object argument) { @Test public void saveWrapperSdkCrashFailsWithIOExceptionAfterLog() throws IOException, JSONException { byte[] data = {'d'}; - doThrow(new IOException()).when(StorageHelper.InternalStorage.class); - StorageHelper.InternalStorage.writeObject(any(File.class), eq(data)); + doThrow(new IOException()).when(FileManager.class); + FileManager.writeObject(any(File.class), eq(data)); LogSerializer logSerializer = Mockito.mock(LogSerializer.class); when(logSerializer.serializeLog(any(ManagedErrorLog.class))).thenReturn("mock"); Crashes.getInstance().setLogSerializer(logSerializer); diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index bd0762e756..fd00b0578f 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -24,8 +24,8 @@ import com.microsoft.appcenter.persistence.Persistence.PersistenceException; import com.microsoft.appcenter.utils.crypto.CryptoUtils; import com.microsoft.appcenter.utils.storage.DatabaseManager; +import com.microsoft.appcenter.utils.storage.FileManager; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import com.microsoft.appcenter.utils.storage.StorageHelper; import org.json.JSONException; import org.junit.Before; @@ -78,7 +78,7 @@ public class DatabasePersistenceAndroidTest { public static void setUpClass() { AppCenter.setLogLevel(android.util.Log.VERBOSE); sContext = InstrumentationRegistry.getTargetContext(); - StorageHelper.initialize(sContext); + FileManager.initialize(sContext); SharedPreferencesManager.initialize(sContext); Constants.loadFromContext(sContext); } @@ -170,7 +170,7 @@ public void putLargeLogAndDeleteAll() throws PersistenceException { /* Verify large file. */ File file = persistence.getLargePayloadFile(persistence.getLargePayloadGroupDirectory("test-p1"), id); assertNotNull(file); - String fileLog = StorageHelper.InternalStorage.read(file); + String fileLog = FileManager.read(file); assertNotNull(fileLog); assertTrue(fileLog.length() >= size); @@ -259,7 +259,7 @@ public void putLargeLogFailsToRead() throws PersistenceException { /* Verify large file. */ File file = persistence.getLargePayloadFile(persistence.getLargePayloadGroupDirectory("test-p1"), id); assertNotNull(file); - String fileLog = StorageHelper.InternalStorage.read(file); + String fileLog = FileManager.read(file); assertNotNull(fileLog); assertTrue(fileLog.length() >= size); diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java index 72d2a2235a..b7eee2c752 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java @@ -26,7 +26,6 @@ import java.util.Set; import java.util.concurrent.TimeUnit; -import static com.microsoft.appcenter.utils.storage.StorageHelper.InternalStorage; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -63,12 +62,12 @@ public class StorageHelperAndroidTest { @BeforeClass public static void setUpClass() { sContext = InstrumentationRegistry.getTargetContext(); - StorageHelper.initialize(sContext); + FileManager.initialize(sContext); SharedPreferencesManager.initialize(sContext); sAndroidFilesPath = sContext.getFilesDir().getAbsolutePath() + "/test/"; /* Create a test directory. */ - InternalStorage.mkdir(sAndroidFilesPath); + FileManager.mkdir(sAndroidFilesPath); } @AfterClass @@ -92,14 +91,14 @@ public boolean accept(File dir, String filename) { } }; - String[] filenames = InternalStorage.getFilenames(sAndroidFilesPath, filter); + String[] filenames = FileManager.getFilenames(sAndroidFilesPath, filter); /* Delete the files to clean up. */ for (String filename : filenames) { - InternalStorage.delete(sAndroidFilesPath + filename); + FileManager.delete(sAndroidFilesPath + filename); } - InternalStorage.delete(sAndroidFilesPath); + FileManager.delete(sAndroidFilesPath); } private static SharedPreferencesTestData[] generateSharedPreferenceData() throws NoSuchMethodException { @@ -226,16 +225,16 @@ public boolean accept(File dir, String filename) { /* Write contents to test files after 2 sec delay. */ Log.i(TAG, "Writing " + filename1); - InternalStorage.write(sAndroidFilesPath + filename1, contents1); + FileManager.write(sAndroidFilesPath + filename1, contents1); TimeUnit.SECONDS.sleep(2); Log.i(TAG, "Writing " + filename2); - InternalStorage.write(sAndroidFilesPath + filename2, contents2); + FileManager.write(sAndroidFilesPath + filename2, contents2); /* Also write empty content to a test file. */ - InternalStorage.write(sAndroidFilesPath + filename3, ""); - InternalStorage.write(sAndroidFilesPath + filename4, " "); + FileManager.write(sAndroidFilesPath + filename3, ""); + FileManager.write(sAndroidFilesPath + filename4, " "); /* Get file names in the root path. */ - String[] filenames = InternalStorage.getFilenames(sAndroidFilesPath, filter); + String[] filenames = FileManager.getFilenames(sAndroidFilesPath, filter); /* Verify the files are created. */ assertNotNull(filenames); @@ -247,14 +246,14 @@ public boolean accept(File dir, String filename) { assertFalse(list.contains(filename4)); /* Get the most recent file. */ - File lastModifiedFile = InternalStorage.lastModifiedFile(sAndroidFilesPath, filter); + File lastModifiedFile = FileManager.lastModifiedFile(sAndroidFilesPath, filter); /* Verify the most recent file. */ assertNotNull(lastModifiedFile); assertEquals(filename2, lastModifiedFile.getName()); /* Read the most recent file. */ - String actual = InternalStorage.read(lastModifiedFile); + String actual = FileManager.read(lastModifiedFile); /* Verify the contents of the most recent file. */ assertNotNull(actual); @@ -263,16 +262,16 @@ public boolean accept(File dir, String filename) { /* Delete the files to clean up. */ for (String filename : filenames) { Log.i(TAG, "Deleting " + filename); - assertTrue(InternalStorage.delete(sAndroidFilesPath + filename)); + assertTrue(FileManager.delete(sAndroidFilesPath + filename)); } /* Verify all the files are properly deleted. */ - assertEquals(0, InternalStorage.getFilenames(sAndroidFilesPath, filter).length); + assertEquals(0, FileManager.getFilenames(sAndroidFilesPath, filter).length); /* Verify invalid accesses. */ - assertNull(InternalStorage.read("not-exist-filename")); - assertArrayEquals(new String[0], InternalStorage.getFilenames("not-exist-path", null)); - assertNull(InternalStorage.lastModifiedFile("not-exist-path", null)); + assertNull(FileManager.read("not-exist-filename")); + assertArrayEquals(new String[0], FileManager.getFilenames("not-exist-path", null)); + assertNull(FileManager.lastModifiedFile("not-exist-path", null)); } @Test @@ -285,16 +284,16 @@ public void internalStorageForObject() throws IOException, ClassNotFoundExceptio DataModel model = new DataModel(10, "Model", true); /* Write the object to a file. */ - InternalStorage.writeObject(file, model); + FileManager.writeObject(file, model); /* Read the file. */ - DataModel actual = InternalStorage.readObject(file); + DataModel actual = FileManager.readObject(file); /* Read with class cast exception. */ Exception readCastException = null; try { @SuppressWarnings("UnusedAssignment") - String wrongType = InternalStorage.readObject(file); + String wrongType = FileManager.readObject(file); } catch (Exception e) { readCastException = e; } @@ -308,7 +307,7 @@ public void internalStorageForObject() throws IOException, ClassNotFoundExceptio /* Delete the files to clean up. */ Log.i(TAG, "Deleting " + file.getName()); - InternalStorage.delete(file); + FileManager.delete(file); } @Test @@ -319,10 +318,10 @@ public void internalStorageForBytes() throws IOException { /* Create a mock object. */ String hello = "Hello world"; - StorageHelper.InternalStorage.write(file, hello); + FileManager.write(file, hello); /* Read the file as bytes. */ - byte[] helloBytes = StorageHelper.InternalStorage.readBytes(file); + byte[] helloBytes = FileManager.readBytes(file); /* Check. */ assertNotNull(helloBytes); @@ -330,10 +329,10 @@ public void internalStorageForBytes() throws IOException { /* Delete the files to clean up. */ Log.i(TAG, "Deleting " + file.getName()); - InternalStorage.delete(file); + FileManager.delete(file); /* Check file not found. */ - assertNull(StorageHelper.InternalStorage.readBytes(file)); + assertNull(FileManager.readBytes(file)); } /** diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java index 74a90e5ccc..9f14cf8289 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java @@ -29,8 +29,9 @@ import com.microsoft.appcenter.utils.PrefStorageConstants; import com.microsoft.appcenter.utils.async.AppCenterFuture; import com.microsoft.appcenter.utils.async.DefaultAppCenterFuture; +import com.microsoft.appcenter.utils.storage.FileManager; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.FileManager; import java.util.ArrayList; import java.util.Collection; @@ -706,7 +707,7 @@ private void finishConfiguration(boolean configureFromApp) { Constants.loadFromContext(mApplication); /* If parameters are valid, init context related resources. */ - StorageHelper.initialize(mApplication); + FileManager.initialize(mApplication); SharedPreferencesManager.initialize(mApplication); /* Initialize session storage. */ diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index 2c9af3f1b0..8c136226fc 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -16,7 +16,7 @@ import com.microsoft.appcenter.utils.UUIDUtils; import com.microsoft.appcenter.utils.crypto.CryptoUtils; import com.microsoft.appcenter.utils.storage.DatabaseManager; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.FileManager; import org.json.JSONException; @@ -239,7 +239,7 @@ public long putLog(@NonNull String group, @NonNull Log log) throws PersistenceEx directory.mkdir(); File payloadFile = getLargePayloadFile(directory, databaseId); try { - StorageHelper.InternalStorage.write(payloadFile, payload); + FileManager.write(payloadFile, payload); } catch (IOException e) { /* Remove database entry if we cannot save payload as a file. */ @@ -386,7 +386,7 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT if (databasePayload == null) { File file = getLargePayloadFile(largePayloadGroupDirectory, dbIdentifier); AppCenterLog.debug(LOG_TAG, "Read payload file " + file); - logPayload = StorageHelper.InternalStorage.read(file); + logPayload = FileManager.read(file); if (logPayload == null) { throw new JSONException("Log payload is null and not stored as a file."); } diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/FileManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/FileManager.java new file mode 100644 index 0000000000..56345eb58c --- /dev/null +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/FileManager.java @@ -0,0 +1,280 @@ +package com.microsoft.appcenter.utils.storage; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; + +import com.microsoft.appcenter.AppCenter; +import com.microsoft.appcenter.utils.AppCenterLog; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * FileManager Helper class + */ +public class FileManager { + + /** + * Application context instance. + */ + @SuppressLint("StaticFieldLeak") + private static Context sContext; + + /** + * Initializes StorageHelper class. + * + * @param context The context of the application. + */ + public static synchronized void initialize(Context context) { + if (sContext == null) { + sContext = context; + } + } + + /** + * Read contents from a file. + * + * @param path The path of the file. + * @return The contents of the file. + */ + @SuppressWarnings("SameParameterValue") + public static String read(@NonNull String path) { + return read(new File(path)); + } + + /** + * Read contents from a file. + * + * @param file The file to read from. + * @return The contents of the file. + */ + public static String read(@NonNull File file) { + try { + BufferedReader reader = new BufferedReader(new FileReader(file)); + StringBuilder contents; + + //noinspection TryFinallyCanBeTryWithResources (requires min API level 19) + try { + String line; + String lineSeparator = System.getProperty("line.separator"); + contents = new StringBuilder(); + while ((line = reader.readLine()) != null) { + contents.append(line).append(lineSeparator); + } + } finally { + + //noinspection ThrowFromFinallyBlock + reader.close(); + } + return contents.toString(); + } catch (IOException e) { + AppCenterLog.error(AppCenter.LOG_TAG, "Could not read file " + file.getAbsolutePath(), e); + } + return null; + } + + /** + * Read contents from a file into byte array. + * + * @param file The file to read from. + * @return The contents of the file. + */ + public static byte[] readBytes(@NonNull File file) { + byte fileContents[] = new byte[(int) file.length()]; + try { + FileInputStream fileStream = new FileInputStream(file); + + //noinspection TryFinallyCanBeTryWithResources + try { + DataInputStream dataInputStream = new DataInputStream(fileStream); + dataInputStream.readFully(fileContents); + return fileContents; + } finally { + fileStream.close(); + } + } catch (IOException e) { + AppCenterLog.error(AppCenter.LOG_TAG, "Could not read file " + file.getAbsolutePath(), e); + } + return null; + } + + /** + * Write contents to a file. + * + * @param path The path of the file. + * @param contents The contents to be written to the file. + * @throws IOException If an I/O error occurs + */ + public static void write(@NonNull String path, @NonNull String contents) throws IOException { + write(new File(path), contents); + } + + /** + * Write contents to a file. + * + * @param file The file instance. + * @param contents The content to be written to the file. Must not be empty or whitespace only. + * @throws IOException If an I/O error occurs + */ + public static void write(@NonNull File file, @NonNull String contents) throws IOException { + if (TextUtils.isEmpty(contents) || TextUtils.getTrimmedLength(contents) <= 0) { + return; + } + BufferedWriter writer = new BufferedWriter(new FileWriter(file)); + //noinspection TryFinallyCanBeTryWithResources + try { + writer.write(contents); + } finally { + //noinspection ThrowFromFinallyBlock + writer.close(); + } + } + + /** + * Read an object from a file (deserialization). + * + * @param file The file to read from. + * @param A type for the deserialized instance. + * @return The deserialized instance. + * @throws IOException If an I/O error occurs + * @throws ClassNotFoundException If no class definition found for serialized instance. + */ + @SuppressWarnings("unchecked") + public static T readObject(@NonNull File file) + throws IOException, ClassNotFoundException { + ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file)); + //noinspection TryFinallyCanBeTryWithResources + try { + return (T) inputStream.readObject(); + } finally { + //noinspection ThrowFromFinallyBlock + inputStream.close(); + } + } + + /** + * Write an object to a file (serialization). + * + * @param file The file to write to. + * @param object The object to be written to the file. + * @param A type for the object. + * @throws IOException If an I/O error occurs + */ + public static void writeObject(@NonNull File file, @NonNull T object) throws IOException { + ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(file)); + + //noinspection TryFinallyCanBeTryWithResources + try { + outputStream.writeObject(object); + } finally { + + //noinspection ThrowFromFinallyBlock + outputStream.close(); + } + } + + /** + * Get an array of filenames in the path. + * + * @param path The directory path. + * @param filter The filter to match file names against, may be {@code null}. + * @return An array of filename that doesn't include paths. + */ + @SuppressWarnings("WeakerAccess") + @NonNull + public static String[] getFilenames(@NonNull String path, @Nullable FilenameFilter filter) { + File dir = new File(path); + if (dir.exists()) { + return dir.list(filter); + } + + return new String[0]; + } + + /** + * Get the most recently modified file in the directory specified. + * + * @param path The directory path. + * @param filter The filter to match file names against, may be {@code null}. + * @return The last modified file in the directory matching the specified filter, if any matches. {@code null} otherwise. + */ + @SuppressWarnings("WeakerAccess") + @Nullable + public static File lastModifiedFile(@NonNull String path, @Nullable FilenameFilter filter) { + return lastModifiedFile(new File(path), filter); + } + + /** + * Get the most recently modified file in the directory specified. + * + * @param dir The directory. + * @param filter The filter to match file names against, may be {@code null}. + * @return The last modified file in the directory matching the specified filter, if any matches. {@code null} otherwise. + */ + @Nullable + public static File lastModifiedFile(@NonNull File dir, @Nullable FilenameFilter filter) { + if (dir.exists()) { + File[] files = dir.listFiles(filter); + long lastModification = 0; + File lastModifiedFile = null; + + if (files != null) { + for (File file : files) { + if (file.lastModified() > lastModification) { + lastModification = file.lastModified(); + lastModifiedFile = file; + } + } + + return lastModifiedFile; + } + } + + return null; + } + + /** + * Delete a file or directory with the given path. + * + * @param path The path of the file or directory. + * @return {@code true} if it was deleted, {@code false} otherwise. + */ + public static boolean delete(@NonNull String path) { + return delete(new File(path)); + } + + /** + * Delete a file or directory. + * + * @param file The file or directory to delete. + * @return {@code true} if it was deleted, {@code false} otherwise. + */ + public static boolean delete(@NonNull File file) { + return file.delete(); + } + + /** + * Create a directory if it does not already exist. + * Will create the whole directory tree if necessary. + * + * @param path An absolute path for the directory to be created. + */ + @SuppressWarnings({"ResultOfMethodCallIgnored", "SpellCheckingInspection"}) + public static void mkdir(@NonNull String path) { + new File(path).mkdirs(); + } +} diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/StorageHelper.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/StorageHelper.java deleted file mode 100644 index 0d748d4b84..0000000000 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/StorageHelper.java +++ /dev/null @@ -1,298 +0,0 @@ -package com.microsoft.appcenter.utils.storage; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.SharedPreferences; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.text.TextUtils; - -import com.microsoft.appcenter.AppCenter; -import com.microsoft.appcenter.utils.AppCenterLog; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.DataInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; - -/** - * StorageHelper class to access local storage. - */ -public class StorageHelper { - - /** - * Name of preferences. - */ - private static final String PREFERENCES_NAME = "AppCenter"; - - /** - * Application context instance. - */ - @SuppressLint("StaticFieldLeak") - private static Context sContext; - - /** - * Android SharedPreferences instance. - */ - private static SharedPreferences sSharedPreferences; - - /** - * Initializes StorageHelper class. - * - * @param context The context of the application. - */ - public static synchronized void initialize(Context context) { - if (sContext == null) { - sContext = context; - sSharedPreferences = sContext.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE); - } - } - - /** - * InternalStorage Helper class - */ - public static class InternalStorage { - - /** - * Read contents from a file. - * - * @param path The path of the file. - * @return The contents of the file. - */ - @SuppressWarnings("SameParameterValue") - public static String read(@NonNull String path) { - return read(new File(path)); - } - - /** - * Read contents from a file. - * - * @param file The file to read from. - * @return The contents of the file. - */ - public static String read(@NonNull File file) { - try { - BufferedReader reader = new BufferedReader(new FileReader(file)); - StringBuilder contents; - - //noinspection TryFinallyCanBeTryWithResources (requires min API level 19) - try { - String line; - String lineSeparator = System.getProperty("line.separator"); - contents = new StringBuilder(); - while ((line = reader.readLine()) != null) { - contents.append(line).append(lineSeparator); - } - } finally { - - //noinspection ThrowFromFinallyBlock - reader.close(); - } - return contents.toString(); - } catch (IOException e) { - AppCenterLog.error(AppCenter.LOG_TAG, "Could not read file " + file.getAbsolutePath(), e); - } - return null; - } - - /** - * Read contents from a file into byte array. - * - * @param file The file to read from. - * @return The contents of the file. - */ - public static byte[] readBytes(@NonNull File file) { - byte fileContents[] = new byte[(int) file.length()]; - try { - FileInputStream fileStream = new FileInputStream(file); - - //noinspection TryFinallyCanBeTryWithResources - try { - DataInputStream dataInputStream = new DataInputStream(fileStream); - dataInputStream.readFully(fileContents); - return fileContents; - } finally { - fileStream.close(); - } - } catch (IOException e) { - AppCenterLog.error(AppCenter.LOG_TAG, "Could not read file " + file.getAbsolutePath(), e); - } - return null; - } - - /** - * Write contents to a file. - * - * @param path The path of the file. - * @param contents The contents to be written to the file. - * @throws IOException If an I/O error occurs - */ - public static void write(@NonNull String path, @NonNull String contents) throws IOException { - write(new File(path), contents); - } - - /** - * Write contents to a file. - * - * @param file The file instance. - * @param contents The content to be written to the file. Must not be empty or whitespace only. - * @throws IOException If an I/O error occurs - */ - public static void write(@NonNull File file, @NonNull String contents) throws IOException { - if (TextUtils.isEmpty(contents) || TextUtils.getTrimmedLength(contents) <= 0) { - return; - } - BufferedWriter writer = new BufferedWriter(new FileWriter(file)); - //noinspection TryFinallyCanBeTryWithResources - try { - writer.write(contents); - } finally { - //noinspection ThrowFromFinallyBlock - writer.close(); - } - } - - /** - * Read an object from a file (deserialization). - * - * @param file The file to read from. - * @param A type for the deserialized instance. - * @return The deserialized instance. - * @throws IOException If an I/O error occurs - * @throws ClassNotFoundException If no class definition found for serialized instance. - */ - @SuppressWarnings("unchecked") - public static T readObject(@NonNull File file) - throws IOException, ClassNotFoundException { - ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file)); - //noinspection TryFinallyCanBeTryWithResources - try { - return (T) inputStream.readObject(); - } finally { - //noinspection ThrowFromFinallyBlock - inputStream.close(); - } - } - - /** - * Write an object to a file (serialization). - * - * @param file The file to write to. - * @param object The object to be written to the file. - * @param A type for the object. - * @throws IOException If an I/O error occurs - */ - public static void writeObject(@NonNull File file, @NonNull T object) throws IOException { - ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(file)); - - //noinspection TryFinallyCanBeTryWithResources - try { - outputStream.writeObject(object); - } finally { - - //noinspection ThrowFromFinallyBlock - outputStream.close(); - } - } - - /** - * Get an array of filenames in the path. - * - * @param path The directory path. - * @param filter The filter to match file names against, may be {@code null}. - * @return An array of filename that doesn't include paths. - */ - @SuppressWarnings("WeakerAccess") - @NonNull - public static String[] getFilenames(@NonNull String path, @Nullable FilenameFilter filter) { - File dir = new File(path); - if (dir.exists()) { - return dir.list(filter); - } - - return new String[0]; - } - - /** - * Get the most recently modified file in the directory specified. - * - * @param path The directory path. - * @param filter The filter to match file names against, may be {@code null}. - * @return The last modified file in the directory matching the specified filter, if any matches. {@code null} otherwise. - */ - @SuppressWarnings("WeakerAccess") - @Nullable - public static File lastModifiedFile(@NonNull String path, @Nullable FilenameFilter filter) { - return lastModifiedFile(new File(path), filter); - } - - /** - * Get the most recently modified file in the directory specified. - * - * @param dir The directory. - * @param filter The filter to match file names against, may be {@code null}. - * @return The last modified file in the directory matching the specified filter, if any matches. {@code null} otherwise. - */ - @Nullable - public static File lastModifiedFile(@NonNull File dir, @Nullable FilenameFilter filter) { - if (dir.exists()) { - File[] files = dir.listFiles(filter); - long lastModification = 0; - File lastModifiedFile = null; - - if (files != null) { - for (File file : files) { - if (file.lastModified() > lastModification) { - lastModification = file.lastModified(); - lastModifiedFile = file; - } - } - - return lastModifiedFile; - } - } - - return null; - } - - /** - * Delete a file or directory with the given path. - * - * @param path The path of the file or directory. - * @return {@code true} if it was deleted, {@code false} otherwise. - */ - public static boolean delete(@NonNull String path) { - return delete(new File(path)); - } - - /** - * Delete a file or directory. - * - * @param file The file or directory to delete. - * @return {@code true} if it was deleted, {@code false} otherwise. - */ - public static boolean delete(@NonNull File file) { - return file.delete(); - } - - /** - * Create a directory if it does not already exist. - * Will create the whole directory tree if necessary. - * - * @param path An absolute path for the directory to be created. - */ - @SuppressWarnings({"ResultOfMethodCallIgnored", "SpellCheckingInspection"}) - public static void mkdir(@NonNull String path) { - new File(path).mkdirs(); - } - } -} \ No newline at end of file diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java index d358cce702..a219e80fa1 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java @@ -20,8 +20,9 @@ import com.microsoft.appcenter.utils.ShutdownHelper; import com.microsoft.appcenter.utils.async.AppCenterFuture; import com.microsoft.appcenter.utils.storage.DatabaseManager; +import com.microsoft.appcenter.utils.storage.FileManager; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.FileManager; import org.junit.After; import org.junit.Before; @@ -59,7 +60,7 @@ Constants.class, AppCenterLog.class, StartServiceLog.class, - StorageHelper.class, + FileManager.class, SharedPreferencesManager.class, IdHelper.class, DeviceInfoHelper.class, @@ -119,7 +120,7 @@ public void setUp() throws Exception { mockStatic(Constants.class); mockStatic(AppCenterLog.class); - mockStatic(StorageHelper.class); + mockStatic(FileManager.class); mockStatic(SharedPreferencesManager.class); mockStatic(IdHelper.class); mockStatic(Thread.class); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java index 0ae4b52aac..1707576dbc 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java @@ -5,8 +5,9 @@ import com.microsoft.appcenter.utils.DeviceInfoHelper; import com.microsoft.appcenter.utils.IdHelper; import com.microsoft.appcenter.utils.PrefStorageConstants; +import com.microsoft.appcenter.utils.storage.FileManager; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.FileManager; import org.junit.Test; @@ -18,8 +19,7 @@ public class InstantiationTest { @Test public void storageHelper() { - new StorageHelper(); - new StorageHelper.InternalStorage(); + new FileManager(); new SharedPreferencesManager(); } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/StorageHelperTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/StorageHelperTest.java index 7bc91aa6ae..ae8fcaa240 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/StorageHelperTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/StorageHelperTest.java @@ -3,7 +3,7 @@ import android.text.TextUtils; import com.microsoft.appcenter.utils.AppCenterLog; -import com.microsoft.appcenter.utils.storage.StorageHelper; +import com.microsoft.appcenter.utils.storage.FileManager; import org.junit.Rule; import org.junit.Test; @@ -39,7 +39,7 @@ import static org.powermock.api.mockito.PowerMockito.whenNew; @SuppressWarnings("unused") -@PrepareForTest({StorageHelper.InternalStorage.class, AppCenterLog.class, TextUtils.class}) +@PrepareForTest({FileManager.class, AppCenterLog.class, TextUtils.class}) public class StorageHelperTest { @Rule @@ -50,7 +50,7 @@ public void readFileNotFound() throws Exception { mockStatic(AppCenterLog.class); FileReader fileReader = mock(FileReader.class, new ThrowsException(new FileNotFoundException())); whenNew(FileReader.class).withAnyArguments().thenReturn(fileReader); - assertNull(StorageHelper.InternalStorage.read(new File(""))); + assertNull(FileManager.read(new File(""))); verify(fileReader).close(); verifyStatic(); AppCenterLog.error(anyString(), anyString(), any(IOException.class)); @@ -64,7 +64,7 @@ public void readError() throws Exception { whenNew(FileReader.class).withAnyArguments().thenReturn(mock(FileReader.class)); when(reader.readLine()).thenReturn("incomplete"); when(reader.readLine()).thenThrow(new EOFException()); - assertNull(StorageHelper.InternalStorage.read(new File(""))); + assertNull(FileManager.read(new File(""))); verify(reader).close(); verifyStatic(); AppCenterLog.error(anyString(), anyString(), any(IOException.class)); @@ -75,7 +75,7 @@ public void readErrorAndCloseError() throws Exception { mockStatic(AppCenterLog.class); FileReader fileReader = mock(FileReader.class, new ThrowsException(new IOException())); whenNew(FileReader.class).withAnyArguments().thenReturn(fileReader); - assertNull(StorageHelper.InternalStorage.read(new File(""))); + assertNull(FileManager.read(new File(""))); verify(fileReader).close(); verifyStatic(); AppCenterLog.error(anyString(), anyString(), any(IOException.class)); @@ -90,7 +90,7 @@ public void writeError() throws Exception { whenNew(BufferedWriter.class).withAnyArguments().thenReturn(writer); whenNew(FileWriter.class).withAnyArguments().thenReturn(mock(FileWriter.class)); doThrow(new IOException("mock")).when(writer).write(anyString()); - StorageHelper.InternalStorage.write(mock(File.class), "test"); + FileManager.write(mock(File.class), "test"); verify(writer).close(); } @@ -100,7 +100,7 @@ public void readObjectError() throws Exception { whenNew(ObjectInputStream.class).withAnyArguments().thenReturn(reader); whenNew(FileInputStream.class).withAnyArguments().thenReturn(mock(FileInputStream.class)); doThrow(new IOException("mock")).when(reader).readObject(); - StorageHelper.InternalStorage.readObject(mock(File.class)); + FileManager.readObject(mock(File.class)); verify(reader).close(); } @@ -110,7 +110,7 @@ public void writeObjectError() throws Exception { whenNew(ObjectOutputStream.class).withAnyArguments().thenReturn(writer); whenNew(FileOutputStream.class).withAnyArguments().thenReturn(mock(FileOutputStream.class)); doThrow(new IOException("mock")).when(writer).writeObject(any()); - StorageHelper.InternalStorage.writeObject(mock(File.class), "test"); + FileManager.writeObject(mock(File.class), "test"); verify(writer).close(); } @@ -121,7 +121,7 @@ public void lastModifiedFile() { when(dir.exists()).thenReturn(true); when(dir.listFiles(any(FilenameFilter.class))).thenReturn(null); - assertNull(StorageHelper.InternalStorage.lastModifiedFile(dir, filter)); + assertNull(FileManager.lastModifiedFile(dir, filter)); File file1 = mock(File.class); File file2 = mock(File.class); @@ -131,7 +131,7 @@ public void lastModifiedFile() { when(file3.lastModified()).thenReturn(3L); when(dir.listFiles(filter)).thenReturn(new File[]{file1, file3, file2}); - assertEquals(file3, StorageHelper.InternalStorage.lastModifiedFile(dir, filter)); + assertEquals(file3, FileManager.lastModifiedFile(dir, filter)); } @Test @@ -142,7 +142,7 @@ public void readBytesError() throws Exception { DataInputStream dataInputStream = mock(DataInputStream.class); whenNew(DataInputStream.class).withAnyArguments().thenReturn(dataInputStream); doThrow(new IOException("mock")).when(dataInputStream).readFully(any(byte[].class)); - assertNull(StorageHelper.InternalStorage.readBytes(new File(""))); + assertNull(FileManager.readBytes(new File(""))); verify(fileInputStream).close(); verifyStatic(); AppCenterLog.error(anyString(), anyString(), any(IOException.class)); @@ -157,7 +157,7 @@ public void readBytesErrorAndCloseError() throws Exception { DataInputStream dataInputStream = mock(DataInputStream.class); whenNew(DataInputStream.class).withAnyArguments().thenReturn(dataInputStream); doThrow(new IOException("mock")).when(dataInputStream).readFully(any(byte[].class)); - assertNull(StorageHelper.InternalStorage.readBytes(new File(""))); + assertNull(FileManager.readBytes(new File(""))); verify(fileInputStream).close(); verifyStatic(); AppCenterLog.error(anyString(), anyString(), any(IOException.class)); From 74b494bb03c7852689cf529fab69efbc9d64b1fd Mon Sep 17 00:00:00 2001 From: Jae Lim Date: Thu, 25 Oct 2018 13:00:01 -0700 Subject: [PATCH 06/68] Refactor storage helper test classes --- ...dTest.java => FileManagerAndroidTest.java} | 164 ++---------------- .../SharedPreferencesManagerAndroidTest.java | 161 +++++++++++++++++ 2 files changed, 176 insertions(+), 149 deletions(-) rename sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/{StorageHelperAndroidTest.java => FileManagerAndroidTest.java} (53%) create mode 100644 sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManagerAndroidTest.java diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/FileManagerAndroidTest.java similarity index 53% rename from sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java rename to sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/FileManagerAndroidTest.java index b7eee2c752..c8f0e9cd34 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/StorageHelperAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/FileManagerAndroidTest.java @@ -5,7 +5,6 @@ import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.util.Log; import com.microsoft.appcenter.utils.UUIDUtils; @@ -18,12 +17,8 @@ import java.io.FilenameFilter; import java.io.IOException; import java.io.Serializable; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.Arrays; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertArrayEquals; @@ -36,17 +31,12 @@ @SuppressWarnings("unused") @SmallTest @RunWith(AndroidJUnit4.class) -public class StorageHelperAndroidTest { - - /** - * Log tag. - */ - private static final String TAG = "StorageHelperTest"; +public class FileManagerAndroidTest { /** * File extension. */ - private static final String INTERNAL_STORAGE_TEST_FILE_EXTENSION = ".stacktrace"; + private static final String FILE_STORAGE_TEST_FILE_EXTENSION = ".stacktrace"; /** * Context instance. @@ -63,7 +53,6 @@ public class StorageHelperAndroidTest { public static void setUpClass() { sContext = InstrumentationRegistry.getTargetContext(); FileManager.initialize(sContext); - SharedPreferencesManager.initialize(sContext); sAndroidFilesPath = sContext.getFilesDir().getAbsolutePath() + "/test/"; /* Create a test directory. */ @@ -73,21 +62,11 @@ public static void setUpClass() { @AfterClass public static void tearDownClass() { - /* Clean up shared preferences. */ - try { - for (SharedPreferencesTestData data : generateSharedPreferenceData()) { - String key = data.value.getClass().getCanonicalName(); - SharedPreferencesManager.remove(key); - } - } catch (NoSuchMethodException ignored) { - /* Ignore exception. */ - } - - /* Clean up internal storage. */ + /* Clean up file storage. */ FilenameFilter filter = new FilenameFilter() { @Override public boolean accept(File dir, String filename) { - return filename.endsWith(INTERNAL_STORAGE_TEST_FILE_EXTENSION); + return filename.endsWith(FILE_STORAGE_TEST_FILE_EXTENSION); } }; @@ -101,134 +80,39 @@ public boolean accept(File dir, String filename) { FileManager.delete(sAndroidFilesPath); } - private static SharedPreferencesTestData[] generateSharedPreferenceData() throws NoSuchMethodException { - SharedPreferencesTestData[] testData = new SharedPreferencesTestData[6]; - - /* boolean */ - testData[0] = new SharedPreferencesTestData(); - testData[0].value = true; - testData[0].defaultValue = false; - testData[0].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getBoolean", String.class); - testData[0].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getBoolean", String.class, boolean.class); - testData[0].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putBoolean", String.class, boolean.class); - - /* float */ - testData[1] = new SharedPreferencesTestData(); - testData[1].value = 111.22f; - testData[1].defaultValue = 0.01f; - testData[1].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getFloat", String.class); - testData[1].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getFloat", String.class, float.class); - testData[1].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putFloat", String.class, float.class); - - /* int */ - testData[2] = new SharedPreferencesTestData(); - testData[2].value = 123; - testData[2].defaultValue = -1; - testData[2].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getInt", String.class); - testData[2].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getInt", String.class, int.class); - testData[2].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putInt", String.class, int.class); - - /* long */ - testData[3] = new SharedPreferencesTestData(); - testData[3].value = 123456789000L; - testData[3].defaultValue = 345L; - testData[3].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getLong", String.class); - testData[3].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getLong", String.class, long.class); - testData[3].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putLong", String.class, long.class); - - /* String */ - testData[4] = new SharedPreferencesTestData(); - testData[4].value = "Hello World"; - testData[4].defaultValue = "Empty"; - testData[4].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getString", String.class); - testData[4].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getString", String.class, String.class); - testData[4].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putString", String.class, String.class); - - /* Set */ - Set data = new HashSet<>(); - data.add("ABC"); - data.add("Hello World"); - data.add("Welcome to the world!"); - Set defaultSet = new HashSet<>(); - defaultSet.add("DEFAULT"); - - testData[5] = new SharedPreferencesTestData(); - testData[5].value = data; - testData[5].defaultValue = defaultSet; - testData[5].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getStringSet", String.class); - testData[5].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getStringSet", String.class, Set.class); - testData[5].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putStringSet", String.class, Set.class); - - return testData; - } - @Test - public void sharedPreferences() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { - Log.i(TAG, "Testing Shared Preference"); - for (SharedPreferencesTestData data : generateSharedPreferenceData()) { - Log.i(TAG, "Get/Put test for type " + data.value.getClass().getSimpleName()); - - /* Put value to shared preferences. */ - String key = data.value.getClass().getCanonicalName(); - data.putMethod.invoke(null, key, data.value); - - /* Get value from shared preferences. */ - Object actual = data.getMethod1.invoke(null, key); - - /* Verify the value is same as assigned. */ - assertEquals(data.value, actual); - - /* Remove key from shared preferences. */ - SharedPreferencesManager.remove(key); - - /* Verify the value equals to default value. */ - assertEquals(data.defaultValue, data.getMethod2.invoke(null, key, data.defaultValue)); - } - - /* Test clear. */ - SharedPreferencesManager.putString("test", "someTest"); - SharedPreferencesManager.putInt("test2", 2); - SharedPreferencesManager.clear(); - assertNull(SharedPreferencesManager.getString("test")); - assertEquals(0, SharedPreferencesManager.getInt("test2")); - } - - @Test - public void internalStorage() throws IOException, InterruptedException { - Log.i(TAG, "Testing Internal Storage file read/write"); - + public void fileManager() throws IOException, InterruptedException { final String prefix = Long.toString(System.currentTimeMillis()); /* Create a mock data. */ - String filename1 = prefix + "-" + UUIDUtils.randomUUID().toString() + INTERNAL_STORAGE_TEST_FILE_EXTENSION; + String filename1 = prefix + "-" + UUIDUtils.randomUUID().toString() + FILE_STORAGE_TEST_FILE_EXTENSION; String contents1 = "java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.isEmpty()' on a null object reference\n" + "at com.microsoft.appcenter.utils.StorageHelperAndroidTest.internalStorage(StorageHelperAndroidTest.java:124)\n" + "at java.lang.reflect.Method.invoke(Native Method)\n" + "at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)"; - String filename2 = prefix + "-" + UUIDUtils.randomUUID().toString() + INTERNAL_STORAGE_TEST_FILE_EXTENSION; + String filename2 = prefix + "-" + UUIDUtils.randomUUID().toString() + FILE_STORAGE_TEST_FILE_EXTENSION; //noinspection SpellCheckingInspection String contents2 = "java.io.FileNotFoundException: 6c1b1c58-1c2f-47d9-8f04-52639c3a804d: open failed: EROFS (Read-only file system)\n" + "at libcore.io.IoBridge.open(IoBridge.java:452)\n" + "at java.io.FileOutputStream.(FileOutputStream.java:87)\n" + "at java.io.FileOutputStream.(FileOutputStream.java:72)\n" + "at java.io.FileWriter.(FileWriter.java:42)"; - String filename3 = prefix + "-" + UUIDUtils.randomUUID().toString() + INTERNAL_STORAGE_TEST_FILE_EXTENSION; - String filename4 = prefix + "-" + UUIDUtils.randomUUID().toString() + INTERNAL_STORAGE_TEST_FILE_EXTENSION; + String filename3 = prefix + "-" + UUIDUtils.randomUUID().toString() + FILE_STORAGE_TEST_FILE_EXTENSION; + String filename4 = prefix + "-" + UUIDUtils.randomUUID().toString() + FILE_STORAGE_TEST_FILE_EXTENSION; /* FilenameFilter to look up files that are created in current test. */ FilenameFilter filter = new FilenameFilter() { @Override public boolean accept(File dir, String filename) { - return filename.startsWith(prefix) && filename.endsWith(INTERNAL_STORAGE_TEST_FILE_EXTENSION); + return filename.startsWith(prefix) && filename.endsWith(FILE_STORAGE_TEST_FILE_EXTENSION); } }; /* Write contents to test files after 2 sec delay. */ - Log.i(TAG, "Writing " + filename1); FileManager.write(sAndroidFilesPath + filename1, contents1); TimeUnit.SECONDS.sleep(2); - Log.i(TAG, "Writing " + filename2); FileManager.write(sAndroidFilesPath + filename2, contents2); + /* Also write empty content to a test file. */ FileManager.write(sAndroidFilesPath + filename3, ""); FileManager.write(sAndroidFilesPath + filename4, " "); @@ -261,7 +145,6 @@ public boolean accept(File dir, String filename) { /* Delete the files to clean up. */ for (String filename : filenames) { - Log.i(TAG, "Deleting " + filename); assertTrue(FileManager.delete(sAndroidFilesPath + filename)); } @@ -275,10 +158,8 @@ public boolean accept(File dir, String filename) { } @Test - public void internalStorageForObject() throws IOException, ClassNotFoundException { - Log.i(TAG, "Testing Internal Storage object serialization"); - - File file = new File(sAndroidFilesPath + UUIDUtils.randomUUID().toString() + INTERNAL_STORAGE_TEST_FILE_EXTENSION); + public void fileManagerForObject() throws IOException, ClassNotFoundException { + File file = new File(sAndroidFilesPath + UUIDUtils.randomUUID().toString() + FILE_STORAGE_TEST_FILE_EXTENSION); /* Create a mock object. */ DataModel model = new DataModel(10, "Model", true); @@ -306,15 +187,12 @@ public void internalStorageForObject() throws IOException, ClassNotFoundExceptio assertEquals(model.object.enabled, actual.object.enabled); /* Delete the files to clean up. */ - Log.i(TAG, "Deleting " + file.getName()); FileManager.delete(file); } @Test - public void internalStorageForBytes() throws IOException { - Log.i(TAG, "Testing Internal Storage bytes read/write"); - - File file = new File(sAndroidFilesPath + UUIDUtils.randomUUID().toString() + INTERNAL_STORAGE_TEST_FILE_EXTENSION); + public void fileManagerForBytes() throws IOException { + File file = new File(sAndroidFilesPath + UUIDUtils.randomUUID().toString() + FILE_STORAGE_TEST_FILE_EXTENSION); /* Create a mock object. */ String hello = "Hello world"; @@ -328,24 +206,12 @@ public void internalStorageForBytes() throws IOException { assertEquals(hello, new String(helloBytes, "UTF-8")); /* Delete the files to clean up. */ - Log.i(TAG, "Deleting " + file.getName()); FileManager.delete(file); /* Check file not found. */ assertNull(FileManager.readBytes(file)); } - /** - * Temporary class for testing shared preferences. - */ - private static class SharedPreferencesTestData { - Object value; - Object defaultValue; - Method getMethod1; - Method getMethod2; - Method putMethod; - } - /** * Temporary class for testing object serialization. */ diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManagerAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManagerAndroidTest.java new file mode 100644 index 0000000000..6e57de59e4 --- /dev/null +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManagerAndroidTest.java @@ -0,0 +1,161 @@ +package com.microsoft.appcenter.utils.storage; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.util.Log; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +@SuppressWarnings("unused") +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SharedPreferencesManagerAndroidTest { + + /** + * Log tag. + */ + private static final String TAG = "StorageHelperTest"; + + /** + * Context instance. + */ + @SuppressLint("StaticFieldLeak") + private static Context sContext; + + @BeforeClass + public static void setUpClass() { + sContext = InstrumentationRegistry.getTargetContext(); + SharedPreferencesManager.initialize(sContext); + } + + @AfterClass + public static void tearDownClass() { + + /* Clean up shared preferences. */ + try { + for (SharedPreferencesTestData data : generateSharedPreferenceData()) { + String key = data.value.getClass().getCanonicalName(); + SharedPreferencesManager.remove(key); + } + } catch (NoSuchMethodException ignored) { + /* Ignore exception. */ + } + } + + private static SharedPreferencesTestData[] generateSharedPreferenceData() throws NoSuchMethodException { + SharedPreferencesTestData[] testData = new SharedPreferencesTestData[6]; + + /* boolean */ + testData[0] = new SharedPreferencesTestData(); + testData[0].value = true; + testData[0].defaultValue = false; + testData[0].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getBoolean", String.class); + testData[0].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getBoolean", String.class, boolean.class); + testData[0].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putBoolean", String.class, boolean.class); + + /* float */ + testData[1] = new SharedPreferencesTestData(); + testData[1].value = 111.22f; + testData[1].defaultValue = 0.01f; + testData[1].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getFloat", String.class); + testData[1].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getFloat", String.class, float.class); + testData[1].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putFloat", String.class, float.class); + + /* int */ + testData[2] = new SharedPreferencesTestData(); + testData[2].value = 123; + testData[2].defaultValue = -1; + testData[2].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getInt", String.class); + testData[2].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getInt", String.class, int.class); + testData[2].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putInt", String.class, int.class); + + /* long */ + testData[3] = new SharedPreferencesTestData(); + testData[3].value = 123456789000L; + testData[3].defaultValue = 345L; + testData[3].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getLong", String.class); + testData[3].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getLong", String.class, long.class); + testData[3].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putLong", String.class, long.class); + + /* String */ + testData[4] = new SharedPreferencesTestData(); + testData[4].value = "Hello World"; + testData[4].defaultValue = "Empty"; + testData[4].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getString", String.class); + testData[4].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getString", String.class, String.class); + testData[4].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putString", String.class, String.class); + + /* Set */ + Set data = new HashSet<>(); + data.add("ABC"); + data.add("Hello World"); + data.add("Welcome to the world!"); + Set defaultSet = new HashSet<>(); + defaultSet.add("DEFAULT"); + + testData[5] = new SharedPreferencesTestData(); + testData[5].value = data; + testData[5].defaultValue = defaultSet; + testData[5].getMethod1 = SharedPreferencesManager.class.getDeclaredMethod("getStringSet", String.class); + testData[5].getMethod2 = SharedPreferencesManager.class.getDeclaredMethod("getStringSet", String.class, Set.class); + testData[5].putMethod = SharedPreferencesManager.class.getDeclaredMethod("putStringSet", String.class, Set.class); + + return testData; + } + + @Test + public void sharedPreferences() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { + Log.i(TAG, "Testing Shared Preference"); + for (SharedPreferencesTestData data : generateSharedPreferenceData()) { + Log.i(TAG, "Get/Put test for type " + data.value.getClass().getSimpleName()); + + /* Put value to shared preferences. */ + String key = data.value.getClass().getCanonicalName(); + data.putMethod.invoke(null, key, data.value); + + /* Get value from shared preferences. */ + Object actual = data.getMethod1.invoke(null, key); + + /* Verify the value is same as assigned. */ + assertEquals(data.value, actual); + + /* Remove key from shared preferences. */ + SharedPreferencesManager.remove(key); + + /* Verify the value equals to default value. */ + assertEquals(data.defaultValue, data.getMethod2.invoke(null, key, data.defaultValue)); + } + + /* Test clear. */ + SharedPreferencesManager.putString("test", "someTest"); + SharedPreferencesManager.putInt("test2", 2); + SharedPreferencesManager.clear(); + assertNull(SharedPreferencesManager.getString("test")); + assertEquals(0, SharedPreferencesManager.getInt("test2")); + } + + /** + * Temporary class for testing shared preferences. + */ + private static class SharedPreferencesTestData { + Object value; + Object defaultValue; + Method getMethod1; + Method getMethod2; + Method putMethod; + } +} \ No newline at end of file From badcc4cd933dcacb5aa13bc35180aed7903332c8 Mon Sep 17 00:00:00 2001 From: Jae Lim Date: Thu, 25 Oct 2018 13:05:21 -0700 Subject: [PATCH 07/68] Clean up after refactoring --- .../storage/SharedPreferencesManagerAndroidTest.java | 8 -------- .../microsoft/appcenter/utils/storage/FileManager.java | 4 ++-- .../appcenter/utils/storage/SharedPreferencesManager.java | 4 ++-- .../{StorageHelperTest.java => FileManagerTest.java} | 2 +- .../java/com/microsoft/appcenter/InstantiationTest.java | 7 +++++-- .../channel/DefaultChannelOtherOperationsTest.java | 2 +- .../appcenter/persistence/DatabasePersistenceTest.java | 2 +- 7 files changed, 12 insertions(+), 17 deletions(-) rename sdk/appcenter/src/test/java/com/microsoft/appcenter/{StorageHelperTest.java => FileManagerTest.java} (99%) diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManagerAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManagerAndroidTest.java index 6e57de59e4..56613092b6 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManagerAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManagerAndroidTest.java @@ -5,7 +5,6 @@ import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.util.Log; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -25,11 +24,6 @@ @RunWith(AndroidJUnit4.class) public class SharedPreferencesManagerAndroidTest { - /** - * Log tag. - */ - private static final String TAG = "StorageHelperTest"; - /** * Context instance. */ @@ -119,9 +113,7 @@ private static SharedPreferencesTestData[] generateSharedPreferenceData() throws @Test public void sharedPreferences() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { - Log.i(TAG, "Testing Shared Preference"); for (SharedPreferencesTestData data : generateSharedPreferenceData()) { - Log.i(TAG, "Get/Put test for type " + data.value.getClass().getSimpleName()); /* Put value to shared preferences. */ String key = data.value.getClass().getCanonicalName(); diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/FileManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/FileManager.java index 56345eb58c..02503ff2b7 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/FileManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/FileManager.java @@ -24,7 +24,7 @@ import java.io.Serializable; /** - * FileManager Helper class + * File manager for internal/external storage access */ public class FileManager { @@ -35,7 +35,7 @@ public class FileManager { private static Context sContext; /** - * Initializes StorageHelper class. + * Initializes FileManager class. * * @param context The context of the application. */ diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManager.java index e5c0b35734..bae09c763b 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManager.java @@ -8,7 +8,7 @@ import java.util.Set; /** - * SharedPreferencesManager Helper class + * Shared preferences manager */ public class SharedPreferencesManager { @@ -29,7 +29,7 @@ public class SharedPreferencesManager { private static SharedPreferences sSharedPreferences; /** - * Initializes StorageHelper class. + * Initializes SharedPreferencesManager class. * * @param context The context of the application. */ diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/StorageHelperTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/FileManagerTest.java similarity index 99% rename from sdk/appcenter/src/test/java/com/microsoft/appcenter/StorageHelperTest.java rename to sdk/appcenter/src/test/java/com/microsoft/appcenter/FileManagerTest.java index ae8fcaa240..37f9668856 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/StorageHelperTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/FileManagerTest.java @@ -40,7 +40,7 @@ @SuppressWarnings("unused") @PrepareForTest({FileManager.class, AppCenterLog.class, TextUtils.class}) -public class StorageHelperTest { +public class FileManagerTest { @Rule public PowerMockRule rule = new PowerMockRule(); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java index 1707576dbc..851615dace 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java @@ -7,7 +7,6 @@ import com.microsoft.appcenter.utils.PrefStorageConstants; import com.microsoft.appcenter.utils.storage.FileManager; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import com.microsoft.appcenter.utils.storage.FileManager; import org.junit.Test; @@ -18,8 +17,12 @@ public class InstantiationTest { @Test - public void storageHelper() { + public void fileManager() { new FileManager(); + } + + @Test + public void sharedPreferencesManager() { new SharedPreferencesManager(); } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java index 1a48b026ae..bc8ffdabe1 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java @@ -202,7 +202,7 @@ public void groupCallbacks() { @Test public void checkSetStorageSizeForwarding() { - /* The real Android test for checking size is in StorageHelperAndroidTest. */ + /* The real Android test for checking size is in DatabaseManagerAndroidTest. */ Persistence persistence = mock(Persistence.class); when(persistence.setMaxStorageSize(anyLong())).thenReturn(true).thenReturn(false); DefaultChannel channel = new DefaultChannel(mock(Context.class), UUIDUtils.randomUUID().toString(), persistence, mock(Ingestion.class), mAppCenterHandler); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java index 0903cd8464..3a982f7983 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java @@ -267,7 +267,7 @@ public Log answer(InvocationOnMock invocation) { @Test public void checkSetStorageSizeForwarding() throws Exception { - /* The real Android test for checking size is in StorageHelperAndroidTest. */ + /* The real Android test for checking size is in DatabaseManagerAndroidTest. */ DatabaseManager databaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); DatabaseManager.Scanner databaseScanner = mock(DatabaseManager.Scanner.class); From 7754e05449e582b55985aff973a1f92c9c4dcd25 Mon Sep 17 00:00:00 2001 From: Jae Lim Date: Thu, 25 Oct 2018 16:32:57 -0700 Subject: [PATCH 08/68] Refactoring getScanner and getCursor with one unit test disabled --- .../DatabasePersistenceAndroidTest.java | 28 +- .../storage/DatabaseManagerAndroidTest.java | 261 +++++++++--------- .../persistence/DatabasePersistence.java | 45 ++- .../utils/storage/DatabaseManager.java | 135 ++++----- .../appcenter/utils/storage/SQLiteUtils.java | 4 +- .../utils/storage/DatabaseManagerTest.java | 10 +- 6 files changed, 236 insertions(+), 247 deletions(-) diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index fd00b0578f..035d930acd 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -4,6 +4,7 @@ import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteQueryBuilder; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; @@ -25,6 +26,7 @@ import com.microsoft.appcenter.utils.crypto.CryptoUtils; import com.microsoft.appcenter.utils.storage.DatabaseManager; import com.microsoft.appcenter.utils.storage.FileManager; +import com.microsoft.appcenter.utils.storage.SQLiteUtils; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.json.JSONException; @@ -83,13 +85,6 @@ public static void setUpClass() { Constants.loadFromContext(sContext); } - @Before - public void setUp() { - - /* Clean up database. */ - sContext.deleteDatabase(DatabasePersistence.DATABASE); - } - private static int getIteratorSize(Iterator iterator) { int count = 0; for (; iterator.hasNext(); iterator.next()) @@ -97,6 +92,13 @@ private static int getIteratorSize(Iterator iterator) { return count; } + @Before + public void setUp() { + + /* Clean up database. */ + sContext.deleteDatabase(DatabasePersistence.DATABASE); + } + @Test public void putLog() throws PersistenceException { @@ -470,10 +472,14 @@ public void deleteLogs() throws PersistenceException { /* Delete. */ persistence.deleteLogs("", id); + /* Create a query builder for column group. */ + SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); + builder.appendWhere(DatabasePersistence.COLUMN_GROUP + " = ?"); + /* Access DatabaseStorage directly to verify the deletions. */ - DatabaseManager.Scanner scanner1 = persistence.mDatabaseManager.getScanner(DatabasePersistence.COLUMN_GROUP, "test-p1", null, null, false); - DatabaseManager.Scanner scanner2 = persistence.mDatabaseManager.getScanner(DatabasePersistence.COLUMN_GROUP, "test-p2", null, null, false); - DatabaseManager.Scanner scanner3 = persistence.mDatabaseManager.getScanner(DatabasePersistence.COLUMN_GROUP, "test-p3", null, null, false); + DatabaseManager.Scanner scanner1 = persistence.mDatabaseManager.getScanner(builder, new String[]{"test-p1"}, false); + DatabaseManager.Scanner scanner2 = persistence.mDatabaseManager.getScanner(builder, new String[]{"test-p2"}, false); + DatabaseManager.Scanner scanner3 = persistence.mDatabaseManager.getScanner(builder, new String[]{"test-p3"}, false); //noinspection TryFinallyCanBeTryWithResources try { @@ -494,7 +500,7 @@ public void deleteLogs() throws PersistenceException { persistence.deleteLogs("test-p1", id); /* Access DatabaseStorage directly to verify the deletions. */ - DatabaseManager.Scanner scanner4 = persistence.mDatabaseManager.getScanner(DatabasePersistence.COLUMN_GROUP, "test-p1", null, null, false); + DatabaseManager.Scanner scanner4 = persistence.mDatabaseManager.getScanner(builder, new String[]{"test-p1"}, false); //noinspection TryFinallyCanBeTryWithResources try { diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java index 3d7f44fb9b..e0a0880412 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java @@ -4,6 +4,7 @@ import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteQueryBuilder; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -33,11 +34,6 @@ @RunWith(AndroidJUnit4.class) public class DatabaseManagerAndroidTest { - /** - * Log tag. - */ - private static final String TAG = "DatabaseManagerTest"; - /** * Random tool. */ @@ -84,97 +80,135 @@ public static void tearDownClass() { } @SuppressWarnings("SpellCheckingInspection") - private static void runDatabaseManagerTest(DatabaseManager databaseManager) { - ContentValues value1 = generateContentValues(); - ContentValues value2 = generateContentValues(); - ContentValues value3 = generateContentValues(); - - /* Put. */ - Long value1Id = databaseManager.put(value1); - assertNotNull(value1Id); - - /* Put another. */ - Long value2Id = databaseManager.put(value2); - assertNotNull(value2Id); - - /* Generate an ID that is neither value1Id nor value2Id. */ - - /* Get. */ - ContentValues value1FromDatabase = databaseManager.get(value1Id); - assertContentValuesEquals(value1, value1FromDatabase); - ContentValues value2FromDatabase = databaseManager.get(DatabaseManager.PRIMARY_KEY, value2Id); - assertContentValuesEquals(value2, value2FromDatabase); - //noinspection ResourceType - ContentValues nullValueFromDatabase = databaseManager.get(-1); - assertNull(nullValueFromDatabase); - - /* Count with scanner. */ - DatabaseManager.Scanner scanner = databaseManager.getScanner(null, null, null, null, false); - assertEquals(2, scanner.getCount()); - assertEquals(2, scanner.getCount()); - DatabaseManager.Scanner scanner1 = databaseManager.getScanner("COL_STRING", value1.getAsString("COL_STRING"), null, null, false); - assertEquals(1, scanner1.getCount()); - Iterator iterator = scanner1.iterator(); - assertContentValuesEquals(value1, iterator.next()); - assertFalse(iterator.hasNext()); - - /* Null value matching. */ - assertEquals(0, databaseManager.getScanner("COL_STRING", null, null, null, false).getCount()); - assertEquals(2, databaseManager.getScanner("COL_STRING_NULL", null, null, null, false).getCount()); - - /* Test null value filter does not exclude anything, so returns the 2 logs. */ - scanner = databaseManager.getScanner(null, null, "COL_STRING", null, false); - assertEquals(2, scanner.getCount()); - - /* Test filtering only with the second key parameter to get only the second log. */ - scanner = databaseManager.getScanner(null, null, "COL_STRING", Collections.singletonList(value1.getAsString("COL_STRING")), false); - assertEquals(1, scanner.getCount()); - assertContentValuesEquals(value2, scanner.iterator().next()); - - /* Delete. */ - databaseManager.delete(value1Id); - assertNull(databaseManager.get(value1Id)); - assertEquals(1, databaseManager.getRowCount()); - assertEquals(1, databaseManager.getScanner(null, null, null, null, false).getCount()); - - /* Put logs to delete multiple IDs. */ - ContentValues value4 = generateContentValues(); - ContentValues value5 = generateContentValues(); - Long value4Id = databaseManager.put(value4); - Long value5Id = databaseManager.put(value5); - assertNotNull(value4Id); - assertNotNull(value5Id); - - /* Delete multiple logs. */ - databaseManager.delete(Arrays.asList(value4Id, value5Id)); - assertNull(databaseManager.get(value4Id)); - assertNull(databaseManager.get(value5Id)); - assertEquals(1, databaseManager.getRowCount()); - - /* Put logs to delete with condition. */ - ContentValues value6 = generateContentValues(); - ContentValues value7 = generateContentValues(); - value6.put("COL_STRING", value2.getAsString("COL_STRING")); - value7.put("COL_STRING", value2.getAsString("COL_STRING") + "A"); - Long value6Id = databaseManager.put(value6); - Long value7Id = databaseManager.put(value7); - assertNotNull(value6Id); - assertNotNull(value7Id); - - /* Delete logs with condition. */ - databaseManager.delete("COL_STRING", value2.getAsString("COL_STRING")); - assertEquals(1, databaseManager.getRowCount()); - ContentValues value7FromDatabase = databaseManager.get(value7Id); - assertContentValuesEquals(value7, value7FromDatabase); - - /* Clear. */ - databaseManager.clear(); - assertEquals(0, databaseManager.getRowCount()); +// private static void runDatabaseManagerTest(DatabaseManager databaseManager) { +// ContentValues value1 = generateContentValues(); +// ContentValues value2 = generateContentValues(); +// ContentValues value3 = generateContentValues(); +// +// /* Put. */ +// Long value1Id = databaseManager.put(value1); +// assertNotNull(value1Id); +// +// /* Put another. */ +// Long value2Id = databaseManager.put(value2); +// assertNotNull(value2Id); +// +// /* Generate an ID that is neither value1Id nor value2Id. */ +// +// /* Get. */ +// ContentValues value1FromDatabase = databaseManager.get(value1Id); +// assertContentValuesEquals(value1, value1FromDatabase); +// ContentValues value2FromDatabase = databaseManager.get(DatabaseManager.PRIMARY_KEY, value2Id); +// assertContentValuesEquals(value2, value2FromDatabase); +// //noinspection ResourceType +// ContentValues nullValueFromDatabase = databaseManager.get(-1); +// assertNull(nullValueFromDatabase); +// +// /* Query builder. */ +// SQLiteQueryBuilder colStringIsNullQuery = SQLiteUtils.newSQLiteQueryBuilder(); +// colStringIsNullQuery.appendWhere("COL_STRING IS NULL"); +// SQLiteQueryBuilder colStringQuery = SQLiteUtils.newSQLiteQueryBuilder(); +// colStringQuery.appendWhere("COL_STRING = ?"); +// SQLiteQueryBuilder colStringNullIsNullQuery = SQLiteUtils.newSQLiteQueryBuilder(); +// colStringNullIsNullQuery.appendWhere("COL_STRING_NULL IS NULL"); +// +// /* Count with scanner. */ +// DatabaseManager.Scanner scanner = databaseManager.getScanner(); +// assertEquals(2, scanner.getCount()); +// assertEquals(2, scanner.getCount()); +// DatabaseManager.Scanner scanner1 = databaseManager.getScanner(colStringQuery, new String[]{value1.getAsString("COL_STRING")}, false); +// assertEquals(1, scanner1.getCount()); +// Iterator iterator = scanner1.iterator(); +// assertContentValuesEquals(value1, iterator.next()); +// assertFalse(iterator.hasNext()); +// +// /* Null value matching. */ +// assertEquals(0, databaseManager.getScanner(colStringIsNullQuery, null, false).getCount()); +// assertEquals(2, databaseManager.getScanner("COL_STRING_NULL", null, null, null, false).getCount()); +// +// /* Test null value filter does not exclude anything, so returns the 2 logs. */ +// scanner = databaseManager.getScanner(null, null, "COL_STRING", null, false); +// assertEquals(2, scanner.getCount()); +// +// /* Test filtering only with the second key parameter to get only the second log. */ +// scanner = databaseManager.getScanner(null, null, "COL_STRING", Collections.singletonList(value1.getAsString("COL_STRING")), false); +// assertEquals(1, scanner.getCount()); +// assertContentValuesEquals(value2, scanner.iterator().next()); +// +// /* Delete. */ +// databaseManager.delete(value1Id); +// assertNull(databaseManager.get(value1Id)); +// assertEquals(1, databaseManager.getRowCount()); +// assertEquals(1, databaseManager.getScanner().getCount()); +// +// /* Put logs to delete multiple IDs. */ +// ContentValues value4 = generateContentValues(); +// ContentValues value5 = generateContentValues(); +// Long value4Id = databaseManager.put(value4); +// Long value5Id = databaseManager.put(value5); +// assertNotNull(value4Id); +// assertNotNull(value5Id); +// +// /* Delete multiple logs. */ +// databaseManager.delete(Arrays.asList(value4Id, value5Id)); +// assertNull(databaseManager.get(value4Id)); +// assertNull(databaseManager.get(value5Id)); +// assertEquals(1, databaseManager.getRowCount()); +// +// /* Put logs to delete with condition. */ +// ContentValues value6 = generateContentValues(); +// ContentValues value7 = generateContentValues(); +// value6.put("COL_STRING", value2.getAsString("COL_STRING")); +// value7.put("COL_STRING", value2.getAsString("COL_STRING") + "A"); +// Long value6Id = databaseManager.put(value6); +// Long value7Id = databaseManager.put(value7); +// assertNotNull(value6Id); +// assertNotNull(value7Id); +// +// /* Delete logs with condition. */ +// databaseManager.delete("COL_STRING", value2.getAsString("COL_STRING")); +// assertEquals(1, databaseManager.getRowCount()); +// ContentValues value7FromDatabase = databaseManager.get(value7Id); +// assertContentValuesEquals(value7, value7FromDatabase); +// +// /* Clear. */ +// databaseManager.clear(); +// assertEquals(0, databaseManager.getRowCount()); +// } + + private static ContentValues generateContentValues() { + byte[] randomBytes = new byte[10]; + RANDOM.nextBytes(randomBytes); + + ContentValues values = new ContentValues(); + values.put("COL_STRING", new String(randomBytes)); + values.put("COL_STRING_NULL", (String) null); + values.put("COL_BYTE", randomBytes[0]); + values.put("COL_SHORT", (short) RANDOM.nextInt(100)); + values.put("COL_INTEGER", RANDOM.nextInt()); + values.put("COL_LONG", RANDOM.nextLong()); + values.put("COL_FLOAT", RANDOM.nextFloat()); + values.put("COL_DOUBLE", RANDOM.nextDouble()); + values.put("COL_BOOLEAN", (mRandomBooleanValue = !mRandomBooleanValue)/*RANDOM.nextBoolean()*/); + values.put("COL_BYTE_ARRAY", randomBytes); + return values; + } + + private static void assertContentValuesEquals(ContentValues expected, ContentValues actual) { + assertEquals(expected.getAsString("COL_STRING"), actual.getAsString("COL_STRING")); + assertEquals(expected.getAsString("COL_STRING_NULL"), actual.getAsString("COL_STRING_NULL")); + assertEquals(expected.getAsByte("COL_BYTE"), actual.getAsByte("COL_BYTE")); + assertEquals(expected.getAsShort("COL_SHORT"), actual.getAsShort("COL_SHORT")); + assertEquals(expected.getAsInteger("COL_INTEGER"), actual.getAsInteger("COL_INTEGER")); + assertEquals(expected.getAsLong("COL_LONG"), actual.getAsLong("COL_LONG")); + assertEquals(expected.getAsFloat("COL_FLOAT"), actual.getAsFloat("COL_FLOAT")); + assertEquals(expected.getAsDouble("COL_DOUBLE"), actual.getAsDouble("COL_DOUBLE")); + assertEquals(expected.getAsBoolean("COL_BOOLEAN"), actual.getAsBoolean("COL_BOOLEAN")); + assertArrayEquals(expected.getAsByteArray("COL_BYTE_ARRAY"), actual.getAsByteArray("COL_BYTE_ARRAY")); } @Test public void databaseManager() { - Log.i(TAG, "Testing Database Manager"); /* Get instance to access database. */ DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseManager", "databaseManager", 1, mSchema, new DatabaseManager.Listener() { @@ -187,7 +221,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) try { - runDatabaseManagerTest(databaseManager); +// runDatabaseManagerTest(databaseManager); } finally { /* Close. */ @@ -198,7 +232,6 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @Test public void databaseManagerUpgradeNotHandled() { - Log.i(TAG, "Testing Database Manager Upgrade by recreating table"); /* Create a schema for v1. */ ContentValues schema = new ContentValues(); @@ -255,7 +288,6 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @Test public void databaseManagerUpgradeHandled() { - Log.i(TAG, "Testing Database Manager Upgrade by updating table"); /* Create a schema for v1. */ ContentValues schema = new ContentValues(); @@ -326,7 +358,6 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @Test(expected = UnsupportedOperationException.class) public void databaseManagerScannerRemove() { - Log.i(TAG, "Testing Database Manager Exceptions"); /* Get instance to access database. */ DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseManagerScannerRemove", "databaseManagerScannerRemove", 1, mSchema, new DatabaseManager.Listener() { @@ -339,7 +370,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) try { - databaseManager.getScanner(null, null, null, null, false).iterator().remove(); + databaseManager.getScanner().iterator().remove(); } finally { /* Close. */ @@ -350,7 +381,6 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { @Test(expected = NoSuchElementException.class) public void databaseManagerScannerNext() { - Log.i(TAG, "Testing Database Manager Exceptions"); /* Get instance to access database. */ DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseManagerScannerNext", "databaseManagerScannerNext", 1, mSchema, new DatabaseManager.Listener() { @@ -363,7 +393,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) try { - databaseManager.getScanner(null, null, null, null, false).iterator().next(); + databaseManager.getScanner().iterator().next(); } finally { /* Close. */ @@ -372,10 +402,8 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } - @Test public void setMaximumSize() { - Log.i(TAG, "Testing Database Manager set maximum size"); /* Get instance to access database. */ DatabaseManager databaseManager = new DatabaseManager(sContext, "test-setMaximumSize", "test.setMaximumSize", 1, mSchema, new DatabaseManager.Listener() { @@ -411,35 +439,4 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } - private static ContentValues generateContentValues() { - byte[] randomBytes = new byte[10]; - RANDOM.nextBytes(randomBytes); - - ContentValues values = new ContentValues(); - values.put("COL_STRING", new String(randomBytes)); - values.put("COL_STRING_NULL", (String) null); - values.put("COL_BYTE", randomBytes[0]); - values.put("COL_SHORT", (short) RANDOM.nextInt(100)); - values.put("COL_INTEGER", RANDOM.nextInt()); - values.put("COL_LONG", RANDOM.nextLong()); - values.put("COL_FLOAT", RANDOM.nextFloat()); - values.put("COL_DOUBLE", RANDOM.nextDouble()); - values.put("COL_BOOLEAN", (mRandomBooleanValue = !mRandomBooleanValue)/*RANDOM.nextBoolean()*/); - values.put("COL_BYTE_ARRAY", randomBytes); - return values; - } - - private static void assertContentValuesEquals(ContentValues expected, ContentValues actual) { - assertEquals(expected.getAsString("COL_STRING"), actual.getAsString("COL_STRING")); - assertEquals(expected.getAsString("COL_STRING_NULL"), actual.getAsString("COL_STRING_NULL")); - assertEquals(expected.getAsByte("COL_BYTE"), actual.getAsByte("COL_BYTE")); - assertEquals(expected.getAsShort("COL_SHORT"), actual.getAsShort("COL_SHORT")); - assertEquals(expected.getAsInteger("COL_INTEGER"), actual.getAsInteger("COL_INTEGER")); - assertEquals(expected.getAsLong("COL_LONG"), actual.getAsLong("COL_LONG")); - assertEquals(expected.getAsFloat("COL_FLOAT"), actual.getAsFloat("COL_FLOAT")); - assertEquals(expected.getAsDouble("COL_DOUBLE"), actual.getAsDouble("COL_DOUBLE")); - assertEquals(expected.getAsBoolean("COL_BOOLEAN"), actual.getAsBoolean("COL_BOOLEAN")); - assertArrayEquals(expected.getAsByteArray("COL_BYTE_ARRAY"), actual.getAsByteArray("COL_BYTE_ARRAY")); - } - } \ No newline at end of file diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index 8c136226fc..65f0724bb6 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -3,6 +3,7 @@ import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteQueryBuilder; import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -17,6 +18,7 @@ import com.microsoft.appcenter.utils.crypto.CryptoUtils; import com.microsoft.appcenter.utils.storage.DatabaseManager; import com.microsoft.appcenter.utils.storage.FileManager; +import com.microsoft.appcenter.utils.storage.SQLiteUtils; import org.json.JSONException; @@ -89,6 +91,7 @@ public class DatabasePersistence extends Persistence { */ @VisibleForTesting static final String TABLE = "logs"; + /** * Current version of the schema. */ @@ -110,11 +113,6 @@ public class DatabasePersistence extends Persistence { */ private static final String PAYLOAD_FILE_EXTENSION = ".json"; - /** - * Application context. - */ - private final Context mContext; - /** * Database manager instance to access Persistence database. */ @@ -133,6 +131,11 @@ public class DatabasePersistence extends Persistence { @VisibleForTesting final Set mPendingDbIdentifiers; + /** + * Application context. + */ + private final Context mContext; + /** * Base directory to store large payloads outside of SQLite. */ @@ -179,11 +182,6 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { mLargePayloadDirectory.mkdirs(); } - @Override - public boolean setMaxStorageSize(long maxStorageSizeInBytes) { - return mDatabaseManager.setMaxSize(maxStorageSizeInBytes); - } - /** * Instantiates {@link ContentValues} with the give values. * @@ -203,6 +201,11 @@ private static ContentValues getContentValues(@Nullable String group, @Nullable return values; } + @Override + public boolean setMaxStorageSize(long maxStorageSizeInBytes) { + return mDatabaseManager.setMaxSize(maxStorageSizeInBytes); + } + @Override public long putLog(@NonNull String group, @NonNull Log log) throws PersistenceException { @@ -328,7 +331,9 @@ public void deleteLogs(String group) { public int countLogs(@NonNull String group) { /* Query database and get scanner. */ - DatabaseManager.Scanner scanner = mDatabaseManager.getScanner(COLUMN_GROUP, group, null, null, true); + SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); + builder.appendWhere(COLUMN_GROUP + " = ?"); + DatabaseManager.Scanner scanner = mDatabaseManager.getScanner(builder, new String[]{group}, true); int count = scanner.getCount(); scanner.close(); return count; @@ -342,13 +347,27 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT AppCenterLog.debug(LOG_TAG, "Trying to get " + limit + " logs from the Persistence database for " + group); /* Query database and get scanner. */ - DatabaseManager.Scanner scanner = mDatabaseManager.getScanner(COLUMN_GROUP, group, COLUMN_TARGET_KEY, pausedTargetKeys, false); + SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); + builder.appendWhere(COLUMN_GROUP + " = ?"); + String[] selectionArgs = new String[pausedTargetKeys.size() + 1]; + selectionArgs[0] = group; + if (!pausedTargetKeys.isEmpty()) { + StringBuilder filter = new StringBuilder(); + for (int i = 0; i < pausedTargetKeys.size(); i++) { + filter.append("?,"); + } + filter.deleteCharAt(filter.length() - 1); + builder.appendWhere(" AND "); + builder.appendWhere(COLUMN_TARGET_KEY + " NOT IN (" + filter.toString() + ")"); + System.arraycopy(pausedTargetKeys.toArray(), 0, selectionArgs, 1, pausedTargetKeys.size()); + } /* Add logs to output parameter after deserialization if logs are not already sent. */ int count = 0; Map candidates = new TreeMap<>(); List failedDbIdentifiers = new ArrayList<>(); File largePayloadGroupDirectory = getLargePayloadGroupDirectory(group); + DatabaseManager.Scanner scanner = mDatabaseManager.getScanner(builder, selectionArgs, false); for (Iterator iterator = scanner.iterator(); iterator.hasNext() && count < limit; ) { ContentValues values = iterator.next(); Long dbIdentifier = values.getAsLong(DatabaseManager.PRIMARY_KEY); @@ -361,7 +380,7 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT */ if (dbIdentifier == null) { AppCenterLog.error(LOG_TAG, "Empty database record, probably content was larger than 2MB, need to delete as it's now corrupted."); - DatabaseManager.Scanner idScanner = mDatabaseManager.getScanner(COLUMN_GROUP, group, COLUMN_TARGET_KEY, pausedTargetKeys, true); + DatabaseManager.Scanner idScanner = mDatabaseManager.getScanner(builder, selectionArgs, true); for (ContentValues idValues : idScanner) { Long invalidId = idValues.getAsLong(DatabaseManager.PRIMARY_KEY); if (!mPendingDbIdentifiers.contains(invalidId) && !candidates.containsKey(invalidId)) { diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index 21bf90ac11..7b07263017 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -18,9 +18,7 @@ import com.microsoft.appcenter.utils.AppCenterLog; import java.io.Closeable; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -188,7 +186,7 @@ public long put(@NonNull ContentValues values) { } catch (SQLiteFullException e) { /* Delete the oldest log. */ - Cursor cursor = getCursor(null, null, null, null, true); + Cursor cursor = getCursor(SQLiteUtils.newSQLiteQueryBuilder(), null, true); try { if (cursor.moveToNext()) { delete(cursor.getLong(0)); @@ -264,7 +262,17 @@ public ContentValues get(@IntRange(from = 0) long id) { */ public ContentValues get(@Nullable String key, @Nullable Object value) { try { - Cursor cursor = getCursor(key, value, null, null, false); + SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); + String[] selectionArgs = null; + if (key != null) { + if (value == null) { + builder.appendWhere(key + " IS NULL"); + } else { + builder.appendWhere(key + " = ?"); + selectionArgs = new String[]{value.toString()}; + } + } + Cursor cursor = getCursor(builder, selectionArgs, false); ContentValues values = cursor.moveToFirst() ? buildValues(cursor, mSchema) : null; cursor.close(); return values; @@ -275,18 +283,25 @@ public ContentValues get(@Nullable String key, @Nullable Object value) { } /** - * Gets a scanner to iterate all values those match - * key1 == value1 and key2 not matching any values from the list in value2Filter. + * Gets a scanner for all data stored in the table. * - * @param key1 The optional key1 for query. - * @param value1 The optional value1 for query. - * @param key2 The optional key2 to filter the query. - * @param value2Filter The optional value filter for key2. - * @param idOnly true to return only identifier, false to return all fields. * @return A scanner to iterate all values. */ - public Scanner getScanner(String key1, Object value1, String key2, Collection value2Filter, boolean idOnly) { - return new Scanner(key1, value1, key2, value2Filter, idOnly); + @VisibleForTesting + Scanner getScanner() { + return getScanner(null, null, false); + } + + /** + * Gets a scanner for the given SQL query. + * + * @param queryBuilder The query builder that contains SQL query . + * @param selectionArgs The array of values for selection. + * @param idOnly Return only row identifier if true, return all fields otherwise. + * @return A scanner to iterate all values. + */ + public Scanner getScanner(SQLiteQueryBuilder queryBuilder, String[] selectionArgs, boolean idOnly) { + return new Scanner(queryBuilder, selectionArgs, idOnly); } /** @@ -329,59 +344,19 @@ public final long getRowCount() { /** * Gets a cursor for all rows in the table, all rows where key matches value if specified. * - * @param key1 The first key to match values against. - * @param value1 The value to match against first key. - * @param key2 The second key to match values against. - * @param value2Filter The list of values to exclude matching the second key. - * @param idOnly Return only row identifier if true, return all fields otherwise. + * @param queryBuilder The query builder that contains SQL query. + * @param selectionArgs The array of values for selection. + * @param idOnly Return only row identifier if true, return all fields otherwise. * @return A cursor for all rows that matches the given criteria. * @throws RuntimeException If an error occurs. */ - Cursor getCursor(String key1, Object value1, String key2, Collection value2Filter, boolean idOnly) throws RuntimeException { - - /* Build a query to get values. */ - SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); - builder.setTables(mTable); - List selectionArgsList = new ArrayList<>(); - - /* Add filter for key1 = value1, with null value matching. */ - if (key1 != null) { - if (value1 == null) { - builder.appendWhere(key1 + " IS NULL"); - } else { - builder.appendWhere(key1 + " = ?"); - selectionArgsList.add(value1.toString()); - } + Cursor getCursor(@Nullable SQLiteQueryBuilder queryBuilder, @Nullable String[] selectionArgs, boolean idOnly) throws RuntimeException { + if (queryBuilder == null) { + queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); } - - /* Append value filter to exclude values matching key2, if key2 and value2Filter were both specified. */ - if (key2 != null && value2Filter != null && !value2Filter.isEmpty()) { - if (key1 != null) { - builder.appendWhere(" AND "); - } - builder.appendWhere(key2); - builder.appendWhere(" NOT IN ("); - StringBuilder inBuilder = new StringBuilder(); - for (String value2 : value2Filter) { - inBuilder.append("?,"); - selectionArgsList.add(value2); - } - inBuilder.deleteCharAt(inBuilder.length() - 1); - builder.appendWhere(inBuilder.toString()); - builder.appendWhere(")"); - } - - /* Convert list to array. */ - String[] selectionArgs; - if (selectionArgsList.isEmpty()) { - selectionArgs = null; - } else { - selectionArgs = selectionArgsList.toArray(new String[selectionArgsList.size()]); - } - - /* Query database. */ + queryBuilder.setTables(mTable); String[] projectionIn = idOnly ? new String[]{PRIMARY_KEY} : null; - return builder.query(getDatabase(), projectionIn, null, selectionArgs, null, null, PRIMARY_KEY); + return queryBuilder.query(getDatabase(), projectionIn, null, selectionArgs, null, null, PRIMARY_KEY); } /** @@ -424,7 +399,11 @@ void setSQLiteOpenHelper(@NonNull SQLiteOpenHelper helper) { */ @VisibleForTesting String[] getColumnNames() { - return getCursor(null, null, null, null, false).getColumnNames(); + + // TODO: Below line doesn't look efficient to get column names. Could use "PRAGMA table_info(table-name)" to avoid getting all data back from database just to get column names. + SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); + builder.setTables(mTable); + return builder.query(getDatabase(), null, null, null, null, null, PRIMARY_KEY).getColumnNames(); } /** @@ -490,27 +469,17 @@ public class Scanner implements Iterable, Closeable { /** * First filter key. */ - private final String key1; - - /** - * Filter value for key1. - */ - private final Object value1; - - /** - * Second filter key. - */ - private final String key2; + private final SQLiteQueryBuilder mQueryBuilder; /** - * Filter values to exclude matching key2. + * Filter value for mQueryBuilder. */ - private final Collection value2Filter; + private final String[] mSelectionArgs; /** * Return only IDs flags (SQLite implementation only). */ - private final boolean idOnly; + private final boolean mIdOnly; /** * SQLite cursor. @@ -520,12 +489,10 @@ public class Scanner implements Iterable, Closeable { /** * Initializes a cursor with optional filter. */ - private Scanner(String key1, Object value1, String key2, Collection value2Filter, boolean idOnly) { - this.key1 = key1; - this.value1 = value1; - this.key2 = key2; - this.value2Filter = value2Filter; - this.idOnly = idOnly; + private Scanner(SQLiteQueryBuilder queryBuilder, String[] selectionArgs, boolean idOnly) { + this.mQueryBuilder = queryBuilder; + this.mSelectionArgs = selectionArgs; + this.mIdOnly = idOnly; } @Override @@ -549,7 +516,7 @@ public Iterator iterator() { /* Close cursor first if it was being used. */ close(); - cursor = getCursor(key1, value1, key2, value2Filter, idOnly); + cursor = getCursor(mQueryBuilder, mSelectionArgs, mIdOnly); /* Wrap cursor as iterator. */ return new Iterator() { @@ -608,7 +575,7 @@ public void remove() { public int getCount() { try { if (cursor == null) { - cursor = getCursor(key1, value1, key2, value2Filter, idOnly); + cursor = getCursor(mQueryBuilder, mSelectionArgs, mIdOnly); } return cursor.getCount(); } catch (RuntimeException e) { diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SQLiteUtils.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SQLiteUtils.java index d7bb31e01a..1ed1b240fc 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SQLiteUtils.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SQLiteUtils.java @@ -3,10 +3,10 @@ import android.database.sqlite.SQLiteQueryBuilder; import android.support.annotation.NonNull; -class SQLiteUtils { +public class SQLiteUtils { @NonNull - static SQLiteQueryBuilder newSQLiteQueryBuilder() { + public static SQLiteQueryBuilder newSQLiteQueryBuilder() { return new SQLiteQueryBuilder(); } } \ No newline at end of file diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java index ada2dc26a3..70485a7fe1 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java @@ -128,7 +128,7 @@ public void scannerIteratorFailed() { mockStatic(AppCenterLog.class); DatabaseManager databaseManagerMock; databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.getScanner(null, null, null, null, false).iterator(); + databaseManagerMock.getScanner().iterator(); verifyStatic(); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -138,7 +138,7 @@ public void scannerCountFailed() { mockStatic(AppCenterLog.class); DatabaseManager databaseManagerMock; databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.getScanner(null, null, null, null, false).getCount(); + databaseManagerMock.getScanner().getCount(); verifyStatic(); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -156,7 +156,7 @@ public void scannerNextFailedButClosingWorks() { SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class, new Returns(cursor)); when(SQLiteUtils.newSQLiteQueryBuilder()).thenReturn(sqLiteQueryBuilder); when(cursor.moveToNext()).thenThrow(new RuntimeException()); - DatabaseManager.Scanner scanner = databaseManagerMock.getScanner(null, null, null, null, false); + DatabaseManager.Scanner scanner = databaseManagerMock.getScanner(); assertFalse(scanner.iterator().hasNext()); verifyStatic(never()); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); @@ -180,7 +180,7 @@ public void scannerNextFailedAndClosingFails() { when(SQLiteUtils.newSQLiteQueryBuilder()).thenReturn(sqLiteQueryBuilder); when(cursor.moveToNext()).thenThrow(new RuntimeException()); doThrow(new RuntimeException()).when(cursor).close(); - DatabaseManager.Scanner scanner = databaseManagerMock.getScanner(null, null, null, null, false); + DatabaseManager.Scanner scanner = databaseManagerMock.getScanner(); assertFalse(scanner.iterator().hasNext()); verifyStatic(never()); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); @@ -201,7 +201,7 @@ public void cursorCloseFailed() { SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class, new Returns(cursor)); when(SQLiteUtils.newSQLiteQueryBuilder()).thenReturn(sqLiteQueryBuilder); doThrow(new RuntimeException()).when(cursor).close(); - DatabaseManager.Scanner scanner = databaseManagerMock.getScanner(null, null, null, null, false); + DatabaseManager.Scanner scanner = databaseManagerMock.getScanner(); assertFalse(scanner.iterator().hasNext()); scanner.close(); verifyStatic(); From 4c79bca45ce5c188020bc9d1383e3ecf06dd38f6 Mon Sep 17 00:00:00 2001 From: Jae Lim Date: Thu, 25 Oct 2018 17:35:34 -0700 Subject: [PATCH 09/68] Fix broken unit tests --- .../storage/DatabaseManagerAndroidTest.java | 195 +++++++++--------- .../utils/storage/DatabaseManager.java | 24 +-- .../appcenter/AbstractAppCenterTest.java | 3 +- .../persistence/DatabasePersistenceTest.java | 10 +- 4 files changed, 117 insertions(+), 115 deletions(-) diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java index e0a0880412..5198e87ae6 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java @@ -8,7 +8,6 @@ import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.util.Log; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -16,7 +15,6 @@ import org.junit.runner.RunWith; import java.util.Arrays; -import java.util.Collections; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Random; @@ -80,101 +78,102 @@ public static void tearDownClass() { } @SuppressWarnings("SpellCheckingInspection") -// private static void runDatabaseManagerTest(DatabaseManager databaseManager) { -// ContentValues value1 = generateContentValues(); -// ContentValues value2 = generateContentValues(); -// ContentValues value3 = generateContentValues(); -// -// /* Put. */ -// Long value1Id = databaseManager.put(value1); -// assertNotNull(value1Id); -// -// /* Put another. */ -// Long value2Id = databaseManager.put(value2); -// assertNotNull(value2Id); -// -// /* Generate an ID that is neither value1Id nor value2Id. */ -// -// /* Get. */ -// ContentValues value1FromDatabase = databaseManager.get(value1Id); -// assertContentValuesEquals(value1, value1FromDatabase); -// ContentValues value2FromDatabase = databaseManager.get(DatabaseManager.PRIMARY_KEY, value2Id); -// assertContentValuesEquals(value2, value2FromDatabase); -// //noinspection ResourceType -// ContentValues nullValueFromDatabase = databaseManager.get(-1); -// assertNull(nullValueFromDatabase); -// -// /* Query builder. */ -// SQLiteQueryBuilder colStringIsNullQuery = SQLiteUtils.newSQLiteQueryBuilder(); -// colStringIsNullQuery.appendWhere("COL_STRING IS NULL"); -// SQLiteQueryBuilder colStringQuery = SQLiteUtils.newSQLiteQueryBuilder(); -// colStringQuery.appendWhere("COL_STRING = ?"); -// SQLiteQueryBuilder colStringNullIsNullQuery = SQLiteUtils.newSQLiteQueryBuilder(); -// colStringNullIsNullQuery.appendWhere("COL_STRING_NULL IS NULL"); -// -// /* Count with scanner. */ -// DatabaseManager.Scanner scanner = databaseManager.getScanner(); -// assertEquals(2, scanner.getCount()); -// assertEquals(2, scanner.getCount()); -// DatabaseManager.Scanner scanner1 = databaseManager.getScanner(colStringQuery, new String[]{value1.getAsString("COL_STRING")}, false); -// assertEquals(1, scanner1.getCount()); -// Iterator iterator = scanner1.iterator(); -// assertContentValuesEquals(value1, iterator.next()); -// assertFalse(iterator.hasNext()); -// -// /* Null value matching. */ -// assertEquals(0, databaseManager.getScanner(colStringIsNullQuery, null, false).getCount()); -// assertEquals(2, databaseManager.getScanner("COL_STRING_NULL", null, null, null, false).getCount()); -// -// /* Test null value filter does not exclude anything, so returns the 2 logs. */ -// scanner = databaseManager.getScanner(null, null, "COL_STRING", null, false); -// assertEquals(2, scanner.getCount()); -// -// /* Test filtering only with the second key parameter to get only the second log. */ -// scanner = databaseManager.getScanner(null, null, "COL_STRING", Collections.singletonList(value1.getAsString("COL_STRING")), false); -// assertEquals(1, scanner.getCount()); -// assertContentValuesEquals(value2, scanner.iterator().next()); -// -// /* Delete. */ -// databaseManager.delete(value1Id); -// assertNull(databaseManager.get(value1Id)); -// assertEquals(1, databaseManager.getRowCount()); -// assertEquals(1, databaseManager.getScanner().getCount()); -// -// /* Put logs to delete multiple IDs. */ -// ContentValues value4 = generateContentValues(); -// ContentValues value5 = generateContentValues(); -// Long value4Id = databaseManager.put(value4); -// Long value5Id = databaseManager.put(value5); -// assertNotNull(value4Id); -// assertNotNull(value5Id); -// -// /* Delete multiple logs. */ -// databaseManager.delete(Arrays.asList(value4Id, value5Id)); -// assertNull(databaseManager.get(value4Id)); -// assertNull(databaseManager.get(value5Id)); -// assertEquals(1, databaseManager.getRowCount()); -// -// /* Put logs to delete with condition. */ -// ContentValues value6 = generateContentValues(); -// ContentValues value7 = generateContentValues(); -// value6.put("COL_STRING", value2.getAsString("COL_STRING")); -// value7.put("COL_STRING", value2.getAsString("COL_STRING") + "A"); -// Long value6Id = databaseManager.put(value6); -// Long value7Id = databaseManager.put(value7); -// assertNotNull(value6Id); -// assertNotNull(value7Id); -// -// /* Delete logs with condition. */ -// databaseManager.delete("COL_STRING", value2.getAsString("COL_STRING")); -// assertEquals(1, databaseManager.getRowCount()); -// ContentValues value7FromDatabase = databaseManager.get(value7Id); -// assertContentValuesEquals(value7, value7FromDatabase); -// -// /* Clear. */ -// databaseManager.clear(); -// assertEquals(0, databaseManager.getRowCount()); -// } + private static void runDatabaseManagerTest(DatabaseManager databaseManager) { + ContentValues value1 = generateContentValues(); + ContentValues value2 = generateContentValues(); + ContentValues value3 = generateContentValues(); + + /* Put. */ + Long value1Id = databaseManager.put(value1); + assertNotNull(value1Id); + + /* Put another. */ + Long value2Id = databaseManager.put(value2); + assertNotNull(value2Id); + + /* Generate an ID that is neither value1Id nor value2Id. */ + + /* Get. */ + ContentValues value1FromDatabase = databaseManager.get(value1Id); + assertContentValuesEquals(value1, value1FromDatabase); + ContentValues value2FromDatabase = databaseManager.get(DatabaseManager.PRIMARY_KEY, value2Id); + assertContentValuesEquals(value2, value2FromDatabase); + //noinspection ResourceType + ContentValues nullValueFromDatabase = databaseManager.get(-1); + assertNull(nullValueFromDatabase); + + /* Count with scanner. */ + DatabaseManager.Scanner scanner = databaseManager.getScanner(); + assertEquals(2, scanner.getCount()); + assertEquals(2, scanner.getCount()); + SQLiteQueryBuilder queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); + queryBuilder.appendWhere("COL_STRING = ?"); + DatabaseManager.Scanner scanner1 = databaseManager.getScanner(queryBuilder, new String[]{value1.getAsString("COL_STRING")}, false); + assertEquals(1, scanner1.getCount()); + Iterator iterator = scanner1.iterator(); + assertContentValuesEquals(value1, iterator.next()); + assertFalse(iterator.hasNext()); + + /* Null value matching. */ + queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); + queryBuilder.appendWhere("COL_STRING IS NULL"); + assertEquals(0, databaseManager.getScanner(queryBuilder, null, false).getCount()); + queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); + queryBuilder.appendWhere("COL_STRING_NULL IS NULL"); + assertEquals(2, databaseManager.getScanner(queryBuilder, null, false).getCount()); + + /* Test null value filter does not exclude anything, so returns the 2 logs. */ + queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); + scanner = databaseManager.getScanner(queryBuilder, null, false); + assertEquals(2, scanner.getCount()); + + /* Test filtering only with the second key parameter to get only the second log. */ + queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); + queryBuilder.appendWhere("COL_STRING NOT IN (?)"); + scanner = databaseManager.getScanner(queryBuilder, new String[]{value1.getAsString("COL_STRING")}, false); + assertEquals(1, scanner.getCount()); + assertContentValuesEquals(value2, scanner.iterator().next()); + + /* Delete. */ + databaseManager.delete(value1Id); + assertNull(databaseManager.get(value1Id)); + assertEquals(1, databaseManager.getRowCount()); + assertEquals(1, databaseManager.getScanner().getCount()); + + /* Put logs to delete multiple IDs. */ + ContentValues value4 = generateContentValues(); + ContentValues value5 = generateContentValues(); + Long value4Id = databaseManager.put(value4); + Long value5Id = databaseManager.put(value5); + assertNotNull(value4Id); + assertNotNull(value5Id); + + /* Delete multiple logs. */ + databaseManager.delete(Arrays.asList(value4Id, value5Id)); + assertNull(databaseManager.get(value4Id)); + assertNull(databaseManager.get(value5Id)); + assertEquals(1, databaseManager.getRowCount()); + + /* Put logs to delete with condition. */ + ContentValues value6 = generateContentValues(); + ContentValues value7 = generateContentValues(); + value6.put("COL_STRING", value2.getAsString("COL_STRING")); + value7.put("COL_STRING", value2.getAsString("COL_STRING") + "A"); + Long value6Id = databaseManager.put(value6); + Long value7Id = databaseManager.put(value7); + assertNotNull(value6Id); + assertNotNull(value7Id); + + /* Delete logs with condition. */ + databaseManager.delete("COL_STRING", value2.getAsString("COL_STRING")); + assertEquals(1, databaseManager.getRowCount()); + ContentValues value7FromDatabase = databaseManager.get(value7Id); + assertContentValuesEquals(value7, value7FromDatabase); + + /* Clear. */ + databaseManager.clear(); + assertEquals(0, databaseManager.getRowCount()); + } private static ContentValues generateContentValues() { byte[] randomBytes = new byte[10]; @@ -221,7 +220,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) try { -// runDatabaseManagerTest(databaseManager); + runDatabaseManagerTest(databaseManager); } finally { /* Close. */ diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index 7b07263017..c3273687fa 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -484,7 +484,7 @@ public class Scanner implements Iterable, Closeable { /** * SQLite cursor. */ - private Cursor cursor; + private Cursor mCursor; /** * Initializes a cursor with optional filter. @@ -499,10 +499,10 @@ private Scanner(SQLiteQueryBuilder queryBuilder, String[] selectionArgs, boolean public void close() { /* Close cursor. */ - if (cursor != null) { + if (mCursor != null) { try { - cursor.close(); - cursor = null; + mCursor.close(); + mCursor = null; } catch (RuntimeException e) { AppCenterLog.error(AppCenter.LOG_TAG, "Failed to close the scanner.", e); } @@ -516,7 +516,7 @@ public Iterator iterator() { /* Close cursor first if it was being used. */ close(); - cursor = getCursor(mQueryBuilder, mSelectionArgs, mIdOnly); + mCursor = getCursor(mQueryBuilder, mSelectionArgs, mIdOnly); /* Wrap cursor as iterator. */ return new Iterator() { @@ -530,7 +530,7 @@ public Iterator iterator() { public boolean hasNext() { if (hasNext == null) { try { - hasNext = cursor.moveToNext(); + hasNext = mCursor.moveToNext(); } catch (RuntimeException e) { /* Consider no next on errors. */ @@ -538,11 +538,11 @@ public boolean hasNext() { /* Close cursor. */ try { - cursor.close(); + mCursor.close(); } catch (RuntimeException e1) { AppCenterLog.warn(AppCenter.LOG_TAG, "Closing cursor failed", e1); } - cursor = null; + mCursor = null; } } return hasNext; @@ -558,7 +558,7 @@ public ContentValues next() { hasNext = null; /* Build object. */ - return buildValues(cursor, mSchema); + return buildValues(mCursor, mSchema); } @Override @@ -574,10 +574,10 @@ public void remove() { public int getCount() { try { - if (cursor == null) { - cursor = getCursor(mQueryBuilder, mSelectionArgs, mIdOnly); + if (mCursor == null) { + mCursor = getCursor(mQueryBuilder, mSelectionArgs, mIdOnly); } - return cursor.getCount(); + return mCursor.getCount(); } catch (RuntimeException e) { AppCenterLog.error(AppCenter.LOG_TAG, "Failed to get count of the scanner.", e); } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java index a219e80fa1..3b1f939d2d 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java @@ -4,6 +4,7 @@ import android.content.ContentValues; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.database.sqlite.SQLiteQueryBuilder; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; @@ -167,7 +168,7 @@ public Void answer(InvocationOnMock invocation) { DatabaseManager databaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); DatabaseManager.Scanner databaseScanner = mock(DatabaseManager.Scanner.class); - when(databaseManager.getScanner(anyString(), anyObject(), anyString(), anyCollectionOf(String.class), anyBoolean())).thenReturn(databaseScanner); + when(databaseManager.getScanner(any(SQLiteQueryBuilder.class), any(String[].class), anyBoolean())).thenReturn(databaseScanner); when(databaseScanner.iterator()).thenReturn(mDataBaseScannerIterator); /* Mock network state helper. */ diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java index 3a982f7983..56ab04d870 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java @@ -2,6 +2,7 @@ import android.content.ContentValues; import android.content.Context; +import android.database.sqlite.SQLiteQueryBuilder; import com.microsoft.appcenter.AppCenter; import com.microsoft.appcenter.ingestion.models.Log; @@ -9,6 +10,7 @@ import com.microsoft.appcenter.ingestion.models.json.LogSerializer; import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.storage.DatabaseManager; +import com.microsoft.appcenter.utils.storage.SQLiteUtils; import org.json.JSONException; import org.junit.Rule; @@ -105,7 +107,7 @@ public void clearPendingLogState() throws Exception { for (int i = 0; i < groupCount; i++) { DatabaseManager.Scanner mockDatabaseScanner = mock(DatabaseManager.Scanner.class); when(mockDatabaseScanner.iterator()).thenReturn(list.get(i).iterator()); - when(mockDatabaseManager.getScanner(COLUMN_GROUP, String.valueOf(i), COLUMN_TARGET_KEY, Collections.emptyList(), false)).thenReturn(mockDatabaseScanner); + when(mockDatabaseManager.getScanner(any(SQLiteQueryBuilder.class), eq(new String[]{String.valueOf(i)}), eq(false))).thenReturn(mockDatabaseScanner); } LogSerializer mockLogSerializer = mock(LogSerializer.class); @@ -163,7 +165,7 @@ public void getLogsWithCorruption() throws Exception { /* Mock log sequence retrieved from scanner. */ DatabaseManager.Scanner databaseScanner = mock(DatabaseManager.Scanner.class); - when(databaseManager.getScanner(anyString(), anyString(), anyString(), anyCollectionOf(String.class), eq(false))).thenReturn(databaseScanner); + when(databaseManager.getScanner(any(SQLiteQueryBuilder.class), any(String[].class), eq(false))).thenReturn(databaseScanner); when(databaseScanner.iterator()).thenReturn(fieldValues.iterator()); /* Mock second scanner with identifiers only. */ @@ -174,7 +176,7 @@ public void getLogsWithCorruption() throws Exception { idValues.add(contentValues); } DatabaseManager.Scanner idDatabaseScanner = mock(DatabaseManager.Scanner.class); - when(databaseManager.getScanner(anyString(), anyObject(), anyString(), anyCollectionOf(String.class), eq(true))).thenReturn(idDatabaseScanner); + when(databaseManager.getScanner(any(SQLiteQueryBuilder.class), any(String[].class), eq(true))).thenReturn(idDatabaseScanner); when(idDatabaseScanner.iterator()).thenReturn(idValues.iterator()); /* Mock serializer and eventually the database. */ @@ -271,7 +273,7 @@ public void checkSetStorageSizeForwarding() throws Exception { DatabaseManager databaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); DatabaseManager.Scanner databaseScanner = mock(DatabaseManager.Scanner.class); - when(databaseManager.getScanner(anyString(), anyObject(), anyString(), anyCollectionOf(String.class), anyBoolean())).thenReturn(databaseScanner); + when(databaseManager.getScanner(any(SQLiteQueryBuilder.class), any(String[].class), anyBoolean())).thenReturn(databaseScanner); when(databaseManager.setMaxSize(anyLong())).thenReturn(true).thenReturn(false); /* Just checks calls are forwarded to the low level database layer. */ From ada48eeb7ffb3a3f924af67c1d06f8ae15f5291e Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Fri, 26 Oct 2018 13:09:04 +0300 Subject: [PATCH 10/68] Remove scanner class --- .../DatabasePersistenceAndroidTest.java | 30 ++-- .../storage/DatabaseManagerAndroidTest.java | 80 ++------- .../persistence/DatabasePersistence.java | 22 +-- .../utils/storage/DatabaseManager.java | 157 +----------------- 4 files changed, 50 insertions(+), 239 deletions(-) diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index 035d930acd..c4ed0c40ce 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -3,6 +3,7 @@ import android.annotation.SuppressLint; import android.content.ContentValues; import android.content.Context; +import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.support.test.InstrumentationRegistry; @@ -85,10 +86,11 @@ public static void setUpClass() { Constants.loadFromContext(sContext); } - private static int getIteratorSize(Iterator iterator) { + private static int getCursorSize(Cursor cursor) { int count = 0; - for (; iterator.hasNext(); iterator.next()) + while (cursor.moveToNext()) { count++; + } return count; } @@ -477,40 +479,40 @@ public void deleteLogs() throws PersistenceException { builder.appendWhere(DatabasePersistence.COLUMN_GROUP + " = ?"); /* Access DatabaseStorage directly to verify the deletions. */ - DatabaseManager.Scanner scanner1 = persistence.mDatabaseManager.getScanner(builder, new String[]{"test-p1"}, false); - DatabaseManager.Scanner scanner2 = persistence.mDatabaseManager.getScanner(builder, new String[]{"test-p2"}, false); - DatabaseManager.Scanner scanner3 = persistence.mDatabaseManager.getScanner(builder, new String[]{"test-p3"}, false); + Cursor cursor1 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p1"}, false); + Cursor cursor2 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p2"}, false); + Cursor cursor3 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p3"}, false); //noinspection TryFinallyCanBeTryWithResources try { /* Verify. */ - assertEquals(2, getIteratorSize(scanner1.iterator())); - assertEquals(1, getIteratorSize(scanner2.iterator())); - assertEquals(1, getIteratorSize(scanner3.iterator())); + assertEquals(2, getCursorSize(cursor1)); + assertEquals(1, getCursorSize(cursor2)); + assertEquals(1, getCursorSize(cursor3)); } finally { /* Close. */ - scanner1.close(); - scanner2.close(); - scanner3.close(); + cursor1.close(); + cursor2.close(); + cursor3.close(); } /* Delete. */ persistence.deleteLogs("test-p1", id); /* Access DatabaseStorage directly to verify the deletions. */ - DatabaseManager.Scanner scanner4 = persistence.mDatabaseManager.getScanner(builder, new String[]{"test-p1"}, false); + Cursor cursor4 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p1"}, false); //noinspection TryFinallyCanBeTryWithResources try { /* Verify. */ - assertEquals(0, getIteratorSize(scanner4.iterator())); + assertEquals(0, getCursorSize(cursor4)); } finally { /* Close. */ - scanner4.close(); + cursor4.close(); } /* Count logs after delete. */ diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java index 5198e87ae6..3bb9cc21f7 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java @@ -3,6 +3,7 @@ import android.annotation.SuppressLint; import android.content.ContentValues; import android.content.Context; +import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.support.test.InstrumentationRegistry; @@ -103,42 +104,43 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { assertNull(nullValueFromDatabase); /* Count with scanner. */ - DatabaseManager.Scanner scanner = databaseManager.getScanner(); - assertEquals(2, scanner.getCount()); - assertEquals(2, scanner.getCount()); + Cursor cursor = databaseManager.getCursor(null, null, false); + assertEquals(2, cursor.getCount()); + assertEquals(2, cursor.getCount()); SQLiteQueryBuilder queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); queryBuilder.appendWhere("COL_STRING = ?"); - DatabaseManager.Scanner scanner1 = databaseManager.getScanner(queryBuilder, new String[]{value1.getAsString("COL_STRING")}, false); - assertEquals(1, scanner1.getCount()); - Iterator iterator = scanner1.iterator(); - assertContentValuesEquals(value1, iterator.next()); - assertFalse(iterator.hasNext()); + Cursor cursor1 = databaseManager.getCursor(queryBuilder, new String[]{value1.getAsString("COL_STRING")}, false); + assertEquals(1, cursor1.getCount()); + assertTrue(cursor1.moveToNext()); + assertContentValuesEquals(value1, databaseManager.buildValues(cursor1)); + assertFalse(cursor1.moveToNext()); /* Null value matching. */ queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); queryBuilder.appendWhere("COL_STRING IS NULL"); - assertEquals(0, databaseManager.getScanner(queryBuilder, null, false).getCount()); + assertEquals(0, databaseManager.getCursor(queryBuilder, null, false).getCount()); queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); queryBuilder.appendWhere("COL_STRING_NULL IS NULL"); - assertEquals(2, databaseManager.getScanner(queryBuilder, null, false).getCount()); + assertEquals(2, databaseManager.getCursor(queryBuilder, null, false).getCount()); /* Test null value filter does not exclude anything, so returns the 2 logs. */ queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); - scanner = databaseManager.getScanner(queryBuilder, null, false); - assertEquals(2, scanner.getCount()); + cursor = databaseManager.getCursor(queryBuilder, null, false); + assertEquals(2, cursor.getCount()); /* Test filtering only with the second key parameter to get only the second log. */ queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); queryBuilder.appendWhere("COL_STRING NOT IN (?)"); - scanner = databaseManager.getScanner(queryBuilder, new String[]{value1.getAsString("COL_STRING")}, false); - assertEquals(1, scanner.getCount()); - assertContentValuesEquals(value2, scanner.iterator().next()); + cursor = databaseManager.getCursor(queryBuilder, new String[]{value1.getAsString("COL_STRING")}, false); + assertEquals(1, cursor.getCount()); + assertTrue(cursor.moveToNext()); + assertContentValuesEquals(value2, databaseManager.buildValues(cursor)); /* Delete. */ databaseManager.delete(value1Id); assertNull(databaseManager.get(value1Id)); assertEquals(1, databaseManager.getRowCount()); - assertEquals(1, databaseManager.getScanner().getCount()); + assertEquals(1, databaseManager.getCursor(null, null, false).getCount()); /* Put logs to delete multiple IDs. */ ContentValues value4 = generateContentValues(); @@ -355,52 +357,6 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } - @Test(expected = UnsupportedOperationException.class) - public void databaseManagerScannerRemove() { - - /* Get instance to access database. */ - DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseManagerScannerRemove", "databaseManagerScannerRemove", 1, mSchema, new DatabaseManager.Listener() { - - @Override - public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - return false; - } - }); - - //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) - try { - databaseManager.getScanner().iterator().remove(); - } finally { - - /* Close. */ - //noinspection ThrowFromFinallyBlock - databaseManager.close(); - } - } - - @Test(expected = NoSuchElementException.class) - public void databaseManagerScannerNext() { - - /* Get instance to access database. */ - DatabaseManager databaseManager = new DatabaseManager(sContext, "test-databaseManagerScannerNext", "databaseManagerScannerNext", 1, mSchema, new DatabaseManager.Listener() { - - @Override - public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - return false; - } - }); - - //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) - try { - databaseManager.getScanner().iterator().next(); - } finally { - - /* Close. */ - //noinspection ThrowFromFinallyBlock - databaseManager.close(); - } - } - @Test public void setMaximumSize() { diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index 65f0724bb6..0266bc9914 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -2,6 +2,7 @@ import android.content.ContentValues; import android.content.Context; +import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.support.annotation.IntRange; @@ -333,9 +334,9 @@ public int countLogs(@NonNull String group) { /* Query database and get scanner. */ SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); builder.appendWhere(COLUMN_GROUP + " = ?"); - DatabaseManager.Scanner scanner = mDatabaseManager.getScanner(builder, new String[]{group}, true); - int count = scanner.getCount(); - scanner.close(); + Cursor cursor = mDatabaseManager.getCursor(builder, new String[]{group}, true); + int count = cursor.getCount(); + cursor.close(); return count; } @@ -367,9 +368,9 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT Map candidates = new TreeMap<>(); List failedDbIdentifiers = new ArrayList<>(); File largePayloadGroupDirectory = getLargePayloadGroupDirectory(group); - DatabaseManager.Scanner scanner = mDatabaseManager.getScanner(builder, selectionArgs, false); - for (Iterator iterator = scanner.iterator(); iterator.hasNext() && count < limit; ) { - ContentValues values = iterator.next(); + Cursor cursor = mDatabaseManager.getCursor(builder, selectionArgs, false); + while (cursor.moveToNext() && count < limit) { + ContentValues values = mDatabaseManager.buildValues(cursor); Long dbIdentifier = values.getAsLong(DatabaseManager.PRIMARY_KEY); /* @@ -380,8 +381,9 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT */ if (dbIdentifier == null) { AppCenterLog.error(LOG_TAG, "Empty database record, probably content was larger than 2MB, need to delete as it's now corrupted."); - DatabaseManager.Scanner idScanner = mDatabaseManager.getScanner(builder, selectionArgs, true); - for (ContentValues idValues : idScanner) { + Cursor idCursor = mDatabaseManager.getCursor(builder, selectionArgs, true); + while (idCursor.moveToNext()) { + ContentValues idValues = mDatabaseManager.buildValues(idCursor); Long invalidId = idValues.getAsLong(DatabaseManager.PRIMARY_KEY); if (!mPendingDbIdentifiers.contains(invalidId) && !candidates.containsKey(invalidId)) { @@ -391,7 +393,7 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT break; } } - idScanner.close(); + idCursor.close(); continue; } @@ -435,7 +437,7 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT } } } - scanner.close(); + cursor.close(); /* Delete any logs that cannot be de-serialized. */ if (failedDbIdentifiers.size() > 0) { diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index c3273687fa..f75ac9833a 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -19,11 +19,8 @@ import java.io.Closeable; import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; import static com.microsoft.appcenter.utils.AppCenterLog.LOG_TAG; @@ -134,7 +131,7 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { * @param cursor The cursor to be converted to an entry. * @return An entry converted from the cursor. */ - private static ContentValues buildValues(Cursor cursor, ContentValues schema) { + public ContentValues buildValues(Cursor cursor) { ContentValues values = new ContentValues(); for (int i = 0; i < cursor.getColumnCount(); i++) { if (cursor.isNull(i)) { @@ -144,7 +141,7 @@ private static ContentValues buildValues(Cursor cursor, ContentValues schema) { if (key.equals(PRIMARY_KEY)) { values.put(key, cursor.getLong(i)); } else { - Object specimen = schema.get(key); + Object specimen = mSchema.get(key); if (specimen instanceof byte[]) { values.put(key, cursor.getBlob(i)); } else if (specimen instanceof Double) { @@ -273,7 +270,7 @@ public ContentValues get(@Nullable String key, @Nullable Object value) { } } Cursor cursor = getCursor(builder, selectionArgs, false); - ContentValues values = cursor.moveToFirst() ? buildValues(cursor, mSchema) : null; + ContentValues values = cursor.moveToFirst() ? buildValues(cursor) : null; cursor.close(); return values; } catch (RuntimeException e) { @@ -282,28 +279,6 @@ public ContentValues get(@Nullable String key, @Nullable Object value) { return null; } - /** - * Gets a scanner for all data stored in the table. - * - * @return A scanner to iterate all values. - */ - @VisibleForTesting - Scanner getScanner() { - return getScanner(null, null, false); - } - - /** - * Gets a scanner for the given SQL query. - * - * @param queryBuilder The query builder that contains SQL query . - * @param selectionArgs The array of values for selection. - * @param idOnly Return only row identifier if true, return all fields otherwise. - * @return A scanner to iterate all values. - */ - public Scanner getScanner(SQLiteQueryBuilder queryBuilder, String[] selectionArgs, boolean idOnly) { - return new Scanner(queryBuilder, selectionArgs, idOnly); - } - /** * Clears the table in the database. */ @@ -350,7 +325,7 @@ public final long getRowCount() { * @return A cursor for all rows that matches the given criteria. * @throws RuntimeException If an error occurs. */ - Cursor getCursor(@Nullable SQLiteQueryBuilder queryBuilder, @Nullable String[] selectionArgs, boolean idOnly) throws RuntimeException { + public Cursor getCursor(@Nullable SQLiteQueryBuilder queryBuilder, @Nullable String[] selectionArgs, boolean idOnly) throws RuntimeException { if (queryBuilder == null) { queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); } @@ -460,128 +435,4 @@ public interface Listener { @SuppressWarnings({"BooleanMethodIsAlwaysInverted", "unused"}) boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion); } - - /** - * Scanner specification. - */ - public class Scanner implements Iterable, Closeable { - - /** - * First filter key. - */ - private final SQLiteQueryBuilder mQueryBuilder; - - /** - * Filter value for mQueryBuilder. - */ - private final String[] mSelectionArgs; - - /** - * Return only IDs flags (SQLite implementation only). - */ - private final boolean mIdOnly; - - /** - * SQLite cursor. - */ - private Cursor mCursor; - - /** - * Initializes a cursor with optional filter. - */ - private Scanner(SQLiteQueryBuilder queryBuilder, String[] selectionArgs, boolean idOnly) { - this.mQueryBuilder = queryBuilder; - this.mSelectionArgs = selectionArgs; - this.mIdOnly = idOnly; - } - - @Override - public void close() { - - /* Close cursor. */ - if (mCursor != null) { - try { - mCursor.close(); - mCursor = null; - } catch (RuntimeException e) { - AppCenterLog.error(AppCenter.LOG_TAG, "Failed to close the scanner.", e); - } - } - } - - @NonNull - @Override - public Iterator iterator() { - try { - - /* Close cursor first if it was being used. */ - close(); - mCursor = getCursor(mQueryBuilder, mSelectionArgs, mIdOnly); - - /* Wrap cursor as iterator. */ - return new Iterator() { - - /** - * If null, cursor needs to be moved to next. - */ - Boolean hasNext; - - @Override - public boolean hasNext() { - if (hasNext == null) { - try { - hasNext = mCursor.moveToNext(); - } catch (RuntimeException e) { - - /* Consider no next on errors. */ - hasNext = false; - - /* Close cursor. */ - try { - mCursor.close(); - } catch (RuntimeException e1) { - AppCenterLog.warn(AppCenter.LOG_TAG, "Closing cursor failed", e1); - } - mCursor = null; - } - } - return hasNext; - } - - @Override - public ContentValues next() { - - /* Check next. */ - if (!hasNext()) { - throw new NoSuchElementException(); - } - hasNext = null; - - /* Build object. */ - return buildValues(mCursor, mSchema); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } catch (RuntimeException e) { - AppCenterLog.error(AppCenter.LOG_TAG, "Failed to get iterator of the scanner.", e); - } - return Collections.emptyList().iterator(); - } - - public int getCount() { - try { - if (mCursor == null) { - mCursor = getCursor(mQueryBuilder, mSelectionArgs, mIdOnly); - } - return mCursor.getCount(); - } catch (RuntimeException e) { - AppCenterLog.error(AppCenter.LOG_TAG, "Failed to get count of the scanner.", e); - } - return -1; - } - } } From bc992de31d897d9623e9477e53f362db65139a62 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Fri, 26 Oct 2018 15:49:20 +0300 Subject: [PATCH 11/68] Restore thread safety --- .../persistence/DatabasePersistence.java | 62 ++++++++++++++----- .../utils/storage/DatabaseManager.java | 31 +++++++++- 2 files changed, 76 insertions(+), 17 deletions(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index 0266bc9914..9d0a1279a4 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -334,9 +334,14 @@ public int countLogs(@NonNull String group) { /* Query database and get scanner. */ SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); builder.appendWhere(COLUMN_GROUP + " = ?"); - Cursor cursor = mDatabaseManager.getCursor(builder, new String[]{group}, true); - int count = cursor.getCount(); - cursor.close(); + int count = 0; + try { + Cursor cursor = mDatabaseManager.getCursor(builder, new String[]{group}, true); + count = cursor.getCount(); + cursor.close(); + } catch (RuntimeException e) { + AppCenterLog.error(LOG_TAG, "Failed to get logs count: ", e); + } return count; } @@ -347,7 +352,7 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT /* Log. */ AppCenterLog.debug(LOG_TAG, "Trying to get " + limit + " logs from the Persistence database for " + group); - /* Query database and get scanner. */ + /* Query database. */ SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); builder.appendWhere(COLUMN_GROUP + " = ?"); String[] selectionArgs = new String[pausedTargetKeys.size() + 1]; @@ -368,9 +373,16 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT Map candidates = new TreeMap<>(); List failedDbIdentifiers = new ArrayList<>(); File largePayloadGroupDirectory = getLargePayloadGroupDirectory(group); - Cursor cursor = mDatabaseManager.getCursor(builder, selectionArgs, false); - while (cursor.moveToNext() && count < limit) { - ContentValues values = mDatabaseManager.buildValues(cursor); + Cursor cursor = null; + ContentValues values; + try { + cursor = mDatabaseManager.getCursor(builder, selectionArgs, false); + } catch (RuntimeException e) { + AppCenterLog.error(LOG_TAG, "Failed to get logs: ", e); + } + while (cursor != null && + (values = mDatabaseManager.nextValues(cursor)) != null && + count < limit) { Long dbIdentifier = values.getAsLong(DatabaseManager.PRIMARY_KEY); /* @@ -381,19 +393,16 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT */ if (dbIdentifier == null) { AppCenterLog.error(LOG_TAG, "Empty database record, probably content was larger than 2MB, need to delete as it's now corrupted."); - Cursor idCursor = mDatabaseManager.getCursor(builder, selectionArgs, true); - while (idCursor.moveToNext()) { - ContentValues idValues = mDatabaseManager.buildValues(idCursor); - Long invalidId = idValues.getAsLong(DatabaseManager.PRIMARY_KEY); - if (!mPendingDbIdentifiers.contains(invalidId) && !candidates.containsKey(invalidId)) { + List corruptedIds = getCorruptedIds(builder, selectionArgs); + for (Long corruptedId : corruptedIds) { + if (!mPendingDbIdentifiers.contains(corruptedId) && !candidates.containsKey(corruptedId)) { /* Found the record to delete that we could not read when selecting all fields. */ - deleteLog(largePayloadGroupDirectory, invalidId); - AppCenterLog.error(LOG_TAG, "Empty database corrupted empty record deleted, id=" + invalidId); + deleteLog(largePayloadGroupDirectory, corruptedId); + AppCenterLog.error(LOG_TAG, "Empty database corrupted empty record deleted, id=" + corruptedId); break; } } - idCursor.close(); continue; } @@ -437,7 +446,12 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT } } } - cursor.close(); + if (cursor != null) { + try { + cursor.close(); + } catch (RuntimeException ignore) { + } + } /* Delete any logs that cannot be de-serialized. */ if (failedDbIdentifiers.size() > 0) { @@ -493,4 +507,20 @@ public void clearPendingLogState() { public void close() { mDatabaseManager.close(); } + + private List getCorruptedIds(SQLiteQueryBuilder builder, String[] selectionArgs) { + List result = new ArrayList<>(); + try { + Cursor cursor = mDatabaseManager.getCursor(builder, selectionArgs, true); + while (cursor.moveToNext()) { + ContentValues idValues = mDatabaseManager.buildValues(cursor); + Long invalidId = idValues.getAsLong(DatabaseManager.PRIMARY_KEY); + result.add(invalidId); + } + cursor.close(); + } catch (RuntimeException e) { + AppCenterLog.error(LOG_TAG, "Failed to get corrupted ids: ", e); + } + return result; + } } \ No newline at end of file diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index f75ac9833a..0926226bd5 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -125,6 +125,24 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }; } + /** + * Get next entry from the cursor. + * + * @param cursor The cursor to be converted to an entry. + * @return An entry converted from the cursor. + */ + @Nullable + public ContentValues nextValues(Cursor cursor) { + try { + if (cursor.moveToNext()) { + return buildValues(cursor, mSchema); + } + } catch (RuntimeException e) { + AppCenterLog.error(LOG_TAG, "Failed to get next cursor value: ", e); + } + return null; + } + /** * Converts a cursor to an entry. * @@ -132,6 +150,17 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { * @return An entry converted from the cursor. */ public ContentValues buildValues(Cursor cursor) { + return buildValues(cursor, mSchema); + } + + /** + * Converts a cursor to an entry. + * + * @param cursor The cursor to be converted to an entry. + * @param schema The schema with value types. + * @return An entry converted from the cursor. + */ + private static ContentValues buildValues(Cursor cursor, ContentValues schema) { ContentValues values = new ContentValues(); for (int i = 0; i < cursor.getColumnCount(); i++) { if (cursor.isNull(i)) { @@ -141,7 +170,7 @@ public ContentValues buildValues(Cursor cursor) { if (key.equals(PRIMARY_KEY)) { values.put(key, cursor.getLong(i)); } else { - Object specimen = mSchema.get(key); + Object specimen = schema.get(key); if (specimen instanceof byte[]) { values.put(key, cursor.getBlob(i)); } else if (specimen instanceof Double) { From 8a74659bcd6aeadc8e55145798430776744b6a06 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Fri, 26 Oct 2018 17:15:10 +0300 Subject: [PATCH 12/68] Fix tests --- .../utils/storage/DatabaseManager.java | 2 +- .../appcenter/AbstractAppCenterTest.java | 14 +-- .../persistence/DatabasePersistenceTest.java | 72 ++++++++++----- .../utils/storage/DatabaseManagerTest.java | 87 ------------------- 4 files changed, 55 insertions(+), 120 deletions(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index 0926226bd5..319b03cb7c 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -135,7 +135,7 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { public ContentValues nextValues(Cursor cursor) { try { if (cursor.moveToNext()) { - return buildValues(cursor, mSchema); + return buildValues(cursor); } } catch (RuntimeException e) { AppCenterLog.error(LOG_TAG, "Failed to get next cursor value: ", e); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java index 3b1f939d2d..5f4ce5d06e 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java @@ -1,9 +1,9 @@ package com.microsoft.appcenter; import android.app.Application; -import android.content.ContentValues; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.database.Cursor; import android.database.sqlite.SQLiteQueryBuilder; import android.os.Bundle; import android.os.Handler; @@ -23,7 +23,6 @@ import com.microsoft.appcenter.utils.storage.DatabaseManager; import com.microsoft.appcenter.utils.storage.FileManager; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import com.microsoft.appcenter.utils.storage.FileManager; import org.junit.After; import org.junit.Before; @@ -36,15 +35,12 @@ import org.powermock.modules.junit4.rule.PowerMockRule; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import static com.microsoft.appcenter.AppCenter.KEY_VALUE_DELIMITER; import static com.microsoft.appcenter.AppCenter.TRANSMISSION_TARGET_TOKEN_KEY; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyCollectionOf; -import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -82,9 +78,6 @@ public class AbstractAppCenterTest { @Rule public PowerMockRule mPowerMockRule = new PowerMockRule(); - @Mock - private Iterator mDataBaseScannerIterator; - @Mock DefaultChannel mChannel; @@ -167,9 +160,8 @@ public Void answer(InvocationOnMock invocation) { /* Mock empty database. */ DatabaseManager databaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); - DatabaseManager.Scanner databaseScanner = mock(DatabaseManager.Scanner.class); - when(databaseManager.getScanner(any(SQLiteQueryBuilder.class), any(String[].class), anyBoolean())).thenReturn(databaseScanner); - when(databaseScanner.iterator()).thenReturn(mDataBaseScannerIterator); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyBoolean())) + .thenReturn(mock(Cursor.class)); /* Mock network state helper. */ when(NetworkStateHelper.getSharedInstance(any(Context.class))).thenReturn(mNetworkStateHelper); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java index 56ab04d870..09c11cc9e8 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java @@ -2,6 +2,8 @@ import android.content.ContentValues; import android.content.Context; +import android.database.Cursor; +import android.database.CursorWrapper; import android.database.sqlite.SQLiteQueryBuilder; import com.microsoft.appcenter.AppCenter; @@ -10,7 +12,6 @@ import com.microsoft.appcenter.ingestion.models.json.LogSerializer; import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.storage.DatabaseManager; -import com.microsoft.appcenter.utils.storage.SQLiteUtils; import org.json.JSONException; import org.junit.Rule; @@ -21,20 +22,15 @@ import org.powermock.modules.junit4.rule.PowerMockRule; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; -import static com.microsoft.appcenter.persistence.DatabasePersistence.COLUMN_GROUP; -import static com.microsoft.appcenter.persistence.DatabasePersistence.COLUMN_TARGET_KEY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyCollectionOf; import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; @@ -103,11 +99,13 @@ public void clearPendingLogState() throws Exception { /* Mock instances. */ DatabaseManager mockDatabaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(mockDatabaseManager); + when(mockDatabaseManager.nextValues(any(Cursor.class))).thenCallRealMethod(); for (int i = 0; i < groupCount; i++) { - DatabaseManager.Scanner mockDatabaseScanner = mock(DatabaseManager.Scanner.class); - when(mockDatabaseScanner.iterator()).thenReturn(list.get(i).iterator()); - when(mockDatabaseManager.getScanner(any(SQLiteQueryBuilder.class), eq(new String[]{String.valueOf(i)}), eq(false))).thenReturn(mockDatabaseScanner); + MockCursor mockCursor = new MockCursor(list.get(i)); + mockCursor.mockBuildValues(mockDatabaseManager); + when(mockDatabaseManager.getCursor(any(SQLiteQueryBuilder.class), eq(new String[]{String.valueOf(i)}), eq(false))) + .thenReturn(mockCursor); } LogSerializer mockLogSerializer = mock(LogSerializer.class); @@ -139,9 +137,10 @@ public void getLogsWithCorruption() throws Exception { int logCount = 3; DatabaseManager databaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); + when(databaseManager.nextValues(any(Cursor.class))).thenCallRealMethod(); /* Make 3 logs, the second one will be corrupted. */ - Collection fieldValues = new ArrayList<>(logCount); + List fieldValues = new ArrayList<>(logCount); { /* Valid record. */ ContentValues contentValues = mock(ContentValues.class); @@ -164,20 +163,20 @@ public void getLogsWithCorruption() throws Exception { } /* Mock log sequence retrieved from scanner. */ - DatabaseManager.Scanner databaseScanner = mock(DatabaseManager.Scanner.class); - when(databaseManager.getScanner(any(SQLiteQueryBuilder.class), any(String[].class), eq(false))).thenReturn(databaseScanner); - when(databaseScanner.iterator()).thenReturn(fieldValues.iterator()); + MockCursor mockCursor = new MockCursor(fieldValues); + mockCursor.mockBuildValues(databaseManager); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(false))).thenReturn(mockCursor); /* Mock second scanner with identifiers only. */ - Collection idValues = new ArrayList<>(logCount); + List idValues = new ArrayList<>(logCount); for (long i = 0; i < logCount; i++) { ContentValues contentValues = mock(ContentValues.class); when(contentValues.getAsLong(DatabaseManager.PRIMARY_KEY)).thenReturn(i); idValues.add(contentValues); } - DatabaseManager.Scanner idDatabaseScanner = mock(DatabaseManager.Scanner.class); - when(databaseManager.getScanner(any(SQLiteQueryBuilder.class), any(String[].class), eq(true))).thenReturn(idDatabaseScanner); - when(idDatabaseScanner.iterator()).thenReturn(idValues.iterator()); + MockCursor mockIdCursor = new MockCursor(idValues); + mockIdCursor.mockBuildValues(databaseManager); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(true))).thenReturn(mockIdCursor); /* Mock serializer and eventually the database. */ LogSerializer logSerializer = mock(LogSerializer.class); @@ -245,7 +244,9 @@ public Log answer(InvocationOnMock invocation) { when(contentValues.getAsString(DatabasePersistence.COLUMN_LOG)).thenReturn("true last"); fieldValues.add(contentValues); } - when(databaseScanner.iterator()).thenReturn(fieldValues.iterator()); + mockCursor = new MockCursor(fieldValues); + mockCursor.mockBuildValues(databaseManager); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(false))).thenReturn(mockCursor); idValues = new ArrayList<>(4); /* Here the id scanner will also skip the new corrupted log which id would be 3. */ @@ -254,7 +255,9 @@ public Log answer(InvocationOnMock invocation) { when(contentValues.getAsLong(DatabaseManager.PRIMARY_KEY)).thenReturn(i); idValues.add(contentValues); } - when(idDatabaseScanner.iterator()).thenReturn(idValues.iterator()); + mockIdCursor = new MockCursor(idValues); + mockIdCursor.mockBuildValues(databaseManager); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(true))).thenReturn(mockIdCursor); /* Verify next call is only the new valid log as others are marked pending. */ outLogs = new ArrayList<>(); @@ -272,8 +275,8 @@ public void checkSetStorageSizeForwarding() throws Exception { /* The real Android test for checking size is in DatabaseManagerAndroidTest. */ DatabaseManager databaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); - DatabaseManager.Scanner databaseScanner = mock(DatabaseManager.Scanner.class); - when(databaseManager.getScanner(any(SQLiteQueryBuilder.class), any(String[].class), anyBoolean())).thenReturn(databaseScanner); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyBoolean())) + .thenReturn(mock(Cursor.class)); when(databaseManager.setMaxSize(anyLong())).thenReturn(true).thenReturn(false); /* Just checks calls are forwarded to the low level database layer. */ @@ -281,4 +284,31 @@ public void checkSetStorageSizeForwarding() throws Exception { assertTrue(persistence.setMaxStorageSize(20480)); assertFalse(persistence.setMaxStorageSize(2)); } + + private static class MockCursor extends CursorWrapper { + + private final List mList; + + private int mIndex = -1; + + private MockCursor(List list) { + super(null); + mList = list; + } + + @Override + public boolean moveToNext() { + return ++mIndex < mList.size(); + } + + private void mockBuildValues(DatabaseManager databaseManager) { + when(databaseManager.buildValues(eq(this))).then(new Answer() { + + @Override + public ContentValues answer(InvocationOnMock invocation) { + return mList.get(mIndex); + } + }); + } + } } \ No newline at end of file diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java index 70485a7fe1..6a2638acbb 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java @@ -22,12 +22,10 @@ import java.util.Arrays; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -123,91 +121,6 @@ public void rowCountFailed() { AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } - @Test - public void scannerIteratorFailed() { - mockStatic(AppCenterLog.class); - DatabaseManager databaseManagerMock; - databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.getScanner().iterator(); - verifyStatic(); - AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); - } - - @Test - public void scannerCountFailed() { - mockStatic(AppCenterLog.class); - DatabaseManager databaseManagerMock; - databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.getScanner().getCount(); - verifyStatic(); - AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); - } - - @Test - public void scannerNextFailedButClosingWorks() { - mockStatic(AppCenterLog.class); - DatabaseManager databaseManagerMock; - - /* Cursor next failing but closing working. */ - databaseManagerMock = spy(new DatabaseManager(null, "database", "table", 1, null, null)); - when(databaseManagerMock.getDatabase()).thenReturn(mock(SQLiteDatabase.class)); - mockStatic(SQLiteUtils.class); - Cursor cursor = mock(Cursor.class); - SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class, new Returns(cursor)); - when(SQLiteUtils.newSQLiteQueryBuilder()).thenReturn(sqLiteQueryBuilder); - when(cursor.moveToNext()).thenThrow(new RuntimeException()); - DatabaseManager.Scanner scanner = databaseManagerMock.getScanner(); - assertFalse(scanner.iterator().hasNext()); - verifyStatic(never()); - AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); - - /* Verify closing failed. */ - verifyStatic(never()); - AppCenterLog.warn(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); - } - - @Test - public void scannerNextFailedAndClosingFails() { - mockStatic(AppCenterLog.class); - DatabaseManager databaseManagerMock; - - /* Cursor next failing and closing failing. */ - databaseManagerMock = spy(new DatabaseManager(null, "database", "table", 1, null, null)); - when(databaseManagerMock.getDatabase()).thenReturn(mock(SQLiteDatabase.class)); - mockStatic(SQLiteUtils.class); - Cursor cursor = mock(Cursor.class); - SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class, new Returns(cursor)); - when(SQLiteUtils.newSQLiteQueryBuilder()).thenReturn(sqLiteQueryBuilder); - when(cursor.moveToNext()).thenThrow(new RuntimeException()); - doThrow(new RuntimeException()).when(cursor).close(); - DatabaseManager.Scanner scanner = databaseManagerMock.getScanner(); - assertFalse(scanner.iterator().hasNext()); - verifyStatic(never()); - AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); - - /* Verify closing failed. */ - verifyStatic(); - AppCenterLog.warn(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); - } - - @Test - public void cursorCloseFailed() { - mockStatic(AppCenterLog.class); - DatabaseManager databaseManagerMock; - databaseManagerMock = spy(new DatabaseManager(null, "database", "table", 1, null, null)); - when(databaseManagerMock.getDatabase()).thenReturn(mock(SQLiteDatabase.class)); - mockStatic(SQLiteUtils.class); - Cursor cursor = mock(Cursor.class); - SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class, new Returns(cursor)); - when(SQLiteUtils.newSQLiteQueryBuilder()).thenReturn(sqLiteQueryBuilder); - doThrow(new RuntimeException()).when(cursor).close(); - DatabaseManager.Scanner scanner = databaseManagerMock.getScanner(); - assertFalse(scanner.iterator().hasNext()); - scanner.close(); - verifyStatic(); - AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); - } - @Test public void getDatabaseFailedOnce() { From b047596249dde22059fabe21f5af53e01eb97595 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 29 Oct 2018 12:09:27 +0300 Subject: [PATCH 13/68] Fix coverage --- .../persistence/DatabasePersistence.java | 10 +- .../utils/storage/DatabaseManager.java | 13 +- .../persistence/DatabasePersistenceTest.java | 141 +++++++++++++++++- 3 files changed, 146 insertions(+), 18 deletions(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index 9d0a1279a4..7f53e0f720 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -510,17 +510,23 @@ public void close() { private List getCorruptedIds(SQLiteQueryBuilder builder, String[] selectionArgs) { List result = new ArrayList<>(); + Cursor cursor = null; try { - Cursor cursor = mDatabaseManager.getCursor(builder, selectionArgs, true); + cursor = mDatabaseManager.getCursor(builder, selectionArgs, true); while (cursor.moveToNext()) { ContentValues idValues = mDatabaseManager.buildValues(cursor); Long invalidId = idValues.getAsLong(DatabaseManager.PRIMARY_KEY); result.add(invalidId); } - cursor.close(); } catch (RuntimeException e) { AppCenterLog.error(LOG_TAG, "Failed to get corrupted ids: ", e); } + if (cursor != null) { + try { + cursor.close(); + } catch (RuntimeException ignore) { + } + } return result; } } \ No newline at end of file diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index 319b03cb7c..31ac35747b 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -286,18 +286,11 @@ public ContentValues get(@IntRange(from = 0) long id) { * @param value The optional value for query. * @return A matching entry. */ - public ContentValues get(@Nullable String key, @Nullable Object value) { + public ContentValues get(String key, Object value) { try { SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); - String[] selectionArgs = null; - if (key != null) { - if (value == null) { - builder.appendWhere(key + " IS NULL"); - } else { - builder.appendWhere(key + " = ?"); - selectionArgs = new String[]{value.toString()}; - } - } + builder.appendWhere(key + " = ?"); + String[] selectionArgs = new String[]{value.toString()}; Cursor cursor = getCursor(builder, selectionArgs, false); ContentValues values = cursor.moveToFirst() ? buildValues(cursor) : null; cursor.close(); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java index 09c11cc9e8..5c50c322a4 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java @@ -71,11 +71,39 @@ public void databaseOperationException() throws Persistence.PersistenceException mockPersistence.close(); } - // There are two error logs on putLog and close + /* There are two error logs on putLog and close. */ verifyStatic(times(2)); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } + @Test + public void countLogsWithGetCountException() throws Exception { + + /* Mock instances. */ + mockStatic(AppCenterLog.class); + DatabaseManager mockDatabaseManager = mock(DatabaseManager.class); + whenNew(DatabaseManager.class).withAnyArguments().thenReturn(mockDatabaseManager); + Cursor mockCursor = mock(Cursor.class); + when(mockCursor.getCount()).thenThrow(new RuntimeException()); + when(mockDatabaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(true))).thenReturn(mockCursor); + DatabasePersistence persistence = new DatabasePersistence(mock(Context.class), 1, DatabasePersistence.SCHEMA); + + /* Try to get logs count. */ + //noinspection TryFinallyCanBeTryWithResources + try { + assertEquals(0, persistence.countLogs("test-p1")); + } finally { + + /* Close. */ + //noinspection ThrowFromFinallyBlock + persistence.close(); + } + + /* There is a error log. */ + verifyStatic(); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); + } + @Test public void clearPendingLogState() throws Exception { @@ -130,6 +158,87 @@ public void clearPendingLogState() throws Exception { assertEquals(0, persistence.mPendingDbIdentifiers.size()); } + @Test + public void getLogsWithGetCursorException() throws Exception { + + /* Mock instances. */ + mockStatic(AppCenterLog.class); + DatabaseManager databaseManager = mock(DatabaseManager.class); + whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); + when(databaseManager.nextValues(any(Cursor.class))).thenCallRealMethod(); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(false))).thenThrow(new RuntimeException()); + DatabasePersistence persistence = new DatabasePersistence(mock(Context.class), 1, DatabasePersistence.SCHEMA); + + /* Try to get logs. */ + ArrayList outLogs = new ArrayList<>(); + persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); + assertEquals(0, outLogs.size()); + + /* There is a error log. */ + verifyStatic(); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); + } + + @Test + public void getLogsWithMoveNextException() throws Exception { + + /* Mock instances. */ + mockStatic(AppCenterLog.class); + DatabaseManager databaseManager = mock(DatabaseManager.class); + whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); + when(databaseManager.nextValues(any(Cursor.class))).thenCallRealMethod(); + Cursor mockCursor = mock(Cursor.class); + when(mockCursor.moveToNext()).thenThrow(new RuntimeException()); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(false))).thenReturn(mockCursor); + DatabasePersistence persistence = new DatabasePersistence(mock(Context.class), 1, DatabasePersistence.SCHEMA); + + /* Try to get logs. */ + ArrayList outLogs = new ArrayList<>(); + persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); + assertEquals(0, outLogs.size()); + + /* There is a error log. */ + verifyStatic(); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); + } + + @Test + public void getLogsWithGetCorruptedIdsException() throws Exception { + + /* Mock instances. */ + mockStatic(AppCenterLog.class); + DatabaseManager databaseManager = mock(DatabaseManager.class); + whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); + when(databaseManager.nextValues(any(Cursor.class))).thenCallRealMethod(); + + /* Make corrupted log. */ + List fieldValues = new ArrayList<>(); + { + /* Empty record, "corrupted", cause identifier is null (and no other field either). */ + ContentValues contentValues = mock(ContentValues.class); + when(contentValues.getAsLong(DatabaseManager.PRIMARY_KEY)).thenReturn(null); + fieldValues.add(contentValues); + } + + /* Mock log sequence retrieved from cursor. */ + MockCursor mockCursor = new MockCursor(fieldValues); + mockCursor.mockBuildValues(databaseManager); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(false))).thenReturn(mockCursor); + + /* Mock second cursor with identifiers only. */ + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(true))).thenThrow(new RuntimeException()); + + /* Get logs and verify we get only non corrupted logs. */ + DatabasePersistence persistence = new DatabasePersistence(mock(Context.class)); + ArrayList outLogs = new ArrayList<>(); + persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); + assertEquals(0, outLogs.size()); + + /* There is a error log. */ + verifyStatic(); + AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); + } + @Test public void getLogsWithCorruption() throws Exception { @@ -162,12 +271,12 @@ public void getLogsWithCorruption() throws Exception { fieldValues.add(contentValues); } - /* Mock log sequence retrieved from scanner. */ + /* Mock log sequence retrieved from cursor. */ MockCursor mockCursor = new MockCursor(fieldValues); mockCursor.mockBuildValues(databaseManager); when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(false))).thenReturn(mockCursor); - /* Mock second scanner with identifiers only. */ + /* Mock second cursor with identifiers only. */ List idValues = new ArrayList<>(logCount); for (long i = 0; i < logCount; i++) { ContentValues contentValues = mock(ContentValues.class); @@ -244,18 +353,34 @@ public Log answer(InvocationOnMock invocation) { when(contentValues.getAsString(DatabasePersistence.COLUMN_LOG)).thenReturn("true last"); fieldValues.add(contentValues); } - mockCursor = new MockCursor(fieldValues); + mockCursor = new MockCursor(fieldValues) { + + @Override + public void close() { + + /* It should be ignored. */ + throw new RuntimeException(); + } + }; mockCursor.mockBuildValues(databaseManager); when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(false))).thenReturn(mockCursor); idValues = new ArrayList<>(4); - /* Here the id scanner will also skip the new corrupted log which id would be 3. */ + /* Here the id cursor will also skip the new corrupted log which id would be 3. */ for (long i = 0; i < logCount; i += 2) { ContentValues contentValues = mock(ContentValues.class); when(contentValues.getAsLong(DatabaseManager.PRIMARY_KEY)).thenReturn(i); idValues.add(contentValues); } - mockIdCursor = new MockCursor(idValues); + mockIdCursor = new MockCursor(idValues) { + + @Override + public void close() { + + /* It should be ignored. */ + throw new RuntimeException(); + } + }; mockIdCursor.mockBuildValues(databaseManager); when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(true))).thenReturn(mockIdCursor); @@ -301,6 +426,10 @@ public boolean moveToNext() { return ++mIndex < mList.size(); } + @Override + public void close() { + } + private void mockBuildValues(DatabaseManager databaseManager) { when(databaseManager.buildValues(eq(this))).then(new Answer() { From 5842ca14b7653d2d39e56d78236cb946094abd48 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 29 Oct 2018 12:27:03 +0300 Subject: [PATCH 14/68] Fix lint issues --- .../java/com/microsoft/appcenter/crashes/Crashes.java | 8 ++++---- .../src/main/java/com/microsoft/appcenter/AppCenter.java | 1 - .../appcenter/ingestion/models/one/MetadataExtension.java | 2 +- .../appcenter/persistence/DatabasePersistence.java | 2 +- .../com/microsoft/appcenter/persistence/Persistence.java | 1 + .../appcenter/utils/storage/SharedPreferencesManager.java | 3 ++- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java index 4a65397d5e..2f6f6122e7 100644 --- a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java +++ b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java @@ -753,10 +753,10 @@ ErrorReport buildErrorReport(ManagedErrorLog log) { ErrorReport report = ErrorLogHelper.getErrorReportFromErrorLog(log, throwable); mErrorReportCache.put(id, new ErrorLogReport(log, report)); return report; - } catch (ClassNotFoundException ignored) { - AppCenterLog.error(LOG_TAG, "Cannot read throwable file " + file.getName(), ignored); - } catch (IOException ignored) { - AppCenterLog.error(LOG_TAG, "Cannot access serialized throwable file " + file.getName(), ignored); + } catch (ClassNotFoundException e) { + AppCenterLog.error(LOG_TAG, "Cannot read throwable file " + file.getName(), e); + } catch (IOException e) { + AppCenterLog.error(LOG_TAG, "Cannot access serialized throwable file " + file.getName(), e); } } } diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java index 9f14cf8289..eafb9008ce 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java @@ -31,7 +31,6 @@ import com.microsoft.appcenter.utils.async.DefaultAppCenterFuture; import com.microsoft.appcenter.utils.storage.FileManager; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import com.microsoft.appcenter.utils.storage.FileManager; import java.util.ArrayList; import java.util.Collection; diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/ingestion/models/one/MetadataExtension.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/ingestion/models/one/MetadataExtension.java index 7f91302b2b..57251945a4 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/ingestion/models/one/MetadataExtension.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/ingestion/models/one/MetadataExtension.java @@ -28,7 +28,7 @@ public JSONObject getMetadata() { } @Override - public void read(JSONObject object) throws JSONException { + public void read(JSONObject object) { mMetadata = object; } diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index 7f53e0f720..e9f2601db6 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -365,7 +365,7 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT filter.deleteCharAt(filter.length() - 1); builder.appendWhere(" AND "); builder.appendWhere(COLUMN_TARGET_KEY + " NOT IN (" + filter.toString() + ")"); - System.arraycopy(pausedTargetKeys.toArray(), 0, selectionArgs, 1, pausedTargetKeys.size()); + System.arraycopy(pausedTargetKeys.toArray(new String[0]), 0, selectionArgs, 1, pausedTargetKeys.size()); } /* Add logs to output parameter after deserialization if logs are not already sent. */ diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/Persistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/Persistence.java index 578ff88855..f37cd429a1 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/Persistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/Persistence.java @@ -108,6 +108,7 @@ public PersistenceException(String detailMessage, Throwable throwable) { super(detailMessage, throwable); } + @SuppressWarnings("SameParameterValue") PersistenceException(String detailMessage) { super(detailMessage); } diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManager.java index bae09c763b..bd02bbcb3f 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/SharedPreferencesManager.java @@ -4,6 +4,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import java.util.Set; @@ -188,7 +189,7 @@ public static void putLong(@NonNull String key, long value) { * @param key The key for which the value is to be retrieved. * @return The value of {@code key} or {@code null} if key is not set. */ - @SuppressWarnings("unused") + @Nullable public static String getString(@NonNull String key) { return getString(key, null); } From c039c7b4be26a3adbb7e83906bae3477e3737910 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 29 Oct 2018 12:36:01 +0300 Subject: [PATCH 15/68] Fix test app build with jcenter flavor --- .../appcenter/sasquatch/activities/SettingsActivity.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivity.java b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivity.java index 1c4456200d..cdde9bc7ac 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivity.java +++ b/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/SettingsActivity.java @@ -35,7 +35,6 @@ import com.microsoft.appcenter.sasquatch.eventfilter.EventFilter; import com.microsoft.appcenter.utils.PrefStorageConstants; import com.microsoft.appcenter.utils.async.AppCenterFuture; -import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import java.io.File; import java.lang.reflect.Method; @@ -367,7 +366,8 @@ public boolean onPreferenceClick(final Preference preference) { public void onClick(DialogInterface dialog, int which) { if (input.getText().toString().matches(UUID_FORMAT_REGEX)) { UUID uuid = UUID.fromString(input.getText().toString()); - SharedPreferencesManager.putString(PrefStorageConstants.KEY_INSTALL_ID, uuid.toString()); + SharedPreferences appCenterPreferences = getActivity().getSharedPreferences("AppCenter", Context.MODE_PRIVATE); + appCenterPreferences.edit().putString(PrefStorageConstants.KEY_INSTALL_ID, uuid.toString()).apply(); Toast.makeText(getActivity(), String.format(getActivity().getString(R.string.install_id_changed_format), uuid.toString()), Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getActivity(), R.string.install_id_invalid, Toast.LENGTH_SHORT).show(); @@ -492,7 +492,8 @@ public void onClick(DialogInterface dialog, int which) { @Override public boolean onPreferenceClick(Preference preference) { - SharedPreferencesManager.remove(Crashes.PREF_KEY_ALWAYS_SEND); + SharedPreferences appCenterPreferences = getActivity().getSharedPreferences("AppCenter", Context.MODE_PRIVATE); + appCenterPreferences.edit().remove(Crashes.PREF_KEY_ALWAYS_SEND).apply(); Toast.makeText(getActivity(), R.string.clear_crash_user_confirmation_toast, Toast.LENGTH_SHORT).show(); return true; } From 1236ea8a33329e587aaed13993d2119f59db5ad0 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Mon, 29 Oct 2018 16:26:49 -0700 Subject: [PATCH 16/68] Move test only utils from SDK to tests and use finally for close --- .../DatabasePersistenceAndroidTest.java | 42 ++++--- .../storage/DatabaseManagerAndroidTest.java | 52 ++++++--- .../persistence/DatabasePersistence.java | 31 ++--- .../utils/storage/DatabaseManager.java | 107 +++++------------- .../persistence/DatabasePersistenceTest.java | 20 ++-- .../utils/storage/DatabaseManagerTest.java | 10 -- 6 files changed, 121 insertions(+), 141 deletions(-) diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index c4ed0c40ce..429420db7f 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -43,7 +43,6 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; @@ -86,14 +85,6 @@ public static void setUpClass() { Constants.loadFromContext(sContext); } - private static int getCursorSize(Cursor cursor) { - int count = 0; - while (cursor.moveToNext()) { - count++; - } - return count; - } - @Before public void setUp() { @@ -399,10 +390,13 @@ public void putLogLargerThanMaxSizeClearsEverything() throws PersistenceExceptio Map properties = new HashMap<>(); properties.put("key", largeValue.toString()); log.setProperties(properties); - long id = persistence.putLog("test-p1", log); + try { + persistence.putLog("test-p1", log); + fail("Expected persistence exception"); + } catch (PersistenceException ignore) { + } /* Verify the behavior: not inserted and database now empty. */ - assertEquals(-1, id); assertEquals(0, persistence.countLogs("test-p1")); } finally { @@ -487,9 +481,9 @@ public void deleteLogs() throws PersistenceException { try { /* Verify. */ - assertEquals(2, getCursorSize(cursor1)); - assertEquals(1, getCursorSize(cursor2)); - assertEquals(1, getCursorSize(cursor3)); + assertEquals(2, cursor1.getCount()); + assertEquals(1, cursor2.getCount()); + assertEquals(1, cursor3.getCount()); } finally { /* Close. */ @@ -508,7 +502,7 @@ public void deleteLogs() throws PersistenceException { try { /* Verify. */ - assertEquals(0, getCursorSize(cursor4)); + assertEquals(0, cursor4.getCount()); } finally { /* Close. */ @@ -565,7 +559,9 @@ public void deleteLogsForGroup() throws PersistenceException { /* Verify. */ Map> pendingGroups = persistence.mPendingDbIdentifiersGroups; assertNull(pendingGroups.get("test-p1" + id1)); - assertEquals(1, pendingGroups.get("test-p2" + id2).size()); + List p2Logs = pendingGroups.get("test-p2" + id2); + assertNotNull(p2Logs); + assertEquals(1, p2Logs.size()); assertEquals(1, pendingGroups.size()); assertEquals(0, outputLogs.size()); assertEquals(1, persistence.mDatabaseManager.getRowCount()); @@ -842,7 +838,12 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { assertEquals(commonSchemaLog, outputLogs.get(0)); /* Verify target token is encrypted. */ - ContentValues values = persistence.mDatabaseManager.get(DatabasePersistence.COLUMN_GROUP, "test/one"); + SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); + builder.appendWhere(DatabasePersistence.COLUMN_GROUP + " = ?"); + String[] selectionArgs = new String[]{"test/one"}; + Cursor cursor = persistence.mDatabaseManager.getCursor(builder, selectionArgs, false); + ContentValues values = persistence.mDatabaseManager.nextValues(cursor); + assertNotNull(values); String token = values.getAsString(DatabasePersistence.COLUMN_TARGET_TOKEN); assertNotNull(token); assertNotEquals("test-guid", token); @@ -928,7 +929,12 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { assertEquals(commonSchemaLog, outputLogs.get(0)); /* Verify target token is encrypted. */ - ContentValues values = persistence.mDatabaseManager.get(DatabasePersistence.COLUMN_GROUP, "test/one"); + SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); + builder.appendWhere(DatabasePersistence.COLUMN_GROUP + " = ?"); + String[] selectionArgs = new String[]{"test/one"}; + Cursor cursor = persistence.mDatabaseManager.getCursor(builder, selectionArgs, false); + ContentValues values = persistence.mDatabaseManager.nextValues(cursor); + assertNotNull(values); String token = values.getAsString(DatabasePersistence.COLUMN_TARGET_TOKEN); assertNotNull(token); assertNotEquals("test-guid", token); diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java index 3bb9cc21f7..3f9bfbf07a 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java @@ -16,8 +16,6 @@ import org.junit.runner.RunWith; import java.util.Arrays; -import java.util.Iterator; -import java.util.NoSuchElementException; import java.util.Random; import static com.microsoft.appcenter.utils.storage.DatabaseManager.ALLOWED_SIZE_MULTIPLE; @@ -78,6 +76,23 @@ public static void tearDownClass() { sContext.deleteDatabase("test-setMaximumSize"); } + @SuppressWarnings("TryFinallyCanBeTryWithResources") + private static ContentValues get(DatabaseManager databaseManager, long id) { + try { + SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); + builder.appendWhere(DatabaseManager.PRIMARY_KEY + " = ?"); + String[] selectionArgs = new String[]{String.valueOf(id)}; + Cursor cursor = databaseManager.getCursor(builder, selectionArgs, false); + try { + return databaseManager.nextValues(cursor); + } finally { + cursor.close(); + } + } catch (RuntimeException e) { + return null; + } + } + @SuppressWarnings("SpellCheckingInspection") private static void runDatabaseManagerTest(DatabaseManager databaseManager) { ContentValues value1 = generateContentValues(); @@ -95,12 +110,12 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { /* Generate an ID that is neither value1Id nor value2Id. */ /* Get. */ - ContentValues value1FromDatabase = databaseManager.get(value1Id); + ContentValues value1FromDatabase = get(databaseManager, value1Id); assertContentValuesEquals(value1, value1FromDatabase); - ContentValues value2FromDatabase = databaseManager.get(DatabaseManager.PRIMARY_KEY, value2Id); + ContentValues value2FromDatabase = get(databaseManager, value2Id); assertContentValuesEquals(value2, value2FromDatabase); //noinspection ResourceType - ContentValues nullValueFromDatabase = databaseManager.get(-1); + ContentValues nullValueFromDatabase = get(databaseManager, -1); assertNull(nullValueFromDatabase); /* Count with scanner. */ @@ -138,7 +153,7 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { /* Delete. */ databaseManager.delete(value1Id); - assertNull(databaseManager.get(value1Id)); + assertNull(get(databaseManager, value1Id)); assertEquals(1, databaseManager.getRowCount()); assertEquals(1, databaseManager.getCursor(null, null, false).getCount()); @@ -152,8 +167,8 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { /* Delete multiple logs. */ databaseManager.delete(Arrays.asList(value4Id, value5Id)); - assertNull(databaseManager.get(value4Id)); - assertNull(databaseManager.get(value5Id)); + assertNull(get(databaseManager, value4Id)); + assertNull(get(databaseManager, value5Id)); assertEquals(1, databaseManager.getRowCount()); /* Put logs to delete with condition. */ @@ -169,7 +184,7 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { /* Delete logs with condition. */ databaseManager.delete("COL_STRING", value2.getAsString("COL_STRING")); assertEquals(1, databaseManager.getRowCount()); - ContentValues value7FromDatabase = databaseManager.get(value7Id); + ContentValues value7FromDatabase = get(databaseManager, value7Id); assertContentValuesEquals(value7, value7FromDatabase); /* Clear. */ @@ -196,6 +211,7 @@ private static ContentValues generateContentValues() { } private static void assertContentValuesEquals(ContentValues expected, ContentValues actual) { + assertNotNull(actual); assertEquals(expected.getAsString("COL_STRING"), actual.getAsString("COL_STRING")); assertEquals(expected.getAsString("COL_STRING_NULL"), actual.getAsString("COL_STRING_NULL")); assertEquals(expected.getAsByte("COL_BYTE"), actual.getAsByte("COL_BYTE")); @@ -253,11 +269,13 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { try { /* Database will always create a column for identifiers so default length of all tables is 1. */ - assertEquals(2, databaseManager.getColumnNames().length); + Cursor cursor = databaseManager.getCursor(SQLiteUtils.newSQLiteQueryBuilder(), null, false); + assertEquals(2, cursor.getColumnCount()); long id = databaseManager.put(oldVersionValue); /* Put data. */ - ContentValues actual = databaseManager.get(id); + ContentValues actual = get(databaseManager, id); + assertNotNull(actual); actual.remove("oid"); assertEquals(oldVersionValue, actual); assertEquals(1, databaseManager.getRowCount()); @@ -278,7 +296,8 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /* Verify data deleted since no handled upgrade. */ try { - assertEquals(11, databaseManager.getColumnNames().length); + Cursor cursor = databaseManager.getCursor(SQLiteUtils.newSQLiteQueryBuilder(), null, false); + assertEquals(11, cursor.getColumnCount()); assertEquals(0, databaseManager.getRowCount()); } finally { @@ -311,7 +330,8 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { long id; try { id = databaseManager.put(oldVersionValue); - ContentValues actual = databaseManager.get(id); + ContentValues actual = get(databaseManager, id); + assertNotNull(actual); actual.remove("oid"); assertEquals(oldVersionValue, actual); assertEquals(1, databaseManager.getRowCount()); @@ -336,7 +356,8 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { try { /* Verify data still there. */ - ContentValues actual = databaseManager.get(id); + ContentValues actual = get(databaseManager, id); + assertNotNull(actual); actual.remove("oid"); assertEquals(oldVersionValue, actual); assertEquals(1, databaseManager.getRowCount()); @@ -346,7 +367,8 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { data.put("COL_STRING", "Hello World"); data.put("COL_INT", 2); id = databaseManager.put(data); - actual = databaseManager.get(id); + actual = get(databaseManager, id); + assertNotNull(actual); actual.remove("oid"); assertEquals(data, actual); assertEquals(2, databaseManager.getRowCount()); diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index e9f2601db6..2648fb5e80 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -37,6 +37,7 @@ import static com.microsoft.appcenter.AppCenter.LOG_TAG; +@SuppressWarnings("TryFinallyCanBeTryWithResources") public class DatabasePersistence extends Persistence { /** @@ -232,7 +233,7 @@ public long putLog(@NonNull String group, @NonNull Log log) throws PersistenceEx contentValues = getContentValues(group, isLargePayload ? null : payload, targetToken, log.getType(), targetKey); long databaseId = mDatabaseManager.put(contentValues); if (databaseId == -1) { - AppCenterLog.warn(LOG_TAG, "Failed to store a log to the Persistence database for log type " + log.getType() + "."); + throw new PersistenceException("Failed to store a log to the Persistence database for log type " + log.getType() + "."); } AppCenterLog.debug(LOG_TAG, "Stored a log to the Persistence database for log type " + log.getType() + " with databaseId=" + databaseId); if (isLargePayload) { @@ -337,8 +338,11 @@ public int countLogs(@NonNull String group) { int count = 0; try { Cursor cursor = mDatabaseManager.getCursor(builder, new String[]{group}, true); - count = cursor.getCount(); - cursor.close(); + try { + count = cursor.getCount(); + } finally { + cursor.close(); + } } catch (RuntimeException e) { AppCenterLog.error(LOG_TAG, "Failed to get logs count: ", e); } @@ -510,22 +514,19 @@ public void close() { private List getCorruptedIds(SQLiteQueryBuilder builder, String[] selectionArgs) { List result = new ArrayList<>(); - Cursor cursor = null; try { - cursor = mDatabaseManager.getCursor(builder, selectionArgs, true); - while (cursor.moveToNext()) { - ContentValues idValues = mDatabaseManager.buildValues(cursor); - Long invalidId = idValues.getAsLong(DatabaseManager.PRIMARY_KEY); - result.add(invalidId); - } - } catch (RuntimeException e) { - AppCenterLog.error(LOG_TAG, "Failed to get corrupted ids: ", e); - } - if (cursor != null) { + Cursor cursor = mDatabaseManager.getCursor(builder, selectionArgs, true); try { + while (cursor.moveToNext()) { + ContentValues idValues = mDatabaseManager.buildValues(cursor); + Long invalidId = idValues.getAsLong(DatabaseManager.PRIMARY_KEY); + result.add(invalidId); + } + } finally { cursor.close(); - } catch (RuntimeException ignore) { } + } catch (RuntimeException e) { + AppCenterLog.error(LOG_TAG, "Failed to get corrupted ids: ", e); } return result; } diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index 31ac35747b..96f246fb75 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -126,34 +126,6 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } /** - * Get next entry from the cursor. - * - * @param cursor The cursor to be converted to an entry. - * @return An entry converted from the cursor. - */ - @Nullable - public ContentValues nextValues(Cursor cursor) { - try { - if (cursor.moveToNext()) { - return buildValues(cursor); - } - } catch (RuntimeException e) { - AppCenterLog.error(LOG_TAG, "Failed to get next cursor value: ", e); - } - return null; - } - - /** - * Converts a cursor to an entry. - * - * @param cursor The cursor to be converted to an entry. - * @return An entry converted from the cursor. - */ - public ContentValues buildValues(Cursor cursor) { - return buildValues(cursor, mSchema); - } - - /** * Converts a cursor to an entry. * * @param cursor The cursor to be converted to an entry. @@ -193,6 +165,34 @@ private static ContentValues buildValues(Cursor cursor, ContentValues schema) { return values; } + /** + * Converts a cursor to an entry. + * + * @param cursor The cursor to be converted to an entry. + * @return An entry converted from the cursor. + */ + public ContentValues buildValues(Cursor cursor) { + return buildValues(cursor, mSchema); + } + + /** + * Get next entry from the cursor. + * + * @param cursor The cursor to be converted to an entry. + * @return An entry converted from the cursor. + */ + @Nullable + public ContentValues nextValues(Cursor cursor) { + try { + if (cursor.moveToNext()) { + return buildValues(cursor); + } + } catch (RuntimeException e) { + AppCenterLog.error(LOG_TAG, "Failed to get next cursor value: ", e); + } + return null; + } + /** * Stores the entry to the table. If the table is full, the oldest logs are discarded until the * new one can fit. If the log is larger than the max table size, database will be cleared and @@ -269,38 +269,6 @@ public void delete(@Nullable String key, @Nullable Object value) { } } - /** - * Gets the entry by the identifier. - * - * @param id The database identifier. - * @return An entry for the identifier or null if not found. - */ - public ContentValues get(@IntRange(from = 0) long id) { - return get(PRIMARY_KEY, id); - } - - /** - * Gets the entry that matches key == value. - * - * @param key The optional key for query. - * @param value The optional value for query. - * @return A matching entry. - */ - public ContentValues get(String key, Object value) { - try { - SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); - builder.appendWhere(key + " = ?"); - String[] selectionArgs = new String[]{value.toString()}; - Cursor cursor = getCursor(builder, selectionArgs, false); - ContentValues values = cursor.moveToFirst() ? buildValues(cursor) : null; - cursor.close(); - return values; - } catch (RuntimeException e) { - AppCenterLog.error(AppCenter.LOG_TAG, String.format("Failed to get values that match key=\"%s\" and value=\"%s\" from database.", key, value), e); - } - return null; - } - /** * Clears the table in the database. */ @@ -334,8 +302,8 @@ public final long getRowCount() { return DatabaseUtils.queryNumEntries(getDatabase(), mTable); } catch (RuntimeException e) { AppCenterLog.error(AppCenter.LOG_TAG, "Failed to get row count of database.", e); + return -1; } - return -1; } /** @@ -389,20 +357,6 @@ void setSQLiteOpenHelper(@NonNull SQLiteOpenHelper helper) { mSQLiteOpenHelper = helper; } - /** - * Gets an array of column names in the table. - * - * @return An array of column names. - */ - @VisibleForTesting - String[] getColumnNames() { - - // TODO: Below line doesn't look efficient to get column names. Could use "PRAGMA table_info(table-name)" to avoid getting all data back from database just to get column names. - SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); - builder.setTables(mTable); - return builder.query(getDatabase(), null, null, null, null, null, PRIMARY_KEY).getColumnNames(); - } - /** * Set maximum SQLite database size. * @@ -434,7 +388,8 @@ public boolean setMaxSize(long maxStorageSizeInBytes) { * * @return The maximum size of database in bytes. */ - public long getMaxSize() { + @VisibleForTesting + long getMaxSize() { return getDatabase().getMaximumSize(); } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java index 5c50c322a4..af4d5dd563 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java @@ -4,6 +4,7 @@ import android.content.Context; import android.database.Cursor; import android.database.CursorWrapper; +import android.database.sqlite.SQLiteDiskIOException; import android.database.sqlite.SQLiteQueryBuilder; import com.microsoft.appcenter.AppCenter; @@ -28,6 +29,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyLong; @@ -51,7 +53,7 @@ public class DatabasePersistenceTest { public PowerMockRule mPowerMockRule = new PowerMockRule(); @Test - public void databaseOperationException() throws Persistence.PersistenceException, JSONException { + public void databaseOperationException() throws JSONException { /* Mock instances. */ mockStatic(AppCenterLog.class); @@ -64,6 +66,8 @@ public void databaseOperationException() throws Persistence.PersistenceException /* Generate a log and persist. */ Log log = mock(Log.class); mockPersistence.putLog("test-p1", log); + fail("Expected persistence exception"); + } catch (Persistence.PersistenceException ignore) { } finally { /* Close. */ @@ -99,7 +103,7 @@ public void countLogsWithGetCountException() throws Exception { persistence.close(); } - /* There is a error log. */ + /* There is an error log. */ verifyStatic(); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -108,7 +112,7 @@ public void countLogsWithGetCountException() throws Exception { public void clearPendingLogState() throws Exception { /* groupCount should be <= 9. */ - final int groupCount = 1; + final int groupCount = 4; final int logCount = 10; /* Mock logs. */ @@ -174,7 +178,7 @@ public void getLogsWithGetCursorException() throws Exception { persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); assertEquals(0, outLogs.size()); - /* There is a error log. */ + /* There is an error log. */ verifyStatic(); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -197,7 +201,7 @@ public void getLogsWithMoveNextException() throws Exception { persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); assertEquals(0, outLogs.size()); - /* There is a error log. */ + /* There is an error log. */ verifyStatic(); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -226,7 +230,9 @@ public void getLogsWithGetCorruptedIdsException() throws Exception { when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(false))).thenReturn(mockCursor); /* Mock second cursor with identifiers only. */ - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(true))).thenThrow(new RuntimeException()); + Cursor failingCursor = mock(Cursor.class); + when(failingCursor.moveToNext()).thenThrow(new SQLiteDiskIOException()); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(true))).thenReturn(failingCursor); /* Get logs and verify we get only non corrupted logs. */ DatabasePersistence persistence = new DatabasePersistence(mock(Context.class)); @@ -234,7 +240,7 @@ public void getLogsWithGetCorruptedIdsException() throws Exception { persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); assertEquals(0, outLogs.size()); - /* There is a error log. */ + /* There is an error log. */ verifyStatic(); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java index 6a2638acbb..555ef7ffc1 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java @@ -50,16 +50,6 @@ private static DatabaseManager getDatabaseManagerMock() { @Test public void putFailed() { - mockStatic(AppCenterLog.class); - DatabaseManager databaseManagerMock; - databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.get(0); - verifyStatic(); - AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); - } - - @Test - public void getFailed() { mockStatic(AppCenterLog.class); DatabaseManager databaseManagerMock; databaseManagerMock = getDatabaseManagerMock(); From 9f6c9482c5def070ecf42f677a56c959c7e65129 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Mon, 29 Oct 2018 18:51:59 -0700 Subject: [PATCH 17/68] Add priority to database persistence --- .../DatabasePersistenceAndroidTest.java | 187 ++++++++++++++---- .../persistence/PersistenceAndroidTest.java | 4 +- .../java/com/microsoft/appcenter/Flags.java | 22 +++ .../appcenter/channel/DefaultChannel.java | 4 +- .../persistence/DatabasePersistence.java | 31 ++- .../appcenter/persistence/Persistence.java | 7 +- .../DefaultChannelAlternateIngestionTest.java | 7 +- .../DefaultChannelOtherOperationsTest.java | 8 +- .../DefaultChannelPauseResumeTest.java | 7 +- .../appcenter/channel/DefaultChannelTest.java | 27 +-- .../persistence/DatabasePersistenceTest.java | 3 +- 11 files changed, 236 insertions(+), 71 deletions(-) create mode 100644 sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index 429420db7f..e9ce9ea4f3 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -6,6 +6,7 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; +import android.support.annotation.NonNull; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; @@ -46,6 +47,8 @@ import java.util.List; import java.util.Map; +import static com.microsoft.appcenter.Flags.PERSISTENCE_CRITICAL; +import static com.microsoft.appcenter.Flags.PERSISTENCE_NORMAL; import static com.microsoft.appcenter.ingestion.models.json.MockLog.MOCK_LOG_TYPE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -109,7 +112,7 @@ public void putLog() throws PersistenceException { /* Generate a log and persist. */ Log log = AndroidTestUtils.generateMockLog(); - persistence.putLog("test-p1", log); + persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); /* Count logs. */ assertEquals(1, persistence.countLogs("test-p1")); @@ -150,7 +153,7 @@ public void putLargeLogAndDeleteAll() throws PersistenceException { Map properties = new HashMap<>(); properties.put("key", largeValue.toString()); log.setProperties(properties); - long id = persistence.putLog("test-p1", log); + long id = persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); /* Count logs. */ assertEquals(1, persistence.countLogs("test-p1")); @@ -208,7 +211,7 @@ public void putLargeLogFails() { Map properties = new HashMap<>(); properties.put("key", largeValue.toString()); log.setProperties(properties); - persistence.putLog("test-p1", log); + persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); fail("putLog was expected to fail"); } catch (Persistence.PersistenceException e) { assertTrue(e.getCause() instanceof IOException); @@ -248,7 +251,7 @@ public void putLargeLogFailsToRead() throws PersistenceException { Map properties = new HashMap<>(); properties.put("key", largeValue.toString()); log.setProperties(properties); - long id = persistence.putLog("test-p1", log); + long id = persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); assertEquals(1, persistence.countLogs("test-p1")); /* Verify large file. */ @@ -303,7 +306,7 @@ public void putLargeLogNotSupportedOnCommonSchema() throws JSONException { /* Persisting that log should fail. */ try { - persistence.putLog("test-p1", log); + persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); fail("Inserting large common schema log is not supposed to work"); } catch (PersistenceException e) { @@ -331,7 +334,7 @@ public void putTooManyLogs() throws PersistenceException { /* Generate some logs that will be evicted. */ for (int i = 0; i < 10; i++) { - persistence.putLog("test-p1", AndroidTestUtils.generateMockLog()); + persistence.putLog("test-p1", AndroidTestUtils.generateMockLog(), PERSISTENCE_NORMAL); } /* @@ -341,7 +344,7 @@ public void putTooManyLogs() throws PersistenceException { List expectedLogs = new ArrayList<>(); for (int i = 0; i < 6; i++) { MockLog log = AndroidTestUtils.generateMockLog(); - persistence.putLog("test-p1", log); + persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); expectedLogs.add(log); } @@ -373,7 +376,7 @@ public void putLogLargerThanMaxSizeClearsEverything() throws PersistenceExceptio /* Generate some logs that will be evicted. */ int someLogCount = 3; for (int i = 0; i < someLogCount; i++) { - persistence.putLog("test-p1", AndroidTestUtils.generateMockLog()); + persistence.putLog("test-p1", AndroidTestUtils.generateMockLog(), PERSISTENCE_NORMAL); } assertEquals(someLogCount, persistence.countLogs("test-p1")); @@ -391,7 +394,7 @@ public void putLogLargerThanMaxSizeClearsEverything() throws PersistenceExceptio properties.put("key", largeValue.toString()); log.setProperties(properties); try { - persistence.putLog("test-p1", log); + persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); fail("Expected persistence exception"); } catch (PersistenceException ignore) { } @@ -419,7 +422,7 @@ public void putLogException() throws PersistenceException, JSONException { /* Generate a log and persist. */ Log log = AndroidTestUtils.generateMockLog(); - persistence.putLog("test-p1", log); + persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); } finally { persistence.close(); } @@ -442,10 +445,10 @@ public void deleteLogs() throws PersistenceException { Log log2 = AndroidTestUtils.generateMockLog(); Log log3 = AndroidTestUtils.generateMockLog(); Log log4 = AndroidTestUtils.generateMockLog(); - persistence.putLog("test-p1", log1); - persistence.putLog("test-p1", log2); - persistence.putLog("test-p2", log3); - persistence.putLog("test-p3", log4); + persistence.putLog("test-p1", log1, PERSISTENCE_NORMAL); + persistence.putLog("test-p1", log2, PERSISTENCE_NORMAL); + persistence.putLog("test-p2", log3, PERSISTENCE_NORMAL); + persistence.putLog("test-p3", log4, PERSISTENCE_NORMAL); assertEquals(2, persistence.countLogs("test-p1")); assertEquals(1, persistence.countLogs("test-p2")); assertEquals(1, persistence.countLogs("test-p3")); @@ -536,10 +539,10 @@ public void deleteLogsForGroup() throws PersistenceException { Log log2 = AndroidTestUtils.generateMockLog(); Log log3 = AndroidTestUtils.generateMockLog(); Log log4 = AndroidTestUtils.generateMockLog(); - persistence.putLog("test-p1", log1); - persistence.putLog("test-p1", log2); - persistence.putLog("test-p2", log3); - persistence.putLog("test-p3", log4); + persistence.putLog("test-p1", log1, PERSISTENCE_NORMAL); + persistence.putLog("test-p1", log2, PERSISTENCE_NORMAL); + persistence.putLog("test-p2", log3, PERSISTENCE_NORMAL); + persistence.putLog("test-p3", log4, PERSISTENCE_NORMAL); /* Get a log from persistence. */ List outputLogs = new ArrayList<>(); @@ -602,7 +605,7 @@ public void getLogs() throws PersistenceException { Log[] logs = new Log[numberOfLogs]; for (int i = 0; i < logs.length; i++) { logs[i] = AndroidTestUtils.generateMockLog(); - persistence.putLog("test", logs[i]); + persistence.putLog("test", logs[i], PERSISTENCE_NORMAL); } /* Get. */ @@ -718,7 +721,7 @@ private void generateCsLogsWithIKey(DatabasePersistence persistence, String iKey log.setTimestamp(new Date()); log.setIKey(iKey); log.addTransmissionTarget(iKey + "-token"); - persistence.putLog("test", log); + persistence.putLog("test", log, PERSISTENCE_NORMAL); } } @@ -753,7 +756,7 @@ public void getLogsException() throws PersistenceException, JSONException { /* Put. */ for (Log log : logs) - persistence.putLog("test", log); + persistence.putLog("test", log, PERSISTENCE_NORMAL); /* Get. */ List outputLogs = new ArrayList<>(); @@ -765,14 +768,26 @@ public void getLogsException() throws PersistenceException, JSONException { } } + @NonNull + private ContentValues getContentValues(DatabasePersistence persistence, String group) { + SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); + builder.appendWhere(DatabasePersistence.COLUMN_GROUP + " = ?"); + String[] selectionArgs = new String[]{group}; + Cursor cursor = persistence.mDatabaseManager.getCursor(builder, selectionArgs, false); + ContentValues values = persistence.mDatabaseManager.nextValues(cursor); + assertNotNull(values); + return values; + } + @Test - public void upgradeFromVersion1to3() throws PersistenceException, JSONException { + public void upgradeFromVersion1to4() throws PersistenceException, JSONException { /* Initialize database persistence with old schema. */ ContentValues oldSchema = new ContentValues(DatabasePersistence.SCHEMA); oldSchema.remove(DatabasePersistence.COLUMN_TARGET_TOKEN); oldSchema.remove(DatabasePersistence.COLUMN_DATA_TYPE); oldSchema.remove(DatabasePersistence.COLUMN_TARGET_KEY); + oldSchema.remove(DatabasePersistence.COLUMN_PRIORITY); DatabaseManager databaseManager = new DatabaseManager(sContext, DatabasePersistence.DATABASE, DatabasePersistence.TABLE, 1, oldSchema, new DatabaseManager.Listener() { @Override @@ -819,8 +834,12 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { assertEquals(1, outputLogs.size()); assertEquals(oldLog, outputLogs.get(0)); + /* Check priority migration. */ + ContentValues values = getContentValues(persistence, "test"); + assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); + /* Put new data with token. */ - persistence.putLog("test/one", commonSchemaLog); + persistence.putLog("test/one", commonSchemaLog, PERSISTENCE_NORMAL); } finally { persistence.close(); } @@ -838,12 +857,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { assertEquals(commonSchemaLog, outputLogs.get(0)); /* Verify target token is encrypted. */ - SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); - builder.appendWhere(DatabasePersistence.COLUMN_GROUP + " = ?"); - String[] selectionArgs = new String[]{"test/one"}; - Cursor cursor = persistence.mDatabaseManager.getCursor(builder, selectionArgs, false); - ContentValues values = persistence.mDatabaseManager.nextValues(cursor); - assertNotNull(values); + ContentValues values = getContentValues(persistence, "test/one"); String token = values.getAsString(DatabasePersistence.COLUMN_TARGET_TOKEN); assertNotNull(token); assertNotEquals("test-guid", token); @@ -852,17 +866,21 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /* Verify target key stored as well. */ String targetKey = values.getAsString(DatabasePersistence.COLUMN_TARGET_KEY); assertEquals(commonSchemaLog.getIKey(), "o:" + targetKey); + + /* Verify priority stored too. */ + assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); } finally { persistence.close(); } } @Test - public void upgradeFromVersion2to3() throws PersistenceException, JSONException { + public void upgradeFromVersion2to4() throws PersistenceException, JSONException { /* Initialize database persistence with old schema. */ ContentValues oldSchema = new ContentValues(DatabasePersistence.SCHEMA); oldSchema.remove(DatabasePersistence.COLUMN_TARGET_KEY); + oldSchema.remove(DatabasePersistence.COLUMN_PRIORITY); DatabaseManager databaseManager = new DatabaseManager(sContext, DatabasePersistence.DATABASE, DatabasePersistence.TABLE, DatabasePersistence.VERSION_TYPE_API_KEY, oldSchema, new DatabaseManager.Listener() { @Override @@ -910,8 +928,12 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { assertEquals(1, outputLogs.size()); assertEquals(oldLog, outputLogs.get(0)); + /* Check priority migration. */ + ContentValues values = getContentValues(persistence, "test"); + assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); + /* Put new data with token. */ - persistence.putLog("test/one", commonSchemaLog); + persistence.putLog("test/one", commonSchemaLog, PERSISTENCE_NORMAL); } finally { persistence.close(); } @@ -929,12 +951,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { assertEquals(commonSchemaLog, outputLogs.get(0)); /* Verify target token is encrypted. */ - SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); - builder.appendWhere(DatabasePersistence.COLUMN_GROUP + " = ?"); - String[] selectionArgs = new String[]{"test/one"}; - Cursor cursor = persistence.mDatabaseManager.getCursor(builder, selectionArgs, false); - ContentValues values = persistence.mDatabaseManager.nextValues(cursor); - assertNotNull(values); + ContentValues values = getContentValues(persistence, "test/one"); String token = values.getAsString(DatabasePersistence.COLUMN_TARGET_TOKEN); assertNotNull(token); assertNotEquals("test-guid", token); @@ -943,6 +960,102 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /* Verify target key stored as well. */ String targetKey = values.getAsString(DatabasePersistence.COLUMN_TARGET_KEY); assertEquals(commonSchemaLog.getIKey(), "o:" + targetKey); + + /* Verify priority stored too. */ + assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); + } finally { + persistence.close(); + } + } + + @Test + public void upgradeFromVersion3to4() throws PersistenceException, JSONException { + + /* Initialize database persistence with old schema. */ + ContentValues oldSchema = new ContentValues(DatabasePersistence.SCHEMA); + oldSchema.remove(DatabasePersistence.COLUMN_PRIORITY); + DatabaseManager databaseManager = new DatabaseManager(sContext, DatabasePersistence.DATABASE, DatabasePersistence.TABLE, DatabasePersistence.VERSION_TARGET_KEY, oldSchema, new DatabaseManager.Listener() { + + @Override + public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + return false; + } + }); + + /* Init log serializer. */ + LogSerializer logSerializer = new DefaultLogSerializer(); + logSerializer.addLogFactory(MOCK_LOG_TYPE, new MockLogFactory()); + logSerializer.addLogFactory(MockCommonSchemaLog.TYPE, new MockCommonSchemaLogFactory()); + + /* Insert old data before upgrade. */ + Log oldLog = AndroidTestUtils.generateMockLog(); + try { + ContentValues contentValues = new ContentValues(); + contentValues.put(DatabasePersistence.COLUMN_GROUP, "test"); + contentValues.put(DatabasePersistence.COLUMN_LOG, logSerializer.serializeLog(oldLog)); + contentValues.put(DatabasePersistence.COLUMN_DATA_TYPE, MOCK_LOG_TYPE); + databaseManager.put(contentValues); + } finally { + databaseManager.close(); + } + + /* Upgrade. */ + DatabasePersistence persistence = new DatabasePersistence(sContext); + persistence.setLogSerializer(logSerializer); + + /* Prepare a common schema log. */ + MockCommonSchemaLog commonSchemaLog = new MockCommonSchemaLog(); + commonSchemaLog.setName("test"); + commonSchemaLog.setIKey("o:test"); + commonSchemaLog.setTimestamp(new Date()); + commonSchemaLog.setVer("3.0"); + commonSchemaLog.addTransmissionTarget("test-guid"); + + /* Check upgrade. */ + try { + + /* Get old data. */ + assertEquals(1, persistence.countLogs("test")); + List outputLogs = new ArrayList<>(); + persistence.getLogs("test", Collections.emptyList(), 1, outputLogs); + assertEquals(1, outputLogs.size()); + assertEquals(oldLog, outputLogs.get(0)); + + /* Check priority migration. */ + ContentValues values = getContentValues(persistence, "test"); + assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); + + /* Put new data with token. */ + persistence.putLog("test/one", commonSchemaLog, PERSISTENCE_CRITICAL); + } finally { + persistence.close(); + } + + /* Get new data after restart. */ + persistence = new DatabasePersistence(sContext); + persistence.setLogSerializer(logSerializer); + try { + + /* Get new data. */ + assertEquals(1, persistence.countLogs("test/one")); + List outputLogs = new ArrayList<>(); + persistence.getLogs("test/one", Collections.emptyList(), 1, outputLogs); + assertEquals(1, outputLogs.size()); + assertEquals(commonSchemaLog, outputLogs.get(0)); + + /* Verify target token is encrypted. */ + ContentValues values = getContentValues(persistence, "test/one"); + String token = values.getAsString(DatabasePersistence.COLUMN_TARGET_TOKEN); + assertNotNull(token); + assertNotEquals("test-guid", token); + assertEquals("test-guid", CryptoUtils.getInstance(sContext).decrypt(token, false).getDecryptedData()); + + /* Verify target key stored as well. */ + String targetKey = values.getAsString(DatabasePersistence.COLUMN_TARGET_KEY); + assertEquals(commonSchemaLog.getIKey(), "o:" + targetKey); + + /* Verify priority stored too. */ + assertEquals((Integer) PERSISTENCE_CRITICAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); } finally { persistence.close(); } diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java index c96574ab2b..c4f3051cf3 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java @@ -14,6 +14,8 @@ import org.junit.Test; import org.junit.runner.RunWith; +import static com.microsoft.appcenter.Flags.PERSISTENCE_NORMAL; + @SuppressWarnings("unused") @SmallTest @RunWith(AndroidJUnit4.class) @@ -49,7 +51,7 @@ public void missingLogSerializer() throws Persistence.PersistenceException { //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) try { /* Generate a log and persist. */ - persistence.putLog("exception", new MockLog()); + persistence.putLog("exception", new MockLog(), PERSISTENCE_NORMAL); } finally { /* Close. */ //noinspection ThrowFromFinallyBlock diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java new file mode 100644 index 0000000000..41bb88cf63 --- /dev/null +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java @@ -0,0 +1,22 @@ +package com.microsoft.appcenter; + +/** + * Persistence and latency flags for telemetry. + */ +public final class Flags { + + /** + * Mask for persistence within flags. + */ + public static final int PERSISTENCE_MASK = 0xFF; + + /** + * An event can be lost due to low bandwidth or disk space constraints. + */ + public static final int PERSISTENCE_NORMAL = 0x01; + + /** + * Used for events that should be prioritized over non-critical events. + */ + public static final int PERSISTENCE_CRITICAL = 0x02; +} diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java index 5518209664..10c39b95e9 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java @@ -7,6 +7,7 @@ import android.support.annotation.VisibleForTesting; import com.microsoft.appcenter.CancellationException; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.http.HttpUtils; import com.microsoft.appcenter.http.ServiceCallback; import com.microsoft.appcenter.ingestion.AppCenterIngestion; @@ -688,7 +689,8 @@ public synchronized void enqueue(@NonNull Log log, @NonNull final String groupNa try { /* Persist log. */ - mPersistence.putLog(groupName, log); + // TODO introduce parameter for flags and extract persistence from mask. + mPersistence.putLog(groupName, log, Flags.PERSISTENCE_NORMAL); /* Nothing more to do if the log is from a paused transmission target. */ Iterator targetKeys = log.getTransmissionTargetTokens().iterator(); diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index 2648fb5e80..65d755f588 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -11,6 +11,7 @@ import android.support.annotation.VisibleForTesting; import com.microsoft.appcenter.Constants; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.ingestion.models.Log; import com.microsoft.appcenter.ingestion.models.one.CommonSchemaLog; import com.microsoft.appcenter.ingestion.models.one.PartAUtils; @@ -36,6 +37,7 @@ import java.util.TreeMap; import static com.microsoft.appcenter.AppCenter.LOG_TAG; +import static com.microsoft.appcenter.Flags.PERSISTENCE_NORMAL; @SuppressWarnings("TryFinallyCanBeTryWithResources") public class DatabasePersistence extends Persistence { @@ -46,6 +48,12 @@ public class DatabasePersistence extends Persistence { @VisibleForTesting static final int VERSION_TYPE_API_KEY = 2; + /** + * Version of the schema that introduced target key field. + */ + @VisibleForTesting + static final int VERSION_TARGET_KEY = 3; + /** * Name of group column in the table. */ @@ -76,11 +84,17 @@ public class DatabasePersistence extends Persistence { @VisibleForTesting static final String COLUMN_TARGET_KEY = "target_key"; + /** + * Priority. + */ + @VisibleForTesting + static final String COLUMN_PRIORITY = "priority"; + /** * Table schema for Persistence. */ @VisibleForTesting - static final ContentValues SCHEMA = getContentValues("", "", "", "", ""); + static final ContentValues SCHEMA = getContentValues("", "", "", "", "", 0); /** * Database name. @@ -97,7 +111,7 @@ public class DatabasePersistence extends Persistence { /** * Current version of the schema. */ - private static final int VERSION = 3; + private static final int VERSION = 4; /** * Size limit (in bytes) for a database row log payload. @@ -174,7 +188,10 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("ALTER TABLE " + TABLE + " ADD COLUMN `" + COLUMN_TARGET_TOKEN + "` TEXT"); db.execSQL("ALTER TABLE " + TABLE + " ADD COLUMN `" + COLUMN_DATA_TYPE + "` TEXT"); } - db.execSQL("ALTER TABLE " + TABLE + " ADD COLUMN `" + COLUMN_TARGET_KEY + "` TEXT"); + if (oldVersion < VERSION_TARGET_KEY) { + db.execSQL("ALTER TABLE " + TABLE + " ADD COLUMN `" + COLUMN_TARGET_KEY + "` TEXT"); + } + db.execSQL("ALTER TABLE " + TABLE + " ADD COLUMN `" + COLUMN_PRIORITY + "` INTEGER DEFAULT " + PERSISTENCE_NORMAL); return true; } }); @@ -191,15 +208,17 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { * @param logJ The JSON string for a log. * @param targetToken target token if the log is common schema. * @param targetKey project identifier part of the target token in clear text. + * @param priority priority. * @return A {@link ContentValues} instance. */ - private static ContentValues getContentValues(@Nullable String group, @Nullable String logJ, String targetToken, String type, String targetKey) { + private static ContentValues getContentValues(@Nullable String group, @Nullable String logJ, String targetToken, String type, String targetKey, int priority) { ContentValues values = new ContentValues(); values.put(COLUMN_GROUP, group); values.put(COLUMN_LOG, logJ); values.put(COLUMN_TARGET_TOKEN, targetToken); values.put(COLUMN_DATA_TYPE, type); values.put(COLUMN_TARGET_KEY, targetKey); + values.put(COLUMN_PRIORITY, priority); return values; } @@ -209,7 +228,7 @@ public boolean setMaxStorageSize(long maxStorageSizeInBytes) { } @Override - public long putLog(@NonNull String group, @NonNull Log log) throws PersistenceException { + public long putLog(@NonNull String group, @NonNull Log log, @IntRange(from = Flags.PERSISTENCE_NORMAL, to = Flags.PERSISTENCE_CRITICAL) int priority) throws PersistenceException { /* Convert log to JSON string and put in the database. */ try { @@ -230,7 +249,7 @@ public long putLog(@NonNull String group, @NonNull Log log) throws PersistenceEx targetKey = null; targetToken = null; } - contentValues = getContentValues(group, isLargePayload ? null : payload, targetToken, log.getType(), targetKey); + contentValues = getContentValues(group, isLargePayload ? null : payload, targetToken, log.getType(), targetKey, priority); long databaseId = mDatabaseManager.put(contentValues); if (databaseId == -1) { throw new PersistenceException("Failed to store a log to the Persistence database for log type " + log.getType() + "."); diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/Persistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/Persistence.java index f37cd429a1..a5104fd913 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/Persistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/Persistence.java @@ -24,12 +24,13 @@ public abstract class Persistence implements Closeable { /** * Writes a log to the storage with the given {@code group}. * - * @param group The group of the storage for the log. - * @param log The log to be placed in the storage. + * @param group The group of the storage for the log. + * @param log The log to be placed in the storage. + * @param priority The persistence priority. * @return Log identifier from persistence after saving. * @throws PersistenceException Exception will be thrown if Persistence cannot write a log to the storage. */ - public abstract long putLog(@NonNull String group, @NonNull Log log) throws PersistenceException; + public abstract long putLog(@NonNull String group, @NonNull Log log, int priority) throws PersistenceException; /** * Deletes a log with the give ID from the {@code group}. diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java index 24071b8929..b0422d234f 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java @@ -2,6 +2,7 @@ import android.content.Context; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.http.ServiceCallback; import com.microsoft.appcenter.ingestion.Ingestion; import com.microsoft.appcenter.ingestion.models.Log; @@ -51,9 +52,9 @@ public void nullAppSecretProvided() throws Persistence.PersistenceException { /* Check enqueue. */ Log log = mock(Log.class); channel.enqueue(log, TEST_GROUP); - verify(persistence, never()).putLog(TEST_GROUP, log); + verify(persistence, never()).putLog(TEST_GROUP, log, Flags.PERSISTENCE_NORMAL); channel.enqueue(mock(Log.class), "other"); - verify(persistence, never()).putLog(anyString(), any(Log.class)); + verify(persistence, never()).putLog(anyString(), any(Log.class), eq(Flags.PERSISTENCE_NORMAL)); /* Check clear. Even without app secret it works as it could be logs from previous process. */ channel.clear(TEST_GROUP); @@ -128,7 +129,7 @@ public void startWithoutAppSecret() throws Persistence.PersistenceException { verify(defaultIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* Verify we didn't persist the log since AppCenter not started with app secret. */ - verify(mockPersistence, never()).putLog(eq(appCenterGroup), any(Log.class)); + verify(mockPersistence, never()).putLog(eq(appCenterGroup), any(Log.class), eq(Flags.PERSISTENCE_NORMAL)); /* Enqueuing 1 event from one collector group. */ channel.enqueue(mock(Log.class), oneCollectorGroup); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java index bc8ffdabe1..2d7cd41fbb 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java @@ -2,6 +2,7 @@ import android.content.Context; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.ingestion.AppCenterIngestion; import com.microsoft.appcenter.ingestion.Ingestion; import com.microsoft.appcenter.ingestion.models.Log; @@ -20,6 +21,7 @@ import static org.mockito.Matchers.anyListOf; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; import static org.mockito.Matchers.notNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -135,7 +137,7 @@ public void filter() throws Persistence.PersistenceException { verify(listener1).shouldFilter(log); verify(listener2).onPreparingLog(log, TEST_GROUP); verify(listener2).shouldFilter(log); - verify(persistence, never()).putLog(TEST_GROUP, log); + verify(persistence, never()).putLog(eq(TEST_GROUP), eq(log), anyInt()); } /* Given 1 log. */ @@ -155,7 +157,7 @@ public void filter() throws Persistence.PersistenceException { /* Second listener skipped since first listener filtered out. */ verify(listener2, never()).shouldFilter(log); - verify(persistence, never()).putLog(TEST_GROUP, log); + verify(persistence, never()).putLog(eq(TEST_GROUP), eq(log), anyInt()); } /* Given 1 log. */ @@ -173,7 +175,7 @@ public void filter() throws Persistence.PersistenceException { verify(listener1).shouldFilter(log); verify(listener2).onPreparingLog(log, TEST_GROUP); verify(listener2).shouldFilter(log); - verify(persistence).putLog(TEST_GROUP, log); + verify(persistence).putLog(TEST_GROUP, log, Flags.PERSISTENCE_NORMAL); } } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java index 6b9b6d9cdf..b5266aa3f3 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java @@ -2,6 +2,7 @@ import android.content.Context; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.http.ServiceCallback; import com.microsoft.appcenter.ingestion.AppCenterIngestion; import com.microsoft.appcenter.ingestion.OneCollectorIngestion; @@ -59,7 +60,7 @@ public void pauseResumeGroup() throws Persistence.PersistenceException { /* 50 logs are persisted but never being sent to Ingestion. */ assertEquals(50, channel.getCounter(TEST_GROUP)); - verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class)); + verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class), eq(Flags.PERSISTENCE_NORMAL)); verify(mockIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mockPersistence, never()).deleteLogs(any(String.class), any(String.class)); verify(mockListener, never()).onBeforeSending(any(Log.class)); @@ -143,7 +144,7 @@ public void pauseResumeTargetToken() throws Persistence.PersistenceException { channel.enqueue(log, TEST_GROUP); /* Verify persisted but not incrementing and checking logs. */ - verify(persistence).putLog(TEST_GROUP, log); + verify(persistence).putLog(TEST_GROUP, log, Flags.PERSISTENCE_NORMAL); assertEquals(0, channel.getCounter(TEST_GROUP)); verify(persistence, never()).countLogs(TEST_GROUP); verify(ingestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -207,7 +208,7 @@ public void pauseGroupPauseTargetResumeGroupResumeTarget() throws Persistence.Pe channel.enqueue(log, TEST_GROUP); /* Verify persisted but not incrementing and checking logs. */ - verify(persistence).putLog(TEST_GROUP, log); + verify(persistence).putLog(TEST_GROUP, log, Flags.PERSISTENCE_NORMAL); assertEquals(0, channel.getCounter(TEST_GROUP)); verify(ingestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java index 57f1b4bd44..a82ad104d4 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java @@ -29,6 +29,7 @@ import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; +import static com.microsoft.appcenter.Flags.PERSISTENCE_NORMAL; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -57,7 +58,7 @@ public void invalidGroup() throws Persistence.PersistenceException { channel.enqueue(log, TEST_GROUP); verify(log, never()).setDevice(any(Device.class)); verify(log, never()).setTimestamp(any(Date.class)); - verify(persistence, never()).putLog(TEST_GROUP, log); + verify(persistence, never()).putLog(eq(TEST_GROUP), eq(log), anyInt()); /* Trying remove group that not registered. */ channel.removeGroup(TEST_GROUP); @@ -92,7 +93,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { assertEquals(0, channel.getCounter(TEST_GROUP)); /* Verify that 5 items have been persisted. */ - verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class)); + verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); /* Verify that we have called sendAsync on the ingestion. */ verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -115,7 +116,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { /* Schedule only 1 log after that. */ channel.enqueue(mock(Log.class), TEST_GROUP); assertEquals(1, channel.getCounter(TEST_GROUP)); - verify(mockPersistence, times(51)).putLog(eq(TEST_GROUP), any(Log.class)); + verify(mockPersistence, times(51)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mockPersistence).deleteLogs(any(String.class), any(String.class)); verify(mockListener, times(50)).onSuccess(any(Log.class)); @@ -134,7 +135,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { channel.enqueue(mock(Log.class), TEST_GROUP); channel.enqueue(mock(Log.class), TEST_GROUP); assertEquals(2, channel.getCounter(TEST_GROUP)); - verify(mockPersistence, times(53)).putLog(eq(TEST_GROUP), any(Log.class)); + verify(mockPersistence, times(53)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); verify(mockIngestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mockPersistence, times(2)).deleteLogs(any(String.class), any(String.class)); verify(mockListener, times(51)).onSuccess(any(Log.class)); @@ -230,7 +231,7 @@ public Object answer(InvocationOnMock invocation) { verify(mAppCenterHandler, times(4)).removeCallbacks(any(Runnable.class)); /* Verify all logs stored, N requests sent, not log deleted yet. */ - verify(mockPersistence, times(200)).putLog(eq(TEST_GROUP), any(Log.class)); + verify(mockPersistence, times(200)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); verify(mockIngestion, times(3)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mockPersistence, never()).deleteLogs(any(String.class), any(String.class)); @@ -279,7 +280,7 @@ public Object answer(InvocationOnMock invocation) { } /* Verify all logs stored, N requests sent, not log deleted yet. */ - verify(mockPersistence, times(100)).putLog(eq(TEST_GROUP), any(Log.class)); + verify(mockPersistence, times(100)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); verify(mockIngestion, times(3)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mockPersistence, never()).deleteLogs(any(String.class), any(String.class)); @@ -326,7 +327,7 @@ public void analyticsRecoverable() throws Persistence.PersistenceException { verify(mAppCenterHandler).removeCallbacks(any(Runnable.class)); /* Verify that 50 items have been persisted. */ - verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class)); + verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); /* Verify that we have called sendAsync on the ingestion. */ verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -404,7 +405,7 @@ public void analyticsFatal() throws Exception { verify(mAppCenterHandler).removeCallbacks(any(Runnable.class)); /* Verify that 50 items have been persisted. */ - verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class)); + verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); /* Verify that we have called sendAsync on the ingestion. */ verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -479,7 +480,7 @@ public void errorLogSuccess() throws Persistence.PersistenceException { channel.enqueue(mock(Log.class), TEST_GROUP); /* Verify that 2 items have been persisted. */ - verify(mockPersistence, times(2)).putLog(eq(TEST_GROUP), any(Log.class)); + verify(mockPersistence, times(2)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); /* Verify that we have called sendAsync on the ingestion twice as batch size is 1. */ verify(mockIngestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -519,7 +520,7 @@ public void errorLogRecoverable() throws Persistence.PersistenceException { channel.enqueue(mock(Log.class), TEST_GROUP); /* Verify that n items have been persisted. */ - verify(mockPersistence, times(logNumber)).putLog(eq(TEST_GROUP), any(Log.class)); + verify(mockPersistence, times(logNumber)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); /* Verify that we have called sendAsync on the ingestion once for the first item, but not more than that. */ verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -637,7 +638,7 @@ public void enqueuePersistenceFailure() throws Persistence.PersistenceException /* Simulate Persistence failing. */ doThrow(new Persistence.PersistenceException("mock", new IOException("mock"))). - when(mockPersistence).putLog(anyString(), any(Log.class)); + when(mockPersistence).putLog(anyString(), any(Log.class), anyInt()); AppCenterIngestion mockIngestion = mock(AppCenterIngestion.class); DefaultChannel channel = new DefaultChannel(mock(Context.class), UUIDUtils.randomUUID().toString(), mockPersistence, mockIngestion, mAppCenterHandler); channel.addGroup(TEST_GROUP, 50, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, null); @@ -646,7 +647,7 @@ public void enqueuePersistenceFailure() throws Persistence.PersistenceException for (int i = 0; i < 50; i++) { channel.enqueue(mock(Log.class), TEST_GROUP); } - verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class)); + verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); verify(mockIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); assertEquals(0, channel.getCounter(TEST_GROUP)); verify(mAppCenterHandler, never()).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); @@ -787,7 +788,7 @@ public void packageManagerIsBroken() throws Persistence.PersistenceException, De channel.enqueue(log, TEST_GROUP); verify(listener).onPreparingLog(log, TEST_GROUP); verify(listener, never()).shouldFilter(log); - verify(persistence, never()).putLog(TEST_GROUP, log); + verify(persistence, never()).putLog(eq(TEST_GROUP), eq(log), anyInt()); } @Test diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java index af4d5dd563..2e729d5b65 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java @@ -8,6 +8,7 @@ import android.database.sqlite.SQLiteQueryBuilder; import com.microsoft.appcenter.AppCenter; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.ingestion.models.Log; import com.microsoft.appcenter.ingestion.models.json.DefaultLogSerializer; import com.microsoft.appcenter.ingestion.models.json.LogSerializer; @@ -65,7 +66,7 @@ public void databaseOperationException() throws JSONException { /* Generate a log and persist. */ Log log = mock(Log.class); - mockPersistence.putLog("test-p1", log); + mockPersistence.putLog("test-p1", log, Flags.PERSISTENCE_NORMAL); fail("Expected persistence exception"); } catch (Persistence.PersistenceException ignore) { } finally { From 4af15dbcdf671255f29a1562e9bf52a8e4bb5f1e Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Mon, 29 Oct 2018 18:56:16 -0700 Subject: [PATCH 18/68] Fix coverage --- .../test/java/com/microsoft/appcenter/InstantiationTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java index 851615dace..af68b7046e 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java @@ -55,4 +55,9 @@ public void idHelper() { public void appCenterLog() { new AppCenterLog(); } + + @Test + public void flags() { + new Flags(); + } } From 90e72bc194eb82e1b7fcb5f5f877922130448c07 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Mon, 29 Oct 2018 19:36:17 -0700 Subject: [PATCH 19/68] Fix most warnings in channel --- .../appcenter/channel/DefaultChannel.java | 51 ++++++++----------- .../DefaultChannelAlternateIngestionTest.java | 2 +- .../DefaultChannelPauseResumeTest.java | 21 ++++---- .../appcenter/channel/DefaultChannelTest.java | 48 ++++++++--------- 4 files changed, 57 insertions(+), 65 deletions(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java index 10c39b95e9..abdddde3c6 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java @@ -189,7 +189,7 @@ public synchronized void setAppSecret(@NonNull String appSecret) { if (mEnabled) { for (GroupState groupState : mGroupStates.values()) { if (groupState.mIngestion == mIngestion) { - checkPendingLogs(groupState.mName); + checkPendingLogs(groupState); } } } @@ -216,7 +216,7 @@ public synchronized void addGroup(final String groupName, int maxLogsPerBatch, l if (mAppSecret != null || mIngestion != ingestion) { /* Schedule sending any pending log. */ - checkPendingLogs(groupState.mName); + checkPendingLogs(groupState); } /* Call listeners so that they can react on group adding. */ @@ -278,12 +278,12 @@ public synchronized void resumeGroup(String groupName, String targetToken) { */ AppCenterLog.debug(LOG_TAG, "resumeGroup(" + groupName + ", " + targetKey + ")"); groupState.mPendingLogCount = mPersistence.countLogs(groupName); - checkPendingLogs(groupState.mName); + checkPendingLogs(groupState); } } else if (groupState.mPaused) { AppCenterLog.debug(LOG_TAG, "resumeGroup(" + groupName + ")"); groupState.mPaused = false; - checkPendingLogs(groupState.mName); + checkPendingLogs(groupState); } /* Call listeners so that they can react on group resuming. */ @@ -317,8 +317,8 @@ public synchronized void setEnabled(boolean enabled) { for (Ingestion ingestion : mIngestions) { ingestion.reopen(); } - for (String groupName : mGroupStates.keySet()) { - checkPendingLogs(groupName); + for (GroupState groupState : mGroupStates.values()) { + checkPendingLogs(groupState); } } else { suspend(true, new CancellationException()); @@ -375,12 +375,11 @@ private void suspend(boolean deleteLogs, Exception exception) { /* Delete all other batches and call callback method that are currently in progress. */ for (Iterator>> iterator = groupState.mSendingBatches.entrySet().iterator(); iterator.hasNext(); ) { Map.Entry> entry = iterator.next(); - List removedLogsForBatchId = groupState.mSendingBatches.get(entry.getKey()); iterator.remove(); if (deleteLogs) { GroupListener groupListener = groupState.mListener; if (groupListener != null) { - for (Log log : removedLogsForBatchId) { + for (Log log : entry.getValue()) { groupListener.onFailure(log, exception); } } @@ -427,28 +426,21 @@ void cancelTimer(GroupState groupState) { } } - @VisibleForTesting - @SuppressWarnings("SameParameterValue") - synchronized int getCounter(@NonNull String groupName) { - return mGroupStates.get(groupName).mPendingLogCount; - } - /** * This will, if we're not using the limit for pending batches, trigger sending of a new request. * It will also reset the counters for sending out items for both the number of items enqueued and * the handlers. It will do this even if we don't have reached the limit * of pending batches or the time interval. * - * @param groupName the group name + * @param groupState the group state. */ - private synchronized void triggerIngestion(final @NonNull String groupName) { + private synchronized void triggerIngestion(final @NonNull GroupState groupState) { if (!mEnabled) { return; } - final GroupState groupState = mGroupStates.get(groupName); int pendingLogCount = groupState.mPendingLogCount; int maxFetch = Math.min(pendingLogCount, groupState.mMaxLogsPerBatch); - AppCenterLog.debug(LOG_TAG, "triggerIngestion(" + groupName + ") pendingLogCount=" + pendingLogCount); + AppCenterLog.debug(LOG_TAG, "triggerIngestion(" + groupState + ") pendingLogCount=" + pendingLogCount); cancelTimer(groupState); /* Check if we have reached the maximum number of pending batches, log to LogCat and don't trigger another sending. */ @@ -460,7 +452,7 @@ private synchronized void triggerIngestion(final @NonNull String groupName) { /* Get a batch from Persistence. */ final List batch = new ArrayList<>(maxFetch); final int stateSnapshot = mCurrentState; - final String batchId = mPersistence.getLogs(groupName, groupState.mPausedTargetKeys, maxFetch, batch); + final String batchId = mPersistence.getLogs(groupState.mName, groupState.mPausedTargetKeys, maxFetch, batch); /* Decrement counter. */ groupState.mPendingLogCount -= maxFetch; @@ -556,7 +548,7 @@ public void run() { private void checkPendingLogsAfterPost(@NonNull final GroupState groupState, int currentState) { if (checkStateDidNotChange(groupState, currentState)) { - checkPendingLogs(groupState.mName); + checkPendingLogs(groupState); } } @@ -578,7 +570,7 @@ private synchronized void handleSendingSuccess(@NonNull final GroupState groupSt groupListener.onSuccess(log); } } - checkPendingLogs(groupName); + checkPendingLogs(groupState); } } @@ -704,7 +696,7 @@ public synchronized void enqueue(@NonNull Log log, @NonNull final String groupNa groupState.mPendingLogCount++; AppCenterLog.debug(LOG_TAG, "enqueue(" + groupState.mName + ") pendingLogCount=" + groupState.mPendingLogCount); if (mEnabled) { - checkPendingLogs(groupState.mName); + checkPendingLogs(groupState); } else { AppCenterLog.debug(LOG_TAG, "Channel is temporarily disabled, log was saved to disk."); } @@ -717,19 +709,18 @@ public synchronized void enqueue(@NonNull Log log, @NonNull final String groupNa /** * Check for logs to trigger immediately or schedule with a timer or does nothing if no logs. * - * @param groupName the group name. + * @param groupState the group state. */ @VisibleForTesting - synchronized void checkPendingLogs(@NonNull String groupName) { - GroupState groupState = mGroupStates.get(groupName); + synchronized void checkPendingLogs(@NonNull GroupState groupState) { if (groupState.mPaused) { - AppCenterLog.debug(LOG_TAG, groupName + " is paused. Skip checking pending logs."); + AppCenterLog.debug(LOG_TAG, groupState + " is paused. Skip checking pending logs."); return; } long pendingLogCount = groupState.mPendingLogCount; - AppCenterLog.debug(LOG_TAG, "checkPendingLogs(" + groupName + ") pendingLogCount=" + pendingLogCount); + AppCenterLog.debug(LOG_TAG, "checkPendingLogs(" + groupState + ") pendingLogCount=" + pendingLogCount); if (pendingLogCount >= groupState.mMaxLogsPerBatch) { - triggerIngestion(groupName); + triggerIngestion(groupState); } else if (pendingLogCount > 0 && !groupState.mScheduled) { groupState.mScheduled = true; mAppCenterHandler.postDelayed(groupState.mRunnable, groupState.mBatchTimeInterval); @@ -737,7 +728,7 @@ synchronized void checkPendingLogs(@NonNull String groupName) { } @VisibleForTesting - GroupState getGroupState(String groupName) { + GroupState getGroupState(@SuppressWarnings("SameParameterValue") String groupName) { return mGroupStates.get(groupName); } @@ -826,7 +817,7 @@ class GroupState { @Override public void run() { mScheduled = false; - triggerIngestion(mName); + triggerIngestion(GroupState.this); } }; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java index b0422d234f..aeb7f058be 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java @@ -84,7 +84,7 @@ public void useAlternateIngestion() throws IOException { verify(defaultIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* The counter should be 0 now as we sent data. */ - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); /* Disabling the channel should close all channels */ channel.setEnabled(false); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java index b5266aa3f3..3d05ba03ae 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java @@ -59,7 +59,7 @@ public void pauseResumeGroup() throws Persistence.PersistenceException { verify(mAppCenterHandler, never()).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); /* 50 logs are persisted but never being sent to Ingestion. */ - assertEquals(50, channel.getCounter(TEST_GROUP)); + assertEquals(50, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class), eq(Flags.PERSISTENCE_NORMAL)); verify(mockIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mockPersistence, never()).deleteLogs(any(String.class), any(String.class)); @@ -67,7 +67,7 @@ public void pauseResumeGroup() throws Persistence.PersistenceException { verify(mockListener, never()).onSuccess(any(Log.class)); /* The counter should still be 50 now as we did NOT send data. */ - assertEquals(50, channel.getCounter(TEST_GROUP)); + assertEquals(50, channel.getGroupState(TEST_GROUP).mPendingLogCount); /* Resume group. */ channel.resumeGroup(TEST_GROUP, null); @@ -81,7 +81,7 @@ public void pauseResumeGroup() throws Persistence.PersistenceException { verify(mockListener, times(50)).onSuccess(any(Log.class)); /* The counter should be 0 now as we sent data. */ - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); } @Test @@ -104,15 +104,16 @@ public void pauseGroupTwice() { public void resumeGroupWhileNotPaused() { DefaultChannel channel = spy(new DefaultChannel(mock(Context.class), UUIDUtils.randomUUID().toString(), mock(Persistence.class), mock(AppCenterIngestion.class), mAppCenterHandler)); channel.addGroup(TEST_GROUP, 50, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, mock(Channel.GroupListener.class)); - verify(channel).checkPendingLogs(eq(TEST_GROUP)); - assertFalse(channel.getGroupState(TEST_GROUP).mPaused); + DefaultChannel.GroupState groupState = channel.getGroupState(TEST_GROUP); + verify(channel).checkPendingLogs(groupState); + assertFalse(groupState.mPaused); /* Resume group. */ channel.resumeGroup(TEST_GROUP, null); - assertFalse(channel.getGroupState(TEST_GROUP).mPaused); + assertFalse(groupState.mPaused); /* Verify resumeGroup doesn't resume the group while un-paused. */ - verify(channel).checkPendingLogs(eq(TEST_GROUP)); + verify(channel).checkPendingLogs(groupState); } @Test @@ -145,7 +146,7 @@ public void pauseResumeTargetToken() throws Persistence.PersistenceException { /* Verify persisted but not incrementing and checking logs. */ verify(persistence).putLog(TEST_GROUP, log, Flags.PERSISTENCE_NORMAL); - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(persistence, never()).countLogs(TEST_GROUP); verify(ingestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -209,7 +210,7 @@ public void pauseGroupPauseTargetResumeGroupResumeTarget() throws Persistence.Pe /* Verify persisted but not incrementing and checking logs. */ verify(persistence).putLog(TEST_GROUP, log, Flags.PERSISTENCE_NORMAL); - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(ingestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* Resume group should not send the log. */ @@ -240,7 +241,7 @@ public void pauseResumeGroupWhenDisabled() { channel.resumeGroup(TEST_GROUP, null); /* Verify channel doesn't do anything on pause. */ - verify(channel, never()).checkPendingLogs(eq(TEST_GROUP)); + verify(channel, never()).checkPendingLogs(any(DefaultChannel.GroupState.class)); verify(listener, never()).onResumed(eq(TEST_GROUP), anyString()); } } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java index a82ad104d4..fc73ea1770 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java @@ -81,7 +81,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { /* Enqueuing 49 events. */ for (int i = 1; i <= 49; i++) { channel.enqueue(mock(Log.class), TEST_GROUP); - assertEquals(i, channel.getCounter(TEST_GROUP)); + assertEquals(i, channel.getGroupState(TEST_GROUP).mPendingLogCount); } verify(mAppCenterHandler).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); @@ -90,7 +90,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { verify(mAppCenterHandler).removeCallbacks(any(Runnable.class)); /* The counter should be 0 as we reset the counter after reaching the limit of 50. */ - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); /* Verify that 5 items have been persisted. */ verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); @@ -108,14 +108,14 @@ public void analyticsSuccess() throws Persistence.PersistenceException { verify(mockListener, times(50)).onSuccess(any(Log.class)); /* The counter should be 0 now as we sent data. */ - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); /* Prepare to mock timer. */ AtomicReference runnable = catchPostRunnable(); /* Schedule only 1 log after that. */ channel.enqueue(mock(Log.class), TEST_GROUP); - assertEquals(1, channel.getCounter(TEST_GROUP)); + assertEquals(1, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(mockPersistence, times(51)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mockPersistence).deleteLogs(any(String.class), any(String.class)); @@ -126,7 +126,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { runnable.get().run(); runnable.set(null); - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(mockIngestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mockPersistence, times(2)).deleteLogs(any(String.class), any(String.class)); verify(mockListener, times(51)).onSuccess(any(Log.class)); @@ -134,7 +134,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { /* 2 more timed logs. */ channel.enqueue(mock(Log.class), TEST_GROUP); channel.enqueue(mock(Log.class), TEST_GROUP); - assertEquals(2, channel.getCounter(TEST_GROUP)); + assertEquals(2, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(mockPersistence, times(53)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); verify(mockIngestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mockPersistence, times(2)).deleteLogs(any(String.class), any(String.class)); @@ -144,7 +144,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { assertNotNull(runnable.get()); runnable.get().run(); - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(mockIngestion, times(3)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mockPersistence, times(3)).deleteLogs(any(String.class), any(String.class)); verify(mockListener, times(53)).onSuccess(any(Log.class)); @@ -174,7 +174,7 @@ public void lessLogsThanExpected() { /* Enqueuing 49 events. */ for (int i = 1; i <= 49; i++) { channel.enqueue(mock(Log.class), TEST_GROUP); - assertEquals(i, channel.getCounter(TEST_GROUP)); + assertEquals(i, channel.getGroupState(TEST_GROUP).mPendingLogCount); } verify(mAppCenterHandler).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); @@ -183,7 +183,7 @@ public void lessLogsThanExpected() { verify(mAppCenterHandler).removeCallbacks(any(Runnable.class)); /* Database returned less logs than we expected (40 vs 50), yet counter must be reset. */ - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); } @NonNull @@ -248,7 +248,7 @@ public Object answer(InvocationOnMock invocation) { verify(mockPersistence, times(4)).deleteLogs(any(String.class), any(String.class)); /* The counter should be 0 now as we sent data. */ - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); } @Test @@ -297,7 +297,7 @@ public Object answer(InvocationOnMock invocation) { verify(mockPersistence, times(4)).deleteLogs(any(String.class), any(String.class)); /* The counter should be 0 now as we sent data. */ - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); /* Only 2 batches after channel start (non initial logs), verify timer interactions. */ verify(mAppCenterHandler, times(2)).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); @@ -346,7 +346,7 @@ public void analyticsRecoverable() throws Persistence.PersistenceException { } /* The counter keeps being increased. */ - assertEquals(70, channel.getCounter(TEST_GROUP)); + assertEquals(70, channel.getGroupState(TEST_GROUP).mPendingLogCount); /* Prepare to mock timer. */ AtomicReference runnable = catchPostRunnable(); @@ -355,14 +355,14 @@ public void analyticsRecoverable() throws Persistence.PersistenceException { channel.setEnabled(true); /* Upon enabling, 1st batch of 50 is sent immediately, 20 logs are remaining. */ - assertEquals(20, channel.getCounter(TEST_GROUP)); + assertEquals(20, channel.getGroupState(TEST_GROUP).mPendingLogCount); /* Wait for timer. */ assertNotNull(runnable.get()); runnable.get().run(); /* The counter should be 0 after the second batch. */ - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); /* Verify that we have called sendAsync on the ingestion 3 times total. */ verify(mockIngestion, times(3)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -417,7 +417,7 @@ public void analyticsFatal() throws Exception { verify(mockPersistence).deleteLogs(TEST_GROUP); /* Verify counter. */ - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); /* Enqueuing 20 more events. */ for (int i = 0; i < 20; i++) { @@ -425,7 +425,7 @@ public void analyticsFatal() throws Exception { } /* The counter should still be 0 as logs are discarded by channel now. */ - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); /* No more timer yet at this point. */ verify(mAppCenterHandler).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); @@ -441,14 +441,14 @@ public void analyticsFatal() throws Exception { for (int i = 0; i < 20; i++) { channel.enqueue(mock(Log.class), TEST_GROUP); } - assertEquals(20, channel.getCounter(TEST_GROUP)); + assertEquals(20, channel.getGroupState(TEST_GROUP).mPendingLogCount); /* Wait for timer. */ assertNotNull(runnable.get()); runnable.get().run(); /* The counter should back to 0 now. */ - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); /* Verify that we have called sendAsync on the ingestion 2 times total: 1 earlier failure then 1 success. */ verify(mockIngestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -495,7 +495,7 @@ public void errorLogSuccess() throws Persistence.PersistenceException { verify(mockListener, times(2)).onSuccess(any(Log.class)); /* The counter should be 0 now as we sent data. */ - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); /* Verify timer. */ verify(mAppCenterHandler, never()).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); @@ -649,7 +649,7 @@ public void enqueuePersistenceFailure() throws Persistence.PersistenceException } verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); verify(mockIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); - assertEquals(0, channel.getCounter(TEST_GROUP)); + assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(mAppCenterHandler, never()).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); verify(mAppCenterHandler, never()).removeCallbacks(any(Runnable.class)); } @@ -722,7 +722,7 @@ public void initialLogs() throws IOException { DefaultChannel channel = new DefaultChannel(mock(Context.class), UUIDUtils.randomUUID().toString(), persistence, ingestion, mAppCenterHandler); channel.addGroup(TEST_GROUP, 50, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, null); verify(ingestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); - assertEquals(3, channel.getCounter(TEST_GROUP)); + assertEquals(3, channel.getGroupState(TEST_GROUP).mPendingLogCount); assertNotNull(runnable.get()); runnable.get().run(); verify(ingestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -741,7 +741,7 @@ public void initialLogsMoreThan1Batch() throws IOException { DefaultChannel channel = new DefaultChannel(mock(Context.class), UUIDUtils.randomUUID().toString(), persistence, ingestion, mAppCenterHandler); channel.addGroup(TEST_GROUP, 50, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, null); verify(ingestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); - assertEquals(3, channel.getCounter(TEST_GROUP)); + assertEquals(3, channel.getGroupState(TEST_GROUP).mPendingLogCount); assertNotNull(runnable.get()); runnable.get().run(); verify(ingestion, times(3)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -759,7 +759,7 @@ public void initialLogsThenDisable() throws IOException { when(persistence.getLogs(anyString(), anyListOf(String.class), anyInt(), anyListOf(Log.class))).thenAnswer(getGetLogsAnswer(3)); DefaultChannel channel = new DefaultChannel(mock(Context.class), UUIDUtils.randomUUID().toString(), persistence, ingestion, mAppCenterHandler); channel.addGroup(TEST_GROUP, 50, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, null); - assertEquals(3, channel.getCounter(TEST_GROUP)); + assertEquals(3, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(mAppCenterHandler).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); channel.setEnabled(false); verify(mAppCenterHandler).removeCallbacks(any(Runnable.class)); @@ -803,7 +803,7 @@ public void somehowDatabaseEmptiedAfterTimer() throws IOException { DefaultChannel channel = new DefaultChannel(mock(Context.class), UUIDUtils.randomUUID().toString(), persistence, ingestion, mAppCenterHandler); channel.addGroup(TEST_GROUP, 50, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, null); verify(ingestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); - assertEquals(2, channel.getCounter(TEST_GROUP)); + assertEquals(2, channel.getGroupState(TEST_GROUP).mPendingLogCount); assertNotNull(runnable.get()); runnable.get().run(); verify(ingestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); From 55e2d86e70724425be5ae5d1e7335c536c3a490f Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Mon, 29 Oct 2018 19:44:55 -0700 Subject: [PATCH 20/68] Fix some debug log messages --- .../com/microsoft/appcenter/channel/DefaultChannel.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java index abdddde3c6..fe98c6ff84 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java @@ -440,7 +440,7 @@ private synchronized void triggerIngestion(final @NonNull GroupState groupState) } int pendingLogCount = groupState.mPendingLogCount; int maxFetch = Math.min(pendingLogCount, groupState.mMaxLogsPerBatch); - AppCenterLog.debug(LOG_TAG, "triggerIngestion(" + groupState + ") pendingLogCount=" + pendingLogCount); + AppCenterLog.debug(LOG_TAG, "triggerIngestion(" + groupState.mName + ") pendingLogCount=" + pendingLogCount); cancelTimer(groupState); /* Check if we have reached the maximum number of pending batches, log to LogCat and don't trigger another sending. */ @@ -714,11 +714,11 @@ public synchronized void enqueue(@NonNull Log log, @NonNull final String groupNa @VisibleForTesting synchronized void checkPendingLogs(@NonNull GroupState groupState) { if (groupState.mPaused) { - AppCenterLog.debug(LOG_TAG, groupState + " is paused. Skip checking pending logs."); + AppCenterLog.debug(LOG_TAG, groupState.mName + " is paused. Skip checking pending logs."); return; } long pendingLogCount = groupState.mPendingLogCount; - AppCenterLog.debug(LOG_TAG, "checkPendingLogs(" + groupState + ") pendingLogCount=" + pendingLogCount); + AppCenterLog.debug(LOG_TAG, "checkPendingLogs(" + groupState.mName + ") pendingLogCount=" + pendingLogCount); if (pendingLogCount >= groupState.mMaxLogsPerBatch) { triggerIngestion(groupState); } else if (pendingLogCount > 0 && !groupState.mScheduled) { From 908b4bb49f702d3bba54a77a4c5248d44f710535 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Mon, 29 Oct 2018 20:01:12 -0700 Subject: [PATCH 21/68] Simplify state change detection and fix null check at same time In ingestion callbacks used by channel. --- .../appcenter/channel/DefaultChannel.java | 33 +++++++++---------- .../DefaultChannelRaceConditionTest.java | 1 + 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java index fe98c6ff84..f832b2dbd7 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java @@ -518,7 +518,7 @@ public void onCallSucceeded(String payload) { @Override public void run() { - handleSendingSuccess(groupState, currentState, batchId); + handleSendingSuccess(groupState, batchId); } }); } @@ -529,7 +529,7 @@ public void onCallFailed(final Exception e) { @Override public void run() { - handleSendingFailure(groupState, currentState, batchId, e); + handleSendingFailure(groupState, batchId, e); } }); } @@ -555,15 +555,13 @@ private void checkPendingLogsAfterPost(@NonNull final GroupState groupState, int /** * The actual implementation to react to sending a batch to the server successfully. * - * @param groupState The group state. - * @param currentState The current state. - * @param batchId The batch ID. + * @param groupState The group state. + * @param batchId The batch ID. */ - private synchronized void handleSendingSuccess(@NonNull final GroupState groupState, int currentState, @NonNull final String batchId) { - if (checkStateDidNotChange(groupState, currentState)) { - String groupName = groupState.mName; - mPersistence.deleteLogs(groupName, batchId); - List removedLogsForBatchId = groupState.mSendingBatches.remove(batchId); + private synchronized void handleSendingSuccess(@NonNull GroupState groupState, @NonNull String batchId) { + List removedLogsForBatchId = groupState.mSendingBatches.remove(batchId); + if (removedLogsForBatchId != null) { + mPersistence.deleteLogs(groupState.mName, batchId); GroupListener groupListener = groupState.mListener; if (groupListener != null) { for (Log log : removedLogsForBatchId) { @@ -579,16 +577,15 @@ private synchronized void handleSendingSuccess(@NonNull final GroupState groupSt * Will disable the sender in case of a recoverable error. * Will delete batch of data in case of a non-recoverable error. * - * @param groupState the group state - * @param currentState the current state - * @param batchId the batch ID - * @param e the exception + * @param groupState the group state + * @param batchId the batch ID + * @param e the exception */ - private synchronized void handleSendingFailure(@NonNull final GroupState groupState, int currentState, @NonNull final String batchId, @NonNull final Exception e) { - if (checkStateDidNotChange(groupState, currentState)) { - String groupName = groupState.mName; + private synchronized void handleSendingFailure(@NonNull GroupState groupState, @NonNull String batchId, @NonNull Exception e) { + String groupName = groupState.mName; + List removedLogsForBatchId = groupState.mSendingBatches.remove(batchId); + if (removedLogsForBatchId != null) { AppCenterLog.error(LOG_TAG, "Sending logs groupName=" + groupName + " id=" + batchId + " failed", e); - List removedLogsForBatchId = groupState.mSendingBatches.remove(batchId); boolean recoverableError = HttpUtils.isRecoverableError(e); if (recoverableError) { groupState.mPendingLogCount += removedLogsForBatchId.size(); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelRaceConditionTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelRaceConditionTest.java index 117333b2fc..ef8a5e5f1a 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelRaceConditionTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelRaceConditionTest.java @@ -131,6 +131,7 @@ public boolean matches(Object argument) { return argument instanceof CancellationException; } })); + verify(mockPersistence, never()).deleteLogs(anyString(), anyString()); } @Test From a3a1160766bd84c3d43b7c2a5944c90dc0bd4a2c Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Tue, 30 Oct 2018 12:14:37 -0700 Subject: [PATCH 22/68] Address feedbacks --- .../DatabasePersistenceAndroidTest.java | 22 +++++++++---------- .../java/com/microsoft/appcenter/Flags.java | 5 +++++ .../persistence/DatabasePersistence.java | 6 ++--- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index e9ce9ea4f3..8e2c1d86e3 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -95,6 +95,17 @@ public void setUp() { sContext.deleteDatabase(DatabasePersistence.DATABASE); } + @NonNull + private ContentValues getContentValues(DatabasePersistence persistence, String group) { + SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); + builder.appendWhere(DatabasePersistence.COLUMN_GROUP + " = ?"); + String[] selectionArgs = new String[]{group}; + Cursor cursor = persistence.mDatabaseManager.getCursor(builder, selectionArgs, false); + ContentValues values = persistence.mDatabaseManager.nextValues(cursor); + assertNotNull(values); + return values; + } + @Test public void putLog() throws PersistenceException { @@ -768,17 +779,6 @@ public void getLogsException() throws PersistenceException, JSONException { } } - @NonNull - private ContentValues getContentValues(DatabasePersistence persistence, String group) { - SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); - builder.appendWhere(DatabasePersistence.COLUMN_GROUP + " = ?"); - String[] selectionArgs = new String[]{group}; - Cursor cursor = persistence.mDatabaseManager.getCursor(builder, selectionArgs, false); - ContentValues values = persistence.mDatabaseManager.nextValues(cursor); - assertNotNull(values); - return values; - } - @Test public void upgradeFromVersion1to4() throws PersistenceException, JSONException { diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java index 41bb88cf63..adbf5ea81d 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java @@ -19,4 +19,9 @@ public final class Flags { * Used for events that should be prioritized over non-critical events. */ public static final int PERSISTENCE_CRITICAL = 0x02; + + /** + * Default combination of flags. + */ + public static final int DEFAULT_FLAGS = PERSISTENCE_NORMAL; } diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index 65d755f588..e05648599b 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -206,9 +206,9 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { * * @param group The group of the storage for the log. * @param logJ The JSON string for a log. - * @param targetToken target token if the log is common schema. - * @param targetKey project identifier part of the target token in clear text. - * @param priority priority. + * @param targetToken The target token if the log is common schema. + * @param targetKey The project identifier part of the target token in clear text. + * @param priority The persistence priority. * @return A {@link ContentValues} instance. */ private static ContentValues getContentValues(@Nullable String group, @Nullable String logJ, String targetToken, String type, String targetKey, int priority) { From 9e446430b4a0435c356eacc1dd5687a3c672156d Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Tue, 30 Oct 2018 15:05:18 -0700 Subject: [PATCH 23/68] Add flags to enqueue item, use critical for crash, defaults otherwise --- .../appcenter/analytics/Analytics.java | 5 +- .../analytics/channel/SessionTracker.java | 3 +- .../appcenter/analytics/AnalyticsTest.java | 80 +++++++++---------- .../AnalyticsTransmissionTargetTest.java | 25 +++--- .../analytics/PropertyConfiguratorTest.java | 16 ++-- .../analytics/channel/SessionTrackerTest.java | 54 +++++++------ .../appcenter/crashes/CrashesAndroidTest.java | 22 ++--- .../microsoft/appcenter/crashes/Crashes.java | 7 +- .../appcenter/crashes/CrashesTest.java | 59 +++++++------- .../appcenter/distribute/Distribute.java | 5 +- .../DistributeBeforeApiSuccessTest.java | 7 +- .../com/microsoft/appcenter/push/Push.java | 3 +- .../microsoft/appcenter/push/PushTest.java | 12 +-- .../com/microsoft/appcenter/AppCenter.java | 4 +- .../microsoft/appcenter/channel/Channel.java | 13 ++- .../appcenter/channel/DefaultChannel.java | 10 +-- .../channel/OneCollectorChannelListener.java | 3 +- .../appcenter/AppCenterLibraryTest.java | 22 ++--- .../microsoft/appcenter/AppCenterTest.java | 41 +++++----- .../channel/ChannelLogDecorateTest.java | 7 +- .../DefaultChannelAlternateIngestionTest.java | 16 ++-- .../DefaultChannelOtherOperationsTest.java | 12 +-- .../DefaultChannelPauseResumeTest.java | 10 +-- .../appcenter/channel/DefaultChannelTest.java | 57 ++++++------- .../OneCollectorChannelListenerTest.java | 12 +-- 25 files changed, 267 insertions(+), 238 deletions(-) diff --git a/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/Analytics.java b/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/Analytics.java index 986feab689..088cec04f5 100644 --- a/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/Analytics.java +++ b/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/Analytics.java @@ -9,6 +9,7 @@ import com.microsoft.appcenter.AbstractAppCenterService; import com.microsoft.appcenter.AppCenter; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.analytics.channel.AnalyticsListener; import com.microsoft.appcenter.analytics.channel.AnalyticsValidator; import com.microsoft.appcenter.analytics.channel.SessionTracker; @@ -662,7 +663,7 @@ private void queuePage(String name, Map properties) { PageLog pageLog = new PageLog(); pageLog.setName(name); pageLog.setProperties(properties); - mChannel.enqueue(pageLog, ANALYTICS_GROUP); + mChannel.enqueue(pageLog, ANALYTICS_GROUP, Flags.DEFAULT_FLAGS); } /** @@ -694,7 +695,7 @@ public void run() { eventLog.setId(UUIDUtils.randomUUID()); eventLog.setName(name); eventLog.setTypedProperties(properties); - mChannel.enqueue(eventLog, ANALYTICS_GROUP); + mChannel.enqueue(eventLog, ANALYTICS_GROUP, Flags.DEFAULT_FLAGS); } }); } diff --git a/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/channel/SessionTracker.java b/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/channel/SessionTracker.java index 1e0ab53977..18536fce10 100644 --- a/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/channel/SessionTracker.java +++ b/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/channel/SessionTracker.java @@ -3,6 +3,7 @@ import android.os.SystemClock; import android.support.annotation.NonNull; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.SessionContext; import com.microsoft.appcenter.analytics.Analytics; import com.microsoft.appcenter.analytics.ingestion.models.StartSessionLog; @@ -134,7 +135,7 @@ private void sendStartSessionIfNeeded() { /* Enqueue a start session log. */ StartSessionLog startSessionLog = new StartSessionLog(); startSessionLog.setSid(mSid); - mChannel.enqueue(startSessionLog, mGroupName); + mChannel.enqueue(startSessionLog, mGroupName, Flags.DEFAULT_FLAGS); } } diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java index d7dd73b41b..3a70fb73ee 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java @@ -27,7 +27,6 @@ import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.async.AppCenterConsumer; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import com.microsoft.appcenter.utils.storage.FileManager; import org.junit.Assert; import org.junit.Test; @@ -44,6 +43,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import static com.microsoft.appcenter.Flags.DEFAULT_FLAGS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -141,7 +141,7 @@ public boolean matches(Object item) { } return false; } - }), eq(analytics.getGroupName())); + }), eq(analytics.getGroupName()), eq(DEFAULT_FLAGS)); } @Test @@ -175,14 +175,14 @@ public void disableAutomaticPageTracking() { public boolean matches(Object argument) { return argument instanceof StartSessionLog; } - }), anyString()); + }), anyString(), eq(DEFAULT_FLAGS)); verify(channel, never()).enqueue(argThat(new ArgumentMatcher() { @Override public boolean matches(Object argument) { return argument instanceof PageLog; } - }), anyString()); + }), anyString(), anyInt()); Analytics.setAutoPageTrackingEnabled(true); assertTrue(Analytics.isAutoPageTrackingEnabled()); analytics.onActivityResumed(new SomeScreen()); @@ -196,7 +196,7 @@ public boolean matches(Object item) { } return false; } - }), eq(analytics.getGroupName())); + }), eq(analytics.getGroupName()), eq(DEFAULT_FLAGS)); } @Test @@ -209,7 +209,7 @@ public void trackEventFromAppWithoutProperties() { /* Send event without properties. */ Analytics.trackEvent("eventName"); - verify(channel).enqueue(argumentCaptor.capture(), anyString()); + verify(channel).enqueue(argumentCaptor.capture(), anyString(), eq(DEFAULT_FLAGS)); assertNotNull(argumentCaptor.getValue()); assertEquals("eventName", argumentCaptor.getValue().getName()); assertNull(argumentCaptor.getValue().getTypedProperties()); @@ -225,7 +225,7 @@ public void trackEventFromAppWithNullMapProperty() { /* Send event with empty Map properties. */ Analytics.trackEvent("eventName", (Map) null); - verify(channel).enqueue(argumentCaptor.capture(), anyString()); + verify(channel).enqueue(argumentCaptor.capture(), anyString(), eq(DEFAULT_FLAGS)); assertNotNull(argumentCaptor.getValue()); assertEquals("eventName", argumentCaptor.getValue().getName()); assertNull(argumentCaptor.getValue().getTypedProperties()); @@ -241,7 +241,7 @@ public void trackEventFromAppWithEmptyMapProperty() { /* Send event with empty Map properties. */ Analytics.trackEvent("eventName", new HashMap()); - verify(channel).enqueue(argumentCaptor.capture(), anyString()); + verify(channel).enqueue(argumentCaptor.capture(), anyString(), eq(DEFAULT_FLAGS)); assertNotNull(argumentCaptor.getValue()); assertEquals("eventName", argumentCaptor.getValue().getName()); assertEquals(Collections.emptyList(), argumentCaptor.getValue().getTypedProperties()); @@ -262,7 +262,7 @@ public void trackEventFromAppWithMapProperties() { StringTypedProperty stringProperty = new StringTypedProperty(); stringProperty.setName("name"); stringProperty.setValue("value"); - verify(channel).enqueue(argumentCaptor.capture(), anyString()); + verify(channel).enqueue(argumentCaptor.capture(), anyString(), eq(DEFAULT_FLAGS)); assertNotNull(argumentCaptor.getValue()); assertEquals("eventName", argumentCaptor.getValue().getName()); assertEquals(Collections.singletonList(stringProperty), argumentCaptor.getValue().getTypedProperties()); @@ -278,7 +278,7 @@ public void trackEventFromAppWithEmptyEventProperties() { /* Send event with empty EventProperties. */ Analytics.trackEvent("eventName", new EventProperties()); - verify(channel).enqueue(argumentCaptor.capture(), anyString()); + verify(channel).enqueue(argumentCaptor.capture(), anyString(), eq(DEFAULT_FLAGS)); assertNotNull(argumentCaptor.getValue()); assertEquals("eventName", argumentCaptor.getValue().getName()); assertEquals(Collections.emptyList(), argumentCaptor.getValue().getTypedProperties()); @@ -314,11 +314,11 @@ public void trackEventFromAppWithEventProperties() { EventProperties eventProperties = new EventProperties(); eventProperties.set("n0", "value"); eventProperties.set("n1", date); - eventProperties.set("n2", 0l); + eventProperties.set("n2", 0L); eventProperties.set("n3", 0d); eventProperties.set("n4", true); Analytics.trackEvent("eventName", eventProperties); - verify(channel).enqueue(argumentCaptor.capture(), anyString()); + verify(channel).enqueue(argumentCaptor.capture(), anyString(), eq(DEFAULT_FLAGS)); assertNotNull(argumentCaptor.getValue()); assertEquals("eventName", argumentCaptor.getValue().getName()); assertEquals(stringTypedProperty, argumentCaptor.getValue().getTypedProperties().get(0)); @@ -337,16 +337,16 @@ public void trackEventFromLibrary() { /* Static track call forbidden if app didn't start Analytics. */ Analytics.trackEvent("eventName"); - verify(channel, never()).enqueue(isA(EventLog.class), anyString()); + verify(channel, never()).enqueue(isA(EventLog.class), anyString(), anyInt()); /* It works from a target. */ AnalyticsTransmissionTarget target = Analytics.getTransmissionTarget("t"); target.trackEvent("eventName"); - verify(channel).enqueue(isA(EventLog.class), anyString()); + verify(channel).enqueue(isA(EventLog.class), anyString(), eq(DEFAULT_FLAGS)); /* It works from a child target. */ target.getTransmissionTarget("t2").trackEvent("eventName"); - verify(channel, times(2)).enqueue(isA(EventLog.class), anyString()); + verify(channel, times(2)).enqueue(isA(EventLog.class), anyString(), eq(DEFAULT_FLAGS)); } @Test @@ -356,7 +356,7 @@ public void trackPageFromApp() { analytics.onStarting(mAppCenterHandler); analytics.onStarted(mock(Context.class), channel, "", null, true); Analytics.trackPage("pageName"); - verify(channel).enqueue(isA(PageLog.class), anyString()); + verify(channel).enqueue(isA(PageLog.class), anyString(), eq(DEFAULT_FLAGS)); } @Test @@ -368,7 +368,7 @@ public void trackPageFromLibrary() { /* Page tracking does not work from library. */ Analytics.trackPage("pageName"); - verify(channel, never()).enqueue(isA(PageLog.class), anyString()); + verify(channel, never()).enqueue(isA(PageLog.class), anyString(), anyInt()); } @Test @@ -412,7 +412,7 @@ public void setEnabled() throws InterruptedException { Analytics.trackPage("test"); analytics.onActivityResumed(new Activity()); analytics.onActivityPaused(new Activity()); - verify(channel, never()).enqueue(any(Log.class), eq(analytics.getGroupName())); + verify(channel, never()).enqueue(any(Log.class), eq(analytics.getGroupName()), anyInt()); /* Enable again, verify the async behavior of setEnabled with the callback. */ final CountDownLatch latch = new CountDownLatch(1); @@ -433,7 +433,7 @@ public void accept(Void aVoid) { target.trackEvent("test"); target.getTransmissionTarget("t2").trackEvent("test"); Analytics.trackPage("test"); - verify(channel, times(4)).enqueue(any(Log.class), eq(analytics.getGroupName())); + verify(channel, times(4)).enqueue(any(Log.class), eq(analytics.getGroupName()), eq(DEFAULT_FLAGS)); /* Disable again. */ Analytics.setEnabled(false); @@ -444,7 +444,7 @@ public void accept(Void aVoid) { analytics.onActivityPaused(new Activity()); /* No more log enqueued. */ - verify(channel, times(4)).enqueue(any(Log.class), eq(analytics.getGroupName())); + verify(channel, times(4)).enqueue(any(Log.class), eq(analytics.getGroupName()), eq(DEFAULT_FLAGS)); } @Test @@ -483,7 +483,7 @@ public void notSendingLogsOnPause() { /* Send logs to verify the logs are enqueued after pause. */ Analytics.trackEvent("test"); Analytics.trackPage("test"); - verify(channel, times(2)).enqueue(any(Log.class), eq(analytics.getGroupName())); + verify(channel, times(2)).enqueue(any(Log.class), eq(analytics.getGroupName()), eq(DEFAULT_FLAGS)); /* Resume Analytics. */ Analytics.resume(); @@ -494,7 +494,7 @@ public void notSendingLogsOnPause() { /* Send logs to verify the logs are enqueued after resume. */ Analytics.trackEvent("test"); Analytics.trackPage("test"); - verify(channel, times(4)).enqueue(any(Log.class), eq(analytics.getGroupName())); + verify(channel, times(4)).enqueue(any(Log.class), eq(analytics.getGroupName()), eq(DEFAULT_FLAGS)); } @Test @@ -521,7 +521,7 @@ public void pauseResumeWhileDisabled() { /* Send logs to verify the logs are enqueued after pause. */ Analytics.trackEvent("test"); Analytics.trackPage("test"); - verify(channel, never()).enqueue(any(Log.class), eq(analytics.getGroupName())); + verify(channel, never()).enqueue(any(Log.class), eq(analytics.getGroupName()), anyInt()); /* Resume Analytics. */ Analytics.resume(); @@ -532,7 +532,7 @@ public void pauseResumeWhileDisabled() { /* Send logs to verify the logs are enqueued after resume. */ Analytics.trackEvent("test"); Analytics.trackPage("test"); - verify(channel, never()).enqueue(any(Log.class), eq(analytics.getGroupName())); + verify(channel, never()).enqueue(any(Log.class), eq(analytics.getGroupName()), anyInt()); } @Test @@ -550,7 +550,7 @@ public void startSessionAfterUserApproval() { /* App in foreground: no log yet, we are disabled. */ analytics.onActivityResumed(new Activity()); - verify(channel, never()).enqueue(any(Log.class), eq(analytics.getGroupName())); + verify(channel, never()).enqueue(any(Log.class), eq(analytics.getGroupName()), anyInt()); /* Enable: start session sent retroactively. */ Analytics.setEnabled(true); @@ -560,14 +560,14 @@ public void startSessionAfterUserApproval() { public boolean matches(Object argument) { return argument instanceof StartSessionLog; } - }), eq(analytics.getGroupName())); + }), eq(analytics.getGroupName()), eq(DEFAULT_FLAGS)); verify(channel).enqueue(argThat(new ArgumentMatcher() { @Override public boolean matches(Object argument) { return argument instanceof PageLog; } - }), eq(analytics.getGroupName())); + }), eq(analytics.getGroupName()), eq(DEFAULT_FLAGS)); /* Go background. */ analytics.onActivityPaused(new Activity()); @@ -577,7 +577,7 @@ public boolean matches(Object argument) { Analytics.setEnabled(true); /* No additional log. */ - verify(channel, times(2)).enqueue(any(Log.class), eq(analytics.getGroupName())); + verify(channel, times(2)).enqueue(any(Log.class), eq(analytics.getGroupName()), eq(DEFAULT_FLAGS)); } @Test @@ -596,11 +596,11 @@ public void startSessionAfterUserApprovalWeakReference() { /* App in foreground: no log yet, we are disabled. */ analytics.onActivityResumed(new Activity()); analytics.getCurrentActivity().clear(); - verify(channel, never()).enqueue(any(Log.class), eq(analytics.getGroupName())); + verify(channel, never()).enqueue(any(Log.class), eq(analytics.getGroupName()), anyInt()); /* Enable: start session not sent retroactively, weak reference lost. */ Analytics.setEnabled(true); - verify(channel, never()).enqueue(any(Log.class), eq(analytics.getGroupName())); + verify(channel, never()).enqueue(any(Log.class), eq(analytics.getGroupName()), anyInt()); } @Test @@ -622,7 +622,7 @@ public Void answer(InvocationOnMock invocation) { captor.getValue().onFailure((Log) invocation.getArguments()[0], new Exception()); return null; } - }).when(channel).enqueue(any(Log.class), anyString()); + }).when(channel).enqueue(any(Log.class), anyString(), anyInt()); Analytics.trackEvent("name"); verify(listener).onBeforeSending(notNull(Log.class)); verify(listener).onSendingSucceeded(notNull(Log.class)); @@ -664,22 +664,22 @@ public void appOnlyFeatures() { analytics.onActivityResumed(activity); analytics.onActivityPaused(activity); analytics.onActivityResumed(new MyActivity()); - verify(channel, never()).enqueue(isA(StartSessionLog.class), eq(analytics.getGroupName())); - verify(channel, never()).enqueue(isA(PageLog.class), eq(analytics.getGroupName())); + verify(channel, never()).enqueue(isA(StartSessionLog.class), eq(analytics.getGroupName()), anyInt()); + verify(channel, never()).enqueue(isA(PageLog.class), eq(analytics.getGroupName()), anyInt()); /* Even when switching states. */ Analytics.setEnabled(false); Analytics.setEnabled(true); verify(channel, never()).addListener(isA(SessionTracker.class)); - verify(channel, never()).enqueue(isA(StartSessionLog.class), eq(analytics.getGroupName())); - verify(channel, never()).enqueue(isA(PageLog.class), eq(analytics.getGroupName())); + verify(channel, never()).enqueue(isA(StartSessionLog.class), eq(analytics.getGroupName()), anyInt()); + verify(channel, never()).enqueue(isA(PageLog.class), eq(analytics.getGroupName()), anyInt()); /* Now start app, no secret needed. */ analytics.onConfigurationUpdated(null, null); /* Session tracker is started now. */ verify(channel).addListener(isA(SessionTracker.class)); - verify(channel).enqueue(isA(StartSessionLog.class), anyString()); + verify(channel).enqueue(isA(StartSessionLog.class), anyString(), eq(DEFAULT_FLAGS)); /* Verify last page tracked as still in foreground. */ verify(channel).enqueue(argThat(new ArgumentMatcher() { @@ -692,10 +692,10 @@ public boolean matches(Object item) { } return false; } - }), eq(analytics.getGroupName())); + }), eq(analytics.getGroupName()), eq(DEFAULT_FLAGS)); /* Check that was the only page sent. */ - verify(channel).enqueue(isA(PageLog.class), eq(analytics.getGroupName())); + verify(channel).enqueue(isA(PageLog.class), eq(analytics.getGroupName()), eq(DEFAULT_FLAGS)); /* Session tracker removed if disabled. */ Analytics.setEnabled(false); @@ -704,8 +704,8 @@ public boolean matches(Object item) { /* And added again on enabling again. Page tracked again. */ Analytics.setEnabled(true); verify(channel, times(2)).addListener(isA(SessionTracker.class)); - verify(channel, times(2)).enqueue(isA(StartSessionLog.class), eq(analytics.getGroupName())); - verify(channel, times(2)).enqueue(isA(PageLog.class), eq(analytics.getGroupName())); + verify(channel, times(2)).enqueue(isA(StartSessionLog.class), eq(analytics.getGroupName()), eq(DEFAULT_FLAGS)); + verify(channel, times(2)).enqueue(isA(PageLog.class), eq(analytics.getGroupName()), eq(DEFAULT_FLAGS)); } @Test diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java index 0ad62a7ff5..26daabe257 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.List; +import static com.microsoft.appcenter.Flags.DEFAULT_FLAGS; import static com.microsoft.appcenter.analytics.Analytics.ANALYTICS_GROUP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -36,9 +37,11 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.contains; +import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; @@ -140,9 +143,9 @@ public boolean matches(Object item) { } return false; } - }), anyString()); + }), anyString(), eq(DEFAULT_FLAGS)); } else { - verify(mChannel, never()).enqueue(isA(EventLog.class), anyString()); + verify(mChannel, never()).enqueue(isA(EventLog.class), anyString(), anyInt()); } reset(mChannel); @@ -161,7 +164,7 @@ public boolean matches(Object item) { } return false; } - }), anyString()); + }), anyString(), eq(DEFAULT_FLAGS)); reset(mChannel); /* Track event via another transmission target method with properties. */ @@ -186,7 +189,7 @@ public boolean matches(Object item) { } return false; } - }), anyString()); + }), anyString(), eq(DEFAULT_FLAGS)); reset(mChannel); /* Create a child transmission target and track event. */ @@ -205,7 +208,7 @@ public boolean matches(Object item) { } return false; } - }), anyString()); + }), anyString(), eq(DEFAULT_FLAGS)); reset(mChannel); /* Another child transmission target with the same token should be the same instance. */ @@ -229,7 +232,7 @@ public boolean matches(Object item) { } return false; } - }), anyString()); + }), anyString(), eq(DEFAULT_FLAGS)); /* Set enabled to false and assert that it cannot track event. */ transmissionTarget.setEnabledAsync(false).get(); @@ -245,7 +248,7 @@ public boolean matches(Object item) { } return false; } - }), anyString()); + }), anyString(), anyInt()); } @Test @@ -270,7 +273,7 @@ public boolean matches(Object item) { } return false; } - }), anyString()); + }), anyString(), anyInt()); /* Set enabled to true on parent. Verify that child can track event. */ parentTransmissionTarget.setEnabledAsync(true); @@ -285,7 +288,7 @@ public boolean matches(Object item) { } return false; } - }), anyString()); + }), anyString(), eq(DEFAULT_FLAGS)); } @Test @@ -300,7 +303,7 @@ public void setEnabledOnChild() { childTransmissionTarget.setEnabledAsync(true); assertFalse(childTransmissionTarget.isEnabledAsync().get()); childTransmissionTarget.trackEvent("eventName"); - verify(mChannel, never()).enqueue(any(Log.class), anyString()); + verify(mChannel, never()).enqueue(any(Log.class), anyString(), anyInt()); } @Test @@ -454,7 +457,7 @@ public Object answer(InvocationOnMock invocation) { AnalyticsTransmissionTarget.getChannelListener().onPreparingLog(log, "test"); return null; } - }).when(mChannel).enqueue(any(Log.class), anyString()); + }).when(mChannel).enqueue(any(Log.class), anyString(), anyInt()); /* Start analytics and simulate background thread handler (we hold the thread command and run it in the test). */ Analytics analytics = Analytics.getInstance(); diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/PropertyConfiguratorTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/PropertyConfiguratorTest.java index 5a9ea74526..15ab090186 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/PropertyConfiguratorTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/PropertyConfiguratorTest.java @@ -37,6 +37,7 @@ import java.util.List; import java.util.Map; +import static com.microsoft.appcenter.Flags.DEFAULT_FLAGS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; @@ -44,6 +45,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; import static org.mockito.Matchers.notNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -330,7 +332,7 @@ public void setCommonEventProperties() { /* Check event. */ ArgumentCaptor eventLogArg = ArgumentCaptor.forClass(EventLog.class); - verify(mChannel).enqueue(eventLogArg.capture(), anyString()); + verify(mChannel).enqueue(eventLogArg.capture(), anyString(), eq(DEFAULT_FLAGS)); EventLog log = eventLogArg.getValue(); assertNotNull(log); assertEquals(Collections.singleton("test"), log.getTransmissionTargetTokens()); @@ -353,7 +355,7 @@ public void setCommonEventPropertiesWithNullMapProperties() { /* Check event. */ ArgumentCaptor eventLogArg = ArgumentCaptor.forClass(EventLog.class); - verify(mChannel).enqueue(eventLogArg.capture(), anyString()); + verify(mChannel).enqueue(eventLogArg.capture(), anyString(), eq(DEFAULT_FLAGS)); EventLog log = eventLogArg.getValue(); assertNotNull(log); assertEquals(Collections.singleton("test"), log.getTransmissionTargetTokens()); @@ -375,7 +377,7 @@ public void trackEventWithEmptyProperties() { /* Check what event was sent. */ ArgumentCaptor eventLogArg = ArgumentCaptor.forClass(EventLog.class); - verify(mChannel).enqueue(eventLogArg.capture(), anyString()); + verify(mChannel).enqueue(eventLogArg.capture(), anyString(), eq(DEFAULT_FLAGS)); EventLog log = eventLogArg.getValue(); assertNotNull(log); assertEquals(Collections.singleton("test"), log.getTransmissionTargetTokens()); @@ -395,7 +397,7 @@ public void trackEventWithEmptyTypedProperties() { /* Check what event was sent. */ ArgumentCaptor eventLogArg = ArgumentCaptor.forClass(EventLog.class); - verify(mChannel).enqueue(eventLogArg.capture(), anyString()); + verify(mChannel).enqueue(eventLogArg.capture(), anyString(), eq(DEFAULT_FLAGS)); EventLog log = eventLogArg.getValue(); assertNotNull(log); assertEquals(Collections.singleton("test"), log.getTransmissionTargetTokens()); @@ -430,7 +432,7 @@ public void trackEventWithCommonTypedProperties() { /* Check what event was sent. */ ArgumentCaptor eventLogArg = ArgumentCaptor.forClass(EventLog.class); - verify(mChannel).enqueue(eventLogArg.capture(), anyString()); + verify(mChannel).enqueue(eventLogArg.capture(), anyString(), eq(DEFAULT_FLAGS)); EventLog log = eventLogArg.getValue(); assertNotNull(log); assertEquals(Collections.singleton("test"), log.getTransmissionTargetTokens()); @@ -487,7 +489,7 @@ public void eventPropertiesCascading() { /* Verify log that was sent. */ ArgumentCaptor logArgumentCaptor = ArgumentCaptor.forClass(EventLog.class); - verify(mChannel).enqueue(logArgumentCaptor.capture(), anyString()); + verify(mChannel).enqueue(logArgumentCaptor.capture(), anyString(), eq(DEFAULT_FLAGS)); EventLog log = logArgumentCaptor.getValue(); assertNotNull(log); assertEquals("eventName", log.getName()); @@ -546,7 +548,7 @@ public void eventPropertiesCascadingWithTypes() { /* Verify log that was sent. */ ArgumentCaptor logArgumentCaptor = ArgumentCaptor.forClass(EventLog.class); - verify(mChannel).enqueue(logArgumentCaptor.capture(), anyString()); + verify(mChannel).enqueue(logArgumentCaptor.capture(), anyString(), eq(DEFAULT_FLAGS)); EventLog log = logArgumentCaptor.getValue(); assertNotNull(log); assertEquals("eventName", log.getName()); diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java index a2cfdb73ea..7320742396 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java @@ -27,6 +27,7 @@ import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; +import static com.microsoft.appcenter.Flags.DEFAULT_FLAGS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -34,6 +35,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anySetOf; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.argThat; @@ -117,7 +119,7 @@ public void longSessionStartingFromBackground() { assertNotNull(log.getSid()); firstSid = expectedSid = log.getSid(); expectedStartSessionLog.setSid(expectedSid); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* Verify session reused for second log. */ @@ -126,7 +128,7 @@ public void longSessionStartingFromBackground() { mSessionTracker.onPreparingLog(log, TEST_GROUP); mSessionTracker.onPreparingLog(expectedStartSessionLog, TEST_GROUP); assertEquals(expectedSid, log.getSid()); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* No usage from background for a long time: new session. */ @@ -138,7 +140,7 @@ public void longSessionStartingFromBackground() { assertNotEquals(expectedSid, log.getSid()); expectedSid = log.getSid(); expectedStartSessionLog.setSid(expectedSid); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* App comes to foreground and sends a log, still session. */ @@ -148,7 +150,7 @@ public void longSessionStartingFromBackground() { mSessionTracker.onPreparingLog(log, TEST_GROUP); mSessionTracker.onPreparingLog(expectedStartSessionLog, TEST_GROUP); assertEquals(expectedSid, log.getSid()); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* We are in foreground, even after timeout a log is still in session. */ @@ -158,7 +160,7 @@ public void longSessionStartingFromBackground() { mSessionTracker.onPreparingLog(log, TEST_GROUP); mSessionTracker.onPreparingLog(expectedStartSessionLog, TEST_GROUP); assertEquals(expectedSid, log.getSid()); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* Switch to another activity and send a log, still session. */ @@ -171,7 +173,7 @@ public void longSessionStartingFromBackground() { mSessionTracker.onPreparingLog(log, TEST_GROUP); mSessionTracker.onPreparingLog(expectedStartSessionLog, TEST_GROUP); assertEquals(expectedSid, log.getSid()); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* We are in foreground, even after timeout a log is still in session. */ @@ -181,7 +183,7 @@ public void longSessionStartingFromBackground() { mSessionTracker.onPreparingLog(log, TEST_GROUP); mSessionTracker.onPreparingLog(expectedStartSessionLog, TEST_GROUP); assertEquals(expectedSid, log.getSid()); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* Background for a short time and send log: still in session. */ @@ -193,7 +195,7 @@ public void longSessionStartingFromBackground() { mSessionTracker.onPreparingLog(log, TEST_GROUP); mSessionTracker.onPreparingLog(expectedStartSessionLog, TEST_GROUP); assertEquals(expectedSid, log.getSid()); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* Background for a long time but correlating a log to first session: should not trigger new session. */ @@ -203,7 +205,7 @@ public void longSessionStartingFromBackground() { mSessionTracker.onPreparingLog(log, TEST_GROUP); mSessionTracker.onPreparingLog(expectedStartSessionLog, TEST_GROUP); assertEquals(firstSid, log.getSid()); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* Background for a long time and coming back to foreground: new session. */ @@ -216,7 +218,7 @@ public void longSessionStartingFromBackground() { assertNotEquals(expectedSid, log.getSid()); expectedSid = log.getSid(); expectedStartSessionLog.setSid(expectedSid); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* Background for a long time sending a log: new session. */ @@ -229,7 +231,7 @@ public void longSessionStartingFromBackground() { assertNotEquals(expectedSid, log.getSid()); expectedSid = log.getSid(); expectedStartSessionLog.setSid(expectedSid); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } } @@ -247,7 +249,7 @@ public void stayOnFirstScreenForLong() { assertNotNull(log.getSid()); expectedSid = log.getSid(); expectedStartSessionLog.setSid(expectedSid); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* Wait a long time. */ @@ -264,7 +266,7 @@ public void stayOnFirstScreenForLong() { mSessionTracker.onPreparingLog(log, TEST_GROUP); mSessionTracker.onPreparingLog(expectedStartSessionLog, TEST_GROUP); assertEquals(expectedSid, log.getSid()); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } } @@ -282,7 +284,7 @@ public void goBackgroundAndComeBackMuchLater() { assertNotNull(log.getSid()); expectedSid = log.getSid(); expectedStartSessionLog.setSid(expectedSid); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* Go background. */ @@ -301,7 +303,7 @@ public void goBackgroundAndComeBackMuchLater() { assertNotEquals(expectedSid, log.getSid()); expectedSid = log.getSid(); expectedStartSessionLog.setSid(expectedSid); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* In total we sent only 2 session logs. */ @@ -311,7 +313,7 @@ public void goBackgroundAndComeBackMuchLater() { public boolean matches(Object argument) { return argument instanceof StartSessionLog; } - }), anyString()); + }), anyString(), eq(DEFAULT_FLAGS)); } @Test @@ -325,11 +327,11 @@ public Object answer(InvocationOnMock invocation) { startSessionLog.set((StartSessionLog) invocation.getArguments()[0]); return null; } - }).when(mChannel).enqueue(notNull(StartSessionLog.class), eq(TEST_GROUP)); + }).when(mChannel).enqueue(notNull(StartSessionLog.class), eq(TEST_GROUP), anyInt()); /* Go foreground, start session is sent. */ mSessionTracker.onActivityResumed(); - verify(mChannel, times(1)).enqueue(notNull(StartSessionLog.class), eq(TEST_GROUP)); + verify(mChannel, times(1)).enqueue(notNull(StartSessionLog.class), eq(TEST_GROUP), eq(DEFAULT_FLAGS)); assertNotNull(startSessionLog.get()); UUID sid = startSessionLog.get().getSid(); assertNotNull(sid); @@ -339,14 +341,14 @@ public Object answer(InvocationOnMock invocation) { mSessionTracker.onActivityPaused(); spendTime(1); mSessionTracker.onActivityResumed(); - verify(mChannel, times(1)).enqueue(notNull(StartSessionLog.class), eq(TEST_GROUP)); + verify(mChannel, times(1)).enqueue(notNull(StartSessionLog.class), eq(TEST_GROUP), eq(DEFAULT_FLAGS)); /* Go background and come back after timeout, second session. */ spendTime(1); mSessionTracker.onActivityPaused(); spendTime(30000); mSessionTracker.onActivityResumed(); - verify(mChannel, times(2)).enqueue(notNull(StartSessionLog.class), eq(TEST_GROUP)); + verify(mChannel, times(2)).enqueue(notNull(StartSessionLog.class), eq(TEST_GROUP), eq(DEFAULT_FLAGS)); assertNotEquals(sid, startSessionLog.get().getSid()); } @@ -366,7 +368,7 @@ public void sdkConfiguredBetweenPauseAndResume() { assertNotNull(log.getSid()); expectedSid = log.getSid(); expectedStartSessionLog.setSid(expectedSid); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* Verify session reused for second log. */ @@ -375,7 +377,7 @@ public void sdkConfiguredBetweenPauseAndResume() { mSessionTracker.onPreparingLog(log, TEST_GROUP); mSessionTracker.onPreparingLog(expectedStartSessionLog, TEST_GROUP); assertEquals(expectedSid, log.getSid()); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* No usage from background for a long time: new session. */ @@ -387,7 +389,7 @@ public void sdkConfiguredBetweenPauseAndResume() { assertNotEquals(expectedSid, log.getSid()); expectedSid = log.getSid(); expectedStartSessionLog.setSid(expectedSid); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* App comes to foreground and sends a log, we were in background for a long time but we sent a log recently, still session. */ @@ -397,7 +399,7 @@ public void sdkConfiguredBetweenPauseAndResume() { mSessionTracker.onPreparingLog(log, TEST_GROUP); mSessionTracker.onPreparingLog(expectedStartSessionLog, TEST_GROUP); assertEquals(expectedSid, log.getSid()); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } /* We are in foreground, even after timeout a log is still in session. */ @@ -407,7 +409,7 @@ public void sdkConfiguredBetweenPauseAndResume() { mSessionTracker.onPreparingLog(log, TEST_GROUP); mSessionTracker.onPreparingLog(expectedStartSessionLog, TEST_GROUP); assertEquals(expectedSid, log.getSid()); - verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP); + verify(mChannel).enqueue(expectedStartSessionLog, TEST_GROUP, DEFAULT_FLAGS); } } @@ -584,7 +586,7 @@ public void partiallyInvalidStorage() { public void ignoreStartService() { Log startServiceLog = spy(new StartServiceLog()); mSessionTracker.onPreparingLog(startServiceLog, TEST_GROUP); - verify(mChannel, never()).enqueue(any(Log.class), anyString()); + verify(mChannel, never()).enqueue(any(Log.class), anyString(), anyInt()); verify(startServiceLog, never()).setSid(any(UUID.class)); } diff --git a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java index 3cdbc07170..06a6f4e731 100644 --- a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java +++ b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/CrashesAndroidTest.java @@ -17,8 +17,8 @@ import com.microsoft.appcenter.ingestion.models.Log; import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.async.AppCenterConsumer; -import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import com.microsoft.appcenter.utils.storage.FileManager; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.After; import org.junit.Before; @@ -35,6 +35,8 @@ import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicReference; +import static com.microsoft.appcenter.Flags.DEFAULT_FLAGS; +import static com.microsoft.appcenter.Flags.PERSISTENCE_CRITICAL; import static com.microsoft.appcenter.test.TestUtils.TAG; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -294,7 +296,7 @@ public boolean matches(Object o) { return o instanceof ManagedErrorLog; } }; - verify(mChannel, never()).enqueue(argThat(matchCrashLog), anyString()); + verify(mChannel, never()).enqueue(argThat(matchCrashLog), anyString(), anyInt()); assertEquals(2, ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter).length); verify(crashesListener).shouldProcess(any(ErrorReport.class)); verify(crashesListener).shouldAwaitUserConfirmation(); @@ -309,10 +311,10 @@ public Object answer(InvocationOnMock invocationOnMock) { log.set((Log) invocationOnMock.getArguments()[0]); return null; } - }).when(mChannel).enqueue(argThat(matchCrashLog), anyString()); + }).when(mChannel).enqueue(argThat(matchCrashLog), anyString(), anyInt()); Crashes.notifyUserConfirmation(Crashes.ALWAYS_SEND); assertTrue(Crashes.isEnabled().get()); - verify(mChannel).enqueue(argThat(matchCrashLog), anyString()); + verify(mChannel).enqueue(argThat(matchCrashLog), anyString(), eq(PERSISTENCE_CRITICAL)); assertNotNull(log.get()); assertEquals(1, ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter).length); @@ -349,7 +351,7 @@ public void run() { semaphore.acquire(); assertEquals(0, ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter).length); - verify(mChannel, never()).enqueue(argThat(matchCrashLog), anyString()); + verify(mChannel, never()).enqueue(argThat(matchCrashLog), anyString(), anyInt()); verify(crashesListener).onBeforeSending(any(ErrorReport.class)); verify(crashesListener).onSendingSucceeded(any(ErrorReport.class)); verifyNoMoreInteractions(crashesListener); @@ -400,7 +402,7 @@ public boolean matches(Object o) { return o instanceof ManagedErrorLog; } }; - verify(mChannel, never()).enqueue(argThat(matchCrashLog), anyString()); + verify(mChannel, never()).enqueue(argThat(matchCrashLog), anyString(), anyInt()); assertEquals(2, ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter).length); verify(crashesListener).shouldProcess(any(ErrorReport.class)); verify(crashesListener).shouldAwaitUserConfirmation(); @@ -415,10 +417,10 @@ public Object answer(InvocationOnMock invocationOnMock) { log.set((Log) invocationOnMock.getArguments()[0]); return null; } - }).when(mChannel).enqueue(argThat(matchCrashLog), anyString()); + }).when(mChannel).enqueue(argThat(matchCrashLog), anyString(), anyInt()); Crashes.notifyUserConfirmation(Crashes.SEND); assertTrue(Crashes.isEnabled().get()); - verify(mChannel).enqueue(argThat(matchCrashLog), anyString()); + verify(mChannel).enqueue(argThat(matchCrashLog), anyString(), eq(PERSISTENCE_CRITICAL)); assertNotNull(log.get()); assertEquals(1, ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter).length); verify(crashesListener).getErrorAttachments(any(ErrorReport.class)); @@ -435,10 +437,10 @@ public boolean matches(Object argument) { } return false; } - }), anyString()); + }), anyString(), eq(DEFAULT_FLAGS)); /* Verify custom text attachment. */ - verify(mChannel).enqueue(eq(textAttachment), anyString()); + verify(mChannel).enqueue(eq(textAttachment), anyString(), eq(DEFAULT_FLAGS)); } @Test diff --git a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java index 2f6f6122e7..084936ef64 100644 --- a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java +++ b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/Crashes.java @@ -8,6 +8,7 @@ import com.microsoft.appcenter.AbstractAppCenterService; import com.microsoft.appcenter.Constants; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.SessionContext; import com.microsoft.appcenter.channel.Channel; import com.microsoft.appcenter.crashes.ingestion.models.ErrorAttachmentLog; @@ -534,7 +535,7 @@ public void run() { errorLog.setId(UUID.randomUUID()); errorLog.setException(exceptionModelBuilder.buildExceptionModel()); errorLog.setProperties(properties); - mChannel.enqueue(errorLog, ERROR_GROUP); + mChannel.enqueue(errorLog, ERROR_GROUP, Flags.DEFAULT_FLAGS); } }); } @@ -820,7 +821,7 @@ public void run() { } /* Send report. */ - mChannel.enqueue(errorLogReport.log, ERROR_GROUP); + mChannel.enqueue(errorLogReport.log, ERROR_GROUP, Flags.PERSISTENCE_CRITICAL); /* Send dump attachment and remove file. */ if (dumpAttachment != null) { @@ -859,7 +860,7 @@ private void sendErrorAttachment(UUID errorId, Iterable atta attachment.setErrorId(errorId); if (attachment.isValid()) { ++totalErrorAttachments; - mChannel.enqueue(attachment, ERROR_GROUP); + mChannel.enqueue(attachment, ERROR_GROUP, Flags.DEFAULT_FLAGS); } else { AppCenterLog.error(LOG_TAG, "Not all required fields are present in ErrorAttachmentLog."); } diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java index 1052d3cc9c..b41099c70f 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java @@ -66,6 +66,8 @@ import java.util.Map; import java.util.UUID; +import static com.microsoft.appcenter.Flags.DEFAULT_FLAGS; +import static com.microsoft.appcenter.Flags.PERSISTENCE_CRITICAL; import static com.microsoft.appcenter.test.TestUtils.generateString; import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; @@ -80,6 +82,7 @@ import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.contains; import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -281,7 +284,7 @@ public void setEnabled() { assertTrue(Crashes.isEnabled().get()); verify(mockChannel, times(2)).addGroup(eq(crashes.getGroupName()), anyInt(), anyInt(), anyInt(), isNull(Ingestion.class), any(Channel.GroupListener.class)); Crashes.trackException(EXCEPTION); - verify(mockChannel, times(1)).enqueue(any(ManagedErrorLog.class), eq(crashes.getGroupName())); + verify(mockChannel, times(1)).enqueue(isA(HandledErrorLog.class), eq(crashes.getGroupName()), eq(DEFAULT_FLAGS)); } @Test @@ -344,8 +347,8 @@ public void queuePendingCrashesShouldProcess() throws IOException, ClassNotFound public boolean matches(Object log) { return log.equals(mErrorLog); } - }), eq(crashes.getGroupName())); - verify(mockChannel, times(errorAttachmentLogList.size() - skipAttachmentLogsCount)).enqueue(mockAttachment, crashes.getGroupName()); + }), eq(crashes.getGroupName()), eq(PERSISTENCE_CRITICAL)); + verify(mockChannel, times(errorAttachmentLogList.size() - skipAttachmentLogsCount)).enqueue(mockAttachment, crashes.getGroupName(), DEFAULT_FLAGS); } @Test @@ -379,7 +382,7 @@ public void queuePendingCrashesShouldNotProcess() throws IOException, ClassNotFo verify(mockListener, never()).shouldAwaitUserConfirmation(); verify(mockListener, never()).getErrorAttachments(report); - verify(mockChannel, never()).enqueue(any(Log.class), eq(crashes.getGroupName())); + verify(mockChannel, never()).enqueue(any(Log.class), eq(crashes.getGroupName()), anyInt()); } @Test @@ -433,9 +436,9 @@ public void queuePendingCrashesAlwaysSend() throws IOException, ClassNotFoundExc public boolean matches(Object log) { return log.equals(mErrorLog); } - }), eq(crashes.getGroupName())); + }), eq(crashes.getGroupName()), eq(PERSISTENCE_CRITICAL)); - verify(mockChannel, times(errorAttachmentLogList.size())).enqueue(mockAttachment, crashes.getGroupName()); + verify(mockChannel, times(errorAttachmentLogList.size())).enqueue(mockAttachment, crashes.getGroupName(), DEFAULT_FLAGS); } @Test @@ -458,7 +461,7 @@ public void processPendingErrorsCorrupted() throws JSONException { crashes.onStarting(mAppCenterHandler); crashes.onStarted(mock(Context.class), channel, "", null, true); verifyZeroInteractions(listener); - verify(channel, never()).enqueue(any(Log.class), anyString()); + verify(channel, never()).enqueue(any(Log.class), anyString(), anyInt()); } @Test @@ -470,7 +473,7 @@ public void noQueueingWhenDisabled() { Crashes crashes = Crashes.getInstance(); crashes.onStarting(mAppCenterHandler); crashes.onStarted(mock(Context.class), channel, "", null, true); - verify(channel, never()).enqueue(any(Log.class), anyString()); + verify(channel, never()).enqueue(any(Log.class), anyString(), anyInt()); } @Test @@ -490,7 +493,7 @@ public void noQueueNullLog() throws JSONException { crashes.onStarting(mAppCenterHandler); crashes.onStarted(mockContext, mockChannel, "", null, true); - verify(mockChannel, never()).enqueue(any(Log.class), anyString()); + verify(mockChannel, never()).enqueue(any(Log.class), anyString(), anyInt()); } @Test @@ -512,7 +515,7 @@ public void printErrorOnJSONException() throws JSONException { crashes.onStarting(mAppCenterHandler); crashes.onStarted(mockContext, mockChannel, "", null, true); - verify(mockChannel, never()).enqueue(any(Log.class), anyString()); + verify(mockChannel, never()).enqueue(any(Log.class), anyString(), anyInt()); verifyStatic(); AppCenterLog.error(eq(Crashes.LOG_TAG), anyString(), eq(jsonException)); @@ -544,7 +547,7 @@ public void trackException() { public boolean matches(Object item) { return item instanceof HandledErrorLog && EXCEPTION.getMessage().equals(((HandledErrorLog) item).getException().getMessage()); } - }), eq(crashes.getGroupName())); + }), eq(crashes.getGroupName()), eq(DEFAULT_FLAGS)); reset(mockChannel); Crashes.trackException(EXCEPTION, new HashMap() {{ put(null, null); @@ -559,7 +562,7 @@ public boolean matches(Object item) { return item instanceof HandledErrorLog && EXCEPTION.getMessage().equals(((HandledErrorLog) item).getException().getMessage()) && ((HandledErrorLog) item).getProperties().size() == 0; } - }), eq(crashes.getGroupName())); + }), eq(crashes.getGroupName()), eq(DEFAULT_FLAGS)); reset(mockChannel); Crashes.trackException(EXCEPTION, new HashMap() {{ for (int i = 0; i < 30; i++) { @@ -573,7 +576,7 @@ public boolean matches(Object item) { return item instanceof HandledErrorLog && EXCEPTION.getMessage().equals(((HandledErrorLog) item).getException().getMessage()) && ((HandledErrorLog) item).getProperties().size() == 20; } - }), eq(crashes.getGroupName())); + }), eq(crashes.getGroupName()), eq(DEFAULT_FLAGS)); reset(mockChannel); final String longerMapItem = generateString(ErrorLogHelper.MAX_PROPERTY_ITEM_LENGTH + 1, '*'); Crashes.trackException(EXCEPTION, new HashMap() {{ @@ -595,7 +598,7 @@ public boolean matches(Object item) { } return false; } - }), eq(crashes.getGroupName())); + }), eq(crashes.getGroupName()), eq(DEFAULT_FLAGS)); HandledErrorLog mockLog = mock(HandledErrorLog.class); CrashesListener mockListener = mock(CrashesListener.class); @@ -634,7 +637,7 @@ public void trackExceptionForWrapperSdk() { Channel mockChannel = mock(Channel.class); WrapperSdkExceptionManager.trackException(exception); - verify(mockChannel, never()).enqueue(any(Log.class), eq(crashes.getGroupName())); + verify(mockChannel, never()).enqueue(any(Log.class), eq(crashes.getGroupName()), anyInt()); crashes.onStarting(mAppCenterHandler); crashes.onStarted(mock(Context.class), mockChannel, "", null, true); WrapperSdkExceptionManager.trackException(exception); @@ -644,7 +647,7 @@ public void trackExceptionForWrapperSdk() { public boolean matches(Object item) { return item instanceof HandledErrorLog && exception.equals(((HandledErrorLog) item).getException()); } - }), eq(crashes.getGroupName())); + }), eq(crashes.getGroupName()), eq(DEFAULT_FLAGS)); reset(mockChannel); WrapperSdkExceptionManager.trackException(exception, new HashMap() {{ put(null, null); @@ -659,7 +662,7 @@ public boolean matches(Object item) { return item instanceof HandledErrorLog && exception.equals(((HandledErrorLog) item).getException()) && ((HandledErrorLog) item).getProperties().size() == 0; } - }), eq(crashes.getGroupName())); + }), eq(crashes.getGroupName()), eq(DEFAULT_FLAGS)); reset(mockChannel); WrapperSdkExceptionManager.trackException(exception, new HashMap() {{ for (int i = 0; i < 30; i++) { @@ -673,7 +676,7 @@ public boolean matches(Object item) { return item instanceof HandledErrorLog && exception.equals(((HandledErrorLog) item).getException()) && ((HandledErrorLog) item).getProperties().size() == 20; } - }), eq(crashes.getGroupName())); + }), eq(crashes.getGroupName()), eq(DEFAULT_FLAGS)); reset(mockChannel); final String longerMapItem = generateString(ErrorLogHelper.MAX_PROPERTY_ITEM_LENGTH + 1, '*'); WrapperSdkExceptionManager.trackException(exception, new HashMap() {{ @@ -695,7 +698,7 @@ public boolean matches(Object item) { } return false; } - }), eq(crashes.getGroupName())); + }), eq(crashes.getGroupName()), eq(DEFAULT_FLAGS)); } @Test @@ -1207,7 +1210,7 @@ public ManagedErrorLog answer(InvocationOnMock invocation) { crashes.onStarted(mockContext, mockChannel, "", null, true); /* No log queued. */ - verify(mockChannel, never()).enqueue(any(Log.class), eq(crashes.getGroupName())); + verify(mockChannel, never()).enqueue(any(Log.class), eq(crashes.getGroupName()), anyInt()); /* Get crash reports. */ Collection reports = WrapperSdkExceptionManager.getUnprocessedErrorReports().get(); @@ -1227,7 +1230,7 @@ public ManagedErrorLog answer(InvocationOnMock invocation) { verifyZeroInteractions(listener); /* No log sent until manual user confirmation in that mode (we are not in always send). */ - verify(mockChannel, never()).enqueue(any(ManagedErrorLog.class), eq(crashes.getGroupName())); + verify(mockChannel, never()).enqueue(any(ManagedErrorLog.class), eq(crashes.getGroupName()), anyInt()); /* Confirm with always send. */ Crashes.notifyUserConfirmation(Crashes.ALWAYS_SEND); @@ -1236,7 +1239,7 @@ public ManagedErrorLog answer(InvocationOnMock invocation) { when(SharedPreferencesManager.getBoolean(eq(Crashes.PREF_KEY_ALWAYS_SEND), anyBoolean())).thenReturn(true); /* 1 log sent. Other one is filtered. */ - verify(mockChannel).enqueue(any(ManagedErrorLog.class), eq(crashes.getGroupName())); + verify(mockChannel).enqueue(any(ManagedErrorLog.class), eq(crashes.getGroupName()), eq(PERSISTENCE_CRITICAL)); /* We can send attachments via wrapper instead of using listener (both work but irrelevant to test with listener). */ ErrorAttachmentLog mockAttachment = mock(ErrorAttachmentLog.class); @@ -1244,7 +1247,7 @@ public ManagedErrorLog answer(InvocationOnMock invocation) { when(mockAttachment.getData()).thenReturn(new byte[0]); when(mockAttachment.isValid()).thenReturn(true); WrapperSdkExceptionManager.sendErrorAttachments(report1.getId(), Collections.singletonList(mockAttachment)); - verify(mockChannel).enqueue(eq(mockAttachment), eq(crashes.getGroupName())); + verify(mockChannel).enqueue(eq(mockAttachment), eq(crashes.getGroupName()), eq(DEFAULT_FLAGS)); /* Send attachment with invalid UUID format for report identifier. */ mockAttachment = mock(ErrorAttachmentLog.class); @@ -1252,7 +1255,7 @@ public ManagedErrorLog answer(InvocationOnMock invocation) { when(mockAttachment.getData()).thenReturn(new byte[0]); when(mockAttachment.isValid()).thenReturn(true); WrapperSdkExceptionManager.sendErrorAttachments("not-a-uuid", Collections.singletonList(mockAttachment)); - verify(mockChannel, never()).enqueue(eq(mockAttachment), eq(crashes.getGroupName())); + verify(mockChannel, never()).enqueue(eq(mockAttachment), eq(crashes.getGroupName()), anyInt()); /* We used manual process function, listener not called and our mock channel does not send events. */ verifyZeroInteractions(listener); @@ -1267,11 +1270,11 @@ public ManagedErrorLog answer(InvocationOnMock invocation) { mockChannel = mock(Channel.class); crashes.onStarted(mockContext, mockChannel, "", null, true); assertTrue(Crashes.isEnabled().get()); - verify(mockChannel, never()).enqueue(any(ManagedErrorLog.class), eq(crashes.getGroupName())); + verify(mockChannel, never()).enqueue(any(ManagedErrorLog.class), eq(crashes.getGroupName()), anyInt()); /* Get crash reports, check always sent was returned and sent without confirmation. */ assertTrue(WrapperSdkExceptionManager.sendCrashReportsOrAwaitUserConfirmation(Collections.singletonList(report2.getId())).get()); - verify(mockChannel).enqueue(any(ManagedErrorLog.class), eq(crashes.getGroupName())); + verify(mockChannel).enqueue(any(ManagedErrorLog.class), eq(crashes.getGroupName()), eq(PERSISTENCE_CRITICAL)); } @Test @@ -1311,7 +1314,7 @@ public ManagedErrorLog answer(InvocationOnMock invocation) { crashes.onStarted(mockContext, mockChannel, "", null, true); /* No log queued. */ - verify(mockChannel, never()).enqueue(any(Log.class), eq(crashes.getGroupName())); + verify(mockChannel, never()).enqueue(any(Log.class), eq(crashes.getGroupName()), anyInt()); /* Get crash reports. */ Collection reports = WrapperSdkExceptionManager.getUnprocessedErrorReports().get(); @@ -1325,7 +1328,7 @@ public ManagedErrorLog answer(InvocationOnMock invocation) { assertFalse(WrapperSdkExceptionManager.sendCrashReportsOrAwaitUserConfirmation(null).get()); /* No log sent. */ - verify(mockChannel, never()).enqueue(any(ManagedErrorLog.class), eq(crashes.getGroupName())); + verify(mockChannel, never()).enqueue(any(ManagedErrorLog.class), eq(crashes.getGroupName()), anyInt()); } private ManagedErrorLog testNativeCrashLog(long appStartTime, long crashTime, boolean correlateSession) throws Exception { diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 99eb70e87d..47b248d457 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -31,6 +31,7 @@ import com.microsoft.appcenter.AbstractAppCenterService; import com.microsoft.appcenter.AppCenter; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.SessionContext; import com.microsoft.appcenter.channel.Channel; import com.microsoft.appcenter.distribute.channel.DistributeInfoTracker; @@ -1157,7 +1158,7 @@ private void changeDistributionGroupIdAfterAppUpdateIfNeeded() { return; } String currentDistributionGroupId = SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID); - if (currentDistributionGroupId != null && lastDownloadedDistributionGroupId.equals(currentDistributionGroupId)) { + if (lastDownloadedDistributionGroupId.equals(currentDistributionGroupId)) { return; } @@ -1868,7 +1869,7 @@ private synchronized void enqueueDistributionStartSessionLog() { @Override public void run() { DistributionStartSessionLog log = new DistributionStartSessionLog(); - mChannel.enqueue(log, DISTRIBUTE_GROUP); + mChannel.enqueue(log, DISTRIBUTE_GROUP, Flags.DEFAULT_FLAGS); } }); } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java index 2d2ee8f720..4bc31132ba 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java @@ -50,6 +50,7 @@ import javax.net.ssl.SSLPeerUnverifiedException; +import static com.microsoft.appcenter.Flags.DEFAULT_FLAGS; import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_ENABLE_UPDATE_SETUP_FAILURE_REDIRECT_KEY; import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_INSTALL_ID; import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_PLATFORM; @@ -1520,7 +1521,7 @@ public void enqueueDistributionStartSessionLogAfterEnablingUpdates() { Distribute.getInstance().storeRedirectionParameters("r", "g", null); /* Verify the log was sent. */ - verify(mChannel).enqueue(any(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName())); + verify(mChannel).enqueue(any(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULT_FLAGS)); } @Test @@ -1540,7 +1541,7 @@ public void dontEnqueueDistributionStartSessionLogIfLastSessionIdIsNull() { Distribute.getInstance().storeRedirectionParameters("r", "g", null); /* Verify the log was sent. */ - verify(mChannel, never()).enqueue(any(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName())); + verify(mChannel, never()).enqueue(any(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULT_FLAGS)); } @Test @@ -1558,7 +1559,7 @@ public void dontEnqueueDistributionStartSessionLogIfNoSessionsWereLoggedBefore() Distribute.getInstance().storeRedirectionParameters("r", "g", null); /* Verify the log was sent. */ - verify(mChannel, never()).enqueue(any(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName())); + verify(mChannel, never()).enqueue(any(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), anyInt()); } @Test diff --git a/sdk/appcenter-push/src/main/java/com/microsoft/appcenter/push/Push.java b/sdk/appcenter-push/src/main/java/com/microsoft/appcenter/push/Push.java index f34cd98864..1f5c4d210a 100644 --- a/sdk/appcenter-push/src/main/java/com/microsoft/appcenter/push/Push.java +++ b/sdk/appcenter-push/src/main/java/com/microsoft/appcenter/push/Push.java @@ -13,6 +13,7 @@ import android.util.Log; import com.microsoft.appcenter.AbstractAppCenterService; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.channel.Channel; import com.microsoft.appcenter.ingestion.models.json.LogFactory; import com.microsoft.appcenter.push.ingestion.models.PushInstallationLog; @@ -224,7 +225,7 @@ private synchronized void setFirebaseAnalyticsEnabled(@NonNull Context context, private void enqueuePushInstallationLog(@NonNull String pushToken) { PushInstallationLog log = new PushInstallationLog(); log.setPushToken(pushToken); - mChannel.enqueue(log, PUSH_GROUP); + mChannel.enqueue(log, PUSH_GROUP, Flags.DEFAULT_FLAGS); } /** diff --git a/sdk/appcenter-push/src/test/java/com/microsoft/appcenter/push/PushTest.java b/sdk/appcenter-push/src/test/java/com/microsoft/appcenter/push/PushTest.java index 5b85ecdfc3..1c754718b5 100644 --- a/sdk/appcenter-push/src/test/java/com/microsoft/appcenter/push/PushTest.java +++ b/sdk/appcenter-push/src/test/java/com/microsoft/appcenter/push/PushTest.java @@ -41,6 +41,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import static com.microsoft.appcenter.Flags.DEFAULT_FLAGS; import static com.microsoft.appcenter.utils.PrefStorageConstants.KEY_ENABLED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -50,6 +51,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; @@ -199,7 +201,7 @@ public void setEnabled() throws InterruptedException { verify(channel).removeGroup(eq(push.getGroupName())); assertTrue(Push.isEnabled().get()); verify(mFirebaseInstanceId).getToken(); - verify(channel).enqueue(any(PushInstallationLog.class), eq(push.getGroupName())); + verify(channel).enqueue(any(PushInstallationLog.class), eq(push.getGroupName()), eq(DEFAULT_FLAGS)); verify(mPackageManager).setComponentEnabledSetting(any(ComponentName.class), eq(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT), eq(PackageManager.DONT_KILL_APP)); @@ -209,7 +211,7 @@ public void setEnabled() throws InterruptedException { /* Verify behavior happened only once. */ verify(mFirebaseInstanceId).getToken(); - verify(channel).enqueue(any(PushInstallationLog.class), eq(push.getGroupName())); + verify(channel).enqueue(any(PushInstallationLog.class), eq(push.getGroupName()), eq(DEFAULT_FLAGS)); /* Disable. */ Push.setEnabled(false).get(); @@ -233,7 +235,7 @@ public void accept(Void aVoid) { /* Verify behavior happened only once. */ verify(mFirebaseInstanceId).getToken(); - verify(channel).enqueue(any(PushInstallationLog.class), eq(push.getGroupName())); + verify(channel).enqueue(any(PushInstallationLog.class), eq(push.getGroupName()), eq(DEFAULT_FLAGS)); /* Make sure no logging when posting check activity intent commands. */ Activity activity = mock(Activity.class); @@ -285,11 +287,11 @@ public void nullTokenOnStartThenRefresh() { start(push, channel); assertTrue(Push.isEnabled().get()); verify(mFirebaseInstanceId).getToken(); - verify(channel, never()).enqueue(any(PushInstallationLog.class), eq(push.getGroupName())); + verify(channel, never()).enqueue(any(PushInstallationLog.class), eq(push.getGroupName()), anyInt()); /* Refresh. */ push.onTokenRefresh(testToken); - verify(channel).enqueue(any(PushInstallationLog.class), eq(push.getGroupName())); + verify(channel).enqueue(any(PushInstallationLog.class), eq(push.getGroupName()), eq(DEFAULT_FLAGS)); /* Only once. */ verify(mFirebaseInstanceId).getToken(); diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java index eafb9008ce..53348b401a 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java @@ -900,7 +900,7 @@ private void sendStartServiceLog() { mStartedServicesNamesToLog.clear(); StartServiceLog startServiceLog = new StartServiceLog(); startServiceLog.setServices(allServiceNamesToStart); - mChannel.enqueue(startServiceLog, CORE_GROUP); + mChannel.enqueue(startServiceLog, CORE_GROUP, Flags.DEFAULT_FLAGS); } } @@ -934,7 +934,7 @@ private void configureAndStartServices(Application application, String appSecret private void queueCustomProperties(@NonNull Map properties) { CustomPropertiesLog customPropertiesLog = new CustomPropertiesLog(); customPropertiesLog.setProperties(properties); - mChannel.enqueue(customPropertiesLog, CORE_GROUP); + mChannel.enqueue(customPropertiesLog, CORE_GROUP, Flags.DEFAULT_FLAGS); } /** diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/Channel.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/Channel.java index 00a06354e3..df8fbc641a 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/Channel.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/Channel.java @@ -1,10 +1,14 @@ package com.microsoft.appcenter.channel; +import android.support.annotation.IntRange; import android.support.annotation.NonNull; import com.microsoft.appcenter.ingestion.Ingestion; import com.microsoft.appcenter.ingestion.models.Log; +import static com.microsoft.appcenter.Flags.PERSISTENCE_CRITICAL; +import static com.microsoft.appcenter.Flags.PERSISTENCE_NORMAL; + /** * The interface for Channel. */ @@ -62,12 +66,15 @@ public interface Channel { void resumeGroup(String groupName, String targetToken); /** - * Add Log to queue to be persisted and sent. + * Add log to queue to be persisted and sent. * - * @param log the Log to be enqueued. + * @param log the log to be enqueued. * @param groupName the group to use. + * @param flags the flags for this log. */ - void enqueue(@NonNull Log log, @NonNull String groupName); + void enqueue(@NonNull Log log, + @NonNull String groupName, + @IntRange(from = PERSISTENCE_NORMAL, to = PERSISTENCE_CRITICAL) int flags); /** * Check whether channel is enabled or disabled. diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java index f832b2dbd7..8e0d30024e 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java @@ -601,17 +601,11 @@ private synchronized void handleSendingFailure(@NonNull GroupState groupState, @ } } - /** - * Actual implementation of enqueue logic. Will increase counters, triggers of batching logic. - * - * @param log the Log to be enqueued - * @param groupName the queue to use - */ @Override - public synchronized void enqueue(@NonNull Log log, @NonNull final String groupName) { + public synchronized void enqueue(@NonNull Log log, @NonNull final String groupName, int flags) { /* Check group name is registered. */ - final GroupState groupState = mGroupStates.get(groupName); + GroupState groupState = mGroupStates.get(groupName); if (groupState == null) { AppCenterLog.error(LOG_TAG, "Invalid group name:" + groupName); return; diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/OneCollectorChannelListener.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/OneCollectorChannelListener.java index 7c5936e995..5599397bb5 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/OneCollectorChannelListener.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/OneCollectorChannelListener.java @@ -4,6 +4,7 @@ import android.support.annotation.NonNull; import android.support.annotation.VisibleForTesting; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.ingestion.Ingestion; import com.microsoft.appcenter.ingestion.OneCollectorIngestion; import com.microsoft.appcenter.ingestion.models.Log; @@ -140,7 +141,7 @@ public void onPreparedLog(@NonNull Log log, @NonNull String groupName) { /* Enqueue logs to one collector group. */ String oneCollectorGroupName = getOneCollectorGroupName(groupName); for (CommonSchemaLog commonSchemaLog : commonSchemaLogs) { - mChannel.enqueue(commonSchemaLog, oneCollectorGroupName); + mChannel.enqueue(commonSchemaLog, oneCollectorGroupName, Flags.DEFAULT_FLAGS); } } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java index 006a0881e8..3f10ac42c6 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java @@ -13,11 +13,13 @@ import static com.microsoft.appcenter.AppCenter.CORE_GROUP; import static com.microsoft.appcenter.AppCenter.LOG_TAG; import static com.microsoft.appcenter.AppCenter.PAIR_DELIMITER; +import static com.microsoft.appcenter.Flags.DEFAULT_FLAGS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isNull; @@ -87,7 +89,7 @@ public void startFromLibraryThenFromApp() { verify(DummyService.getInstance()).getLogFactories(); verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), isNull(String.class), eq(false)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); - verify(mChannel, never()).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel, never()).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), anyInt()); verify(mChannel, never()).setAppSecret(anyString()); /* Libraries have to use the default max storage size. */ @@ -121,7 +123,7 @@ public void startFromLibraryThenFromApp() { assertTrue(AnotherDummyService.isEnabled().get()); /* Verify start service log is sent. */ - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services = new ArrayList<>(); services.add(DummyService.getInstance().getServiceName()); services.add(AnotherDummyService.getInstance().getServiceName()); @@ -143,7 +145,7 @@ public void startFromLibraryThenFromAppWithTargetToken() { verify(DummyService.getInstance()).getLogFactories(); verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), isNull(String.class), eq(false)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); - verify(mChannel, never()).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel, never()).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), anyInt()); verify(mChannel, never()).setAppSecret(anyString()); /* Start two services from app. */ @@ -167,7 +169,7 @@ public void startFromLibraryThenFromAppWithTargetToken() { verify(mApplication, never()).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); /* Verify start service log is sent. */ - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services = new ArrayList<>(); services.add(DummyService.getInstance().getServiceName()); verify(mStartServiceLog).setServices(eq(services)); @@ -189,7 +191,7 @@ public void startFromLibraryThenFromAppWithBothSecrets() { verify(DummyService.getInstance()).getLogFactories(); verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), isNull(String.class), eq(false)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); - verify(mChannel, never()).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel, never()).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), anyInt()); verify(mChannel, never()).setAppSecret(anyString()); /* Start two services from app with app secret and transmission target. */ @@ -214,7 +216,7 @@ public void startFromLibraryThenFromAppWithBothSecrets() { verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); /* Verify start service log is sent. */ - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services = new ArrayList<>(); services.add(DummyService.getInstance().getServiceName()); services.add(AnotherDummyService.getInstance().getServiceName()); @@ -246,7 +248,7 @@ public void startFromAppThenFromLibrary() { verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); /* Verify start service log is sent. */ - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services = new ArrayList<>(); services.add(DummyService.getInstance().getServiceName()); services.add(AnotherDummyService.getInstance().getServiceName()); @@ -270,7 +272,7 @@ public void startFromAppThenFromLibrary() { verify(AnotherDummyService.getInstance()).getLogFactories(); verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); services = new ArrayList<>(); services.add(DummyService.getInstance().getServiceName()); services.add(AnotherDummyService.getInstance().getServiceName()); @@ -302,7 +304,7 @@ public void startWithTargetTokenThenFromLibrary() { verify(mApplication, never()).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); /* Verify start service log is sent with only first service. */ - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services = new ArrayList<>(); services.add(DummyService.getInstance().getServiceName()); verify(mStartServiceLog).setServices(eq(services)); @@ -321,7 +323,7 @@ public void startWithTargetTokenThenFromLibrary() { verify(AnotherDummyService.getInstance(), never()).getLogFactories(); verify(AnotherDummyService.getInstance(), never()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); verify(mApplication, never()).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); services = new ArrayList<>(); services.add(DummyService.getInstance().getServiceName()); verify(mStartServiceLog).setServices(eq(services)); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java index 73fc954b4e..37576a05d8 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java @@ -28,6 +28,7 @@ import static com.microsoft.appcenter.AppCenter.KEY_VALUE_DELIMITER; import static com.microsoft.appcenter.AppCenter.LOG_TAG; import static com.microsoft.appcenter.AppCenter.PAIR_DELIMITER; +import static com.microsoft.appcenter.Flags.DEFAULT_FLAGS; import static com.microsoft.appcenter.utils.PrefStorageConstants.KEY_ENABLED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -89,7 +90,7 @@ public void useDummyServiceTest() { verify(service).getLogFactories(); verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); verify(mChannel).setMaxStorageSize(AppCenter.DEFAULT_MAX_STORAGE_SIZE_IN_BYTES); List services = new ArrayList<>(); services.add(service.getServiceName()); @@ -108,19 +109,19 @@ public void useDummyServiceWhenDisablePersisted() { assertFalse(AppCenter.isEnabled().get()); assertEquals(1, AppCenter.getInstance().getServices().size()); assertTrue(appCenter.getServices().contains(service)); - verify(mChannel, never()).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel, never()).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), anyInt()); /* Start another service. */ AppCenter.start(AnotherDummyService.class); assertFalse(AppCenter.isEnabled().get()); assertEquals(2, AppCenter.getInstance().getServices().size()); assertTrue(appCenter.getServices().contains(anotherService)); - verify(mChannel, never()).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel, never()).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), anyInt()); /* Enable. */ AppCenter.setEnabled(true); assertTrue(AppCenter.isEnabled().get()); - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services = new ArrayList<>(); services.add(service.getServiceName()); services.add(anotherService.getServiceName()); @@ -141,7 +142,7 @@ public void useDummyServiceTestSplitCall() { verify(service).getLogFactories(); verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services = new ArrayList<>(); services.add(service.getServiceName()); verify(mStartServiceLog).setServices(eq(services)); @@ -160,7 +161,7 @@ public void configureAndStartTwiceTest() { verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); verify(service, never()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET + "a"), isNull(String.class), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services = new ArrayList<>(); services.add(service.getServiceName()); verify(mStartServiceLog).setServices(eq(services)); @@ -179,7 +180,7 @@ public void startTargetTokenThenStartWithAppSecretTest() { verify(service).onStarted(any(Context.class), any(Channel.class), isNull(String.class), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); verify(service, never()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services = new ArrayList<>(); services.add(service.getServiceName()); verify(mStartServiceLog).setServices(eq(services)); @@ -198,7 +199,7 @@ public void startAppSecretThenStartWithTargetTokenTest() { verify(service, never()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services = new ArrayList<>(); services.add(service.getServiceName()); verify(mStartServiceLog).setServices(eq(services)); @@ -219,7 +220,7 @@ public void configureTwiceTest() { verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); verify(service, never()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET + "a"), isNull(String.class), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services = new ArrayList<>(); services.add(service.getServiceName()); verify(mStartServiceLog).setServices(eq(services)); @@ -248,7 +249,7 @@ public void startTwoServicesTest() { verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); /* Verify start service log is sent. */ - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services = new ArrayList<>(); services.add(DummyService.getInstance().getServiceName()); services.add(AnotherDummyService.getInstance().getServiceName()); @@ -274,7 +275,7 @@ public void startTwoServicesSplit() { verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); } - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services = new ArrayList<>(); services.add(DummyService.getInstance().getServiceName()); services.add(AnotherDummyService.getInstance().getServiceName()); @@ -301,7 +302,7 @@ public void startTwoServicesSplitEvenMore() { verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); } - verify(mChannel, times(2)).enqueue(any(StartServiceLog.class), eq(CORE_GROUP)); + verify(mChannel, times(2)).enqueue(any(StartServiceLog.class), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services1 = new ArrayList<>(); services1.add(DummyService.getInstance().getServiceName()); verify(mStartServiceLog).setServices(eq(services1)); @@ -328,7 +329,7 @@ public void startTwoServicesWithSomeInvalidReferences() { verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); } - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services = new ArrayList<>(); services.add(DummyService.getInstance().getServiceName()); services.add(AnotherDummyService.getInstance().getServiceName()); @@ -355,7 +356,7 @@ public void startTwoServicesWithSomeInvalidReferencesSplit() { verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); } - verify(mChannel, times(2)).enqueue(any(StartServiceLog.class), eq(CORE_GROUP)); + verify(mChannel, times(2)).enqueue(any(StartServiceLog.class), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services1 = new ArrayList<>(); services1.add(DummyService.getInstance().getServiceName()); verify(mStartServiceLog).setServices(eq(services1)); @@ -387,7 +388,7 @@ public void startServiceTwice() { verify(service).getLogFactories(); verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); - verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); List services = new ArrayList<>(); services.add(DummyService.getInstance().getServiceName()); verify(mStartServiceLog).setServices(eq(services)); @@ -863,7 +864,7 @@ public void setCustomPropertiesTest() throws Exception { /* Call before start is forbidden. */ AppCenter.setCustomProperties(new CustomProperties().clear("test")); - verify(mChannel, never()).enqueue(eq(log), eq(CORE_GROUP)); + verify(mChannel, never()).enqueue(eq(log), eq(CORE_GROUP), anyInt()); verifyStatic(times(1)); AppCenterLog.error(eq(LOG_TAG), anyString()); @@ -872,14 +873,14 @@ public void setCustomPropertiesTest() throws Exception { /* Set null. */ AppCenter.setCustomProperties(null); - verify(mChannel, never()).enqueue(eq(log), eq(CORE_GROUP)); + verify(mChannel, never()).enqueue(eq(log), eq(CORE_GROUP), anyInt()); verifyStatic(times(2)); AppCenterLog.error(eq(LOG_TAG), anyString()); /* Set empty. */ CustomProperties empty = new CustomProperties(); AppCenter.setCustomProperties(empty); - verify(mChannel, never()).enqueue(eq(log), eq(CORE_GROUP)); + verify(mChannel, never()).enqueue(eq(log), eq(CORE_GROUP), anyInt()); verifyStatic(times(3)); AppCenterLog.error(eq(LOG_TAG), anyString()); @@ -888,7 +889,7 @@ public void setCustomPropertiesTest() throws Exception { properties.set("test", "test"); AppCenter.setCustomProperties(properties); verify(log).setProperties(eq(properties.getProperties())); - verify(mChannel).enqueue(eq(log), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(log), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); /* Call after disabled triggers an error. */ AppCenter.setEnabled(false); @@ -897,7 +898,7 @@ public void setCustomPropertiesTest() throws Exception { AppCenterLog.error(eq(LOG_TAG), anyString()); /* No more log enqueued. */ - verify(mChannel).enqueue(eq(log), eq(CORE_GROUP)); + verify(mChannel).enqueue(eq(log), eq(CORE_GROUP), eq(DEFAULT_FLAGS)); } @Test diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/ChannelLogDecorateTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/ChannelLogDecorateTest.java index bbb7c2b50c..d38ee511ee 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/ChannelLogDecorateTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/ChannelLogDecorateTest.java @@ -18,6 +18,7 @@ import java.util.Date; import java.util.UUID; +import static com.microsoft.appcenter.Flags.DEFAULT_FLAGS; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -44,7 +45,7 @@ public void checkLogAttributes() throws DeviceInfoHelper.DeviceInfoException { /* Test a log that should be decorated. */ for (int i = 0; i < 3; i++) { Log log = mock(Log.class); - channel.enqueue(log, ""); + channel.enqueue(log, "", DEFAULT_FLAGS); verify(log).setDevice(device); verify(log).setTimestamp(any(Date.class)); } @@ -57,7 +58,7 @@ public void checkLogAttributes() throws DeviceInfoHelper.DeviceInfoException { Log log2 = mock(Log.class); when(log2.getDevice()).thenReturn(device); when(log2.getTimestamp()).thenReturn(new Date(123L)); - channel.enqueue(log2, ""); + channel.enqueue(log2, "", DEFAULT_FLAGS); verify(log2, never()).setDevice(any(Device.class)); verify(log2, never()).setTimestamp(any(Date.class)); @@ -69,7 +70,7 @@ public void checkLogAttributes() throws DeviceInfoHelper.DeviceInfoException { /* Generate some logs to verify device properties have been updated. */ for (int i = 0; i < 3; i++) { Log log3 = mock(Log.class); - channel.enqueue(log3, ""); + channel.enqueue(log3, "", DEFAULT_FLAGS); verify(log3).setDevice(device2); verify(log3).setTimestamp(any(Date.class)); } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java index aeb7f058be..f52963d43b 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java @@ -51,10 +51,10 @@ public void nullAppSecretProvided() throws Persistence.PersistenceException { /* Check enqueue. */ Log log = mock(Log.class); - channel.enqueue(log, TEST_GROUP); - verify(persistence, never()).putLog(TEST_GROUP, log, Flags.PERSISTENCE_NORMAL); - channel.enqueue(mock(Log.class), "other"); - verify(persistence, never()).putLog(anyString(), any(Log.class), eq(Flags.PERSISTENCE_NORMAL)); + channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); + verify(persistence, never()).putLog(eq(TEST_GROUP), eq(log), anyInt()); + channel.enqueue(mock(Log.class), "other", Flags.DEFAULT_FLAGS); + verify(persistence, never()).putLog(anyString(), any(Log.class), anyInt()); /* Check clear. Even without app secret it works as it could be logs from previous process. */ channel.clear(TEST_GROUP); @@ -77,7 +77,7 @@ public void useAlternateIngestion() throws IOException { channel.addGroup(TEST_GROUP, 1, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, alternateIngestion, null); /* Enqueuing 1 event. */ - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); /* Verify that we have called sendAsync on the ingestion. */ verify(alternateIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -123,7 +123,7 @@ public void startWithoutAppSecret() throws Persistence.PersistenceException { verify(alternateIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* Enqueuing 1 new event for app center. */ - channel.enqueue(mock(Log.class), appCenterGroup); + channel.enqueue(mock(Log.class), appCenterGroup, Flags.DEFAULT_FLAGS); /* Not sent. */ verify(defaultIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -132,7 +132,7 @@ public void startWithoutAppSecret() throws Persistence.PersistenceException { verify(mockPersistence, never()).putLog(eq(appCenterGroup), any(Log.class), eq(Flags.PERSISTENCE_NORMAL)); /* Enqueuing 1 event from one collector group. */ - channel.enqueue(mock(Log.class), oneCollectorGroup); + channel.enqueue(mock(Log.class), oneCollectorGroup, Flags.DEFAULT_FLAGS); /* Verify that we have called sendAsync on the alternate ingestion a second time. */ verify(alternateIngestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -140,7 +140,7 @@ public void startWithoutAppSecret() throws Persistence.PersistenceException { /* Verify that we can now send logs to app center after we have set app secret. */ channel.setAppSecret("testAppSecret"); - channel.enqueue(mock(Log.class), appCenterGroup); + channel.enqueue(mock(Log.class), appCenterGroup, Flags.DEFAULT_FLAGS); verify(defaultIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java index 2d7cd41fbb..314c36dd02 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java @@ -54,7 +54,7 @@ public void logCallbacks() { /* Check enqueue. */ Log log = mock(Log.class); - channel.enqueue(log, TEST_GROUP); + channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); verify(listener).onPreparingLog(log, TEST_GROUP); verify(listener).onPreparedLog(log, TEST_GROUP); verify(listener).shouldFilter(log); @@ -68,7 +68,7 @@ public void logCallbacks() { /* Check no more calls after removing listener. */ log = mock(Log.class); channel.removeListener(listener); - channel.enqueue(log, TEST_GROUP); + channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); verifyNoMoreInteractions(listener); } @@ -99,7 +99,7 @@ public void shutdown() { channel.addGroup(TEST_GROUP, 1, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, mockListener); /* Enqueuing 1 event. */ - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); verify(mockListener).onBeforeSending(notNull(Log.class)); channel.shutdown(); @@ -130,7 +130,7 @@ public void filter() throws Persistence.PersistenceException { when(listener2.shouldFilter(log)).thenReturn(true); /* When we enqueue that log. */ - channel.enqueue(log, TEST_GROUP); + channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); /* Then except the following. behaviors. */ verify(listener1).onPreparingLog(log, TEST_GROUP); @@ -148,7 +148,7 @@ public void filter() throws Persistence.PersistenceException { when(listener2.shouldFilter(log)).thenReturn(false); /* When we enqueue that log. */ - channel.enqueue(log, TEST_GROUP); + channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); /* Then except the following. behaviors. */ verify(listener1).onPreparingLog(log, TEST_GROUP); @@ -168,7 +168,7 @@ public void filter() throws Persistence.PersistenceException { when(listener2.shouldFilter(log)).thenReturn(false); /* When we enqueue that log. */ - channel.enqueue(log, TEST_GROUP); + channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); /* Then except the following. behaviors. */ verify(listener1).onPreparingLog(log, TEST_GROUP); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java index 3d05ba03ae..785195e641 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java @@ -54,7 +54,7 @@ public void pauseResumeGroup() throws Persistence.PersistenceException { /* Enqueue a log. */ for (int i = 0; i < 50; i++) { - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); } verify(mAppCenterHandler, never()).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); @@ -142,7 +142,7 @@ public void pauseResumeTargetToken() throws Persistence.PersistenceException { /* Enqueue a log. */ Log log = mock(Log.class); when(log.getTransmissionTargetTokens()).thenReturn(Collections.singleton(targetToken)); - channel.enqueue(log, TEST_GROUP); + channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); /* Verify persisted but not incrementing and checking logs. */ verify(persistence).putLog(TEST_GROUP, log, Flags.PERSISTENCE_NORMAL); @@ -157,7 +157,7 @@ public void pauseResumeTargetToken() throws Persistence.PersistenceException { /* Enqueueing a log from another transmission target works. */ Log otherLog = mock(Log.class); when(otherLog.getTransmissionTargetTokens()).thenReturn(Collections.singleton("iKey2-apiKey2")); - channel.enqueue(otherLog, TEST_GROUP); + channel.enqueue(otherLog, TEST_GROUP, Flags.DEFAULT_FLAGS); verify(ingestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); reset(ingestion); @@ -167,7 +167,7 @@ public void pauseResumeTargetToken() throws Persistence.PersistenceException { /* Sending more logs works now. */ reset(ingestion); - channel.enqueue(log, TEST_GROUP); + channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); verify(ingestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* Calling resume a second time has 0 effect. */ @@ -206,7 +206,7 @@ public void pauseGroupPauseTargetResumeGroupResumeTarget() throws Persistence.Pe /* Enqueue a log. */ Log log = mock(Log.class); when(log.getTransmissionTargetTokens()).thenReturn(Collections.singleton(targetToken)); - channel.enqueue(log, TEST_GROUP); + channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); /* Verify persisted but not incrementing and checking logs. */ verify(persistence).putLog(TEST_GROUP, log, Flags.PERSISTENCE_NORMAL); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java index fc73ea1770..5a52b3e9b3 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java @@ -5,6 +5,7 @@ import android.support.annotation.NonNull; import com.microsoft.appcenter.CancellationException; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.http.HttpException; import com.microsoft.appcenter.http.ServiceCallback; import com.microsoft.appcenter.ingestion.AppCenterIngestion; @@ -55,7 +56,7 @@ public void invalidGroup() throws Persistence.PersistenceException { /* Enqueue a log before group is registered = failure. */ Log log = mock(Log.class); - channel.enqueue(log, TEST_GROUP); + channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); verify(log, never()).setDevice(any(Device.class)); verify(log, never()).setTimestamp(any(Date.class)); verify(persistence, never()).putLog(eq(TEST_GROUP), eq(log), anyInt()); @@ -80,13 +81,13 @@ public void analyticsSuccess() throws Persistence.PersistenceException { /* Enqueuing 49 events. */ for (int i = 1; i <= 49; i++) { - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); assertEquals(i, channel.getGroupState(TEST_GROUP).mPendingLogCount); } verify(mAppCenterHandler).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); /* Enqueue another event. */ - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); verify(mAppCenterHandler).removeCallbacks(any(Runnable.class)); /* The counter should be 0 as we reset the counter after reaching the limit of 50. */ @@ -114,7 +115,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { AtomicReference runnable = catchPostRunnable(); /* Schedule only 1 log after that. */ - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); assertEquals(1, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(mockPersistence, times(51)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -132,8 +133,8 @@ public void analyticsSuccess() throws Persistence.PersistenceException { verify(mockListener, times(51)).onSuccess(any(Log.class)); /* 2 more timed logs. */ - channel.enqueue(mock(Log.class), TEST_GROUP); - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); assertEquals(2, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(mockPersistence, times(53)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); verify(mockIngestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -173,13 +174,13 @@ public void lessLogsThanExpected() { /* Enqueuing 49 events. */ for (int i = 1; i <= 49; i++) { - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); assertEquals(i, channel.getGroupState(TEST_GROUP).mPendingLogCount); } verify(mAppCenterHandler).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); /* Enqueue another event. */ - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); verify(mAppCenterHandler).removeCallbacks(any(Runnable.class)); /* Database returned less logs than we expected (40 vs 50), yet counter must be reset. */ @@ -225,7 +226,7 @@ public Object answer(InvocationOnMock invocation) { /* Enqueue enough logs to be split in N + 1 maximum requests. */ for (int i = 0; i < 200; i++) { - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); } verify(mAppCenterHandler, times(4)).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); verify(mAppCenterHandler, times(4)).removeCallbacks(any(Runnable.class)); @@ -276,7 +277,7 @@ public Object answer(InvocationOnMock invocation) { /* Enqueue enough logs to be split in N + 1 maximum requests. */ for (int i = 0; i < 100; i++) { - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); } /* Verify all logs stored, N requests sent, not log deleted yet. */ @@ -321,7 +322,7 @@ public void analyticsRecoverable() throws Persistence.PersistenceException { /* Enqueuing 50 events. */ for (int i = 0; i < 50; i++) { - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); } verify(mAppCenterHandler).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); verify(mAppCenterHandler).removeCallbacks(any(Runnable.class)); @@ -342,7 +343,7 @@ public void analyticsRecoverable() throws Persistence.PersistenceException { /* Enqueuing 20 more events. */ for (int i = 0; i < 20; i++) { - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); } /* The counter keeps being increased. */ @@ -399,7 +400,7 @@ public void analyticsFatal() throws Exception { /* Enqueuing 50 events. */ for (int i = 0; i < 50; i++) { - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); } verify(mAppCenterHandler).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); verify(mAppCenterHandler).removeCallbacks(any(Runnable.class)); @@ -421,7 +422,7 @@ public void analyticsFatal() throws Exception { /* Enqueuing 20 more events. */ for (int i = 0; i < 20; i++) { - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); } /* The counter should still be 0 as logs are discarded by channel now. */ @@ -439,7 +440,7 @@ public void analyticsFatal() throws Exception { /* Enqueuing 20 more events. */ for (int i = 0; i < 20; i++) { - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); } assertEquals(20, channel.getGroupState(TEST_GROUP).mPendingLogCount); @@ -476,8 +477,8 @@ public void errorLogSuccess() throws Persistence.PersistenceException { channel.addGroup(TEST_GROUP, 1, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, mockListener); /* Enqueuing 2 error logs. */ - channel.enqueue(mock(Log.class), TEST_GROUP); - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); /* Verify that 2 items have been persisted. */ verify(mockPersistence, times(2)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); @@ -517,7 +518,7 @@ public void errorLogRecoverable() throws Persistence.PersistenceException { /* Enqueuing n errors. */ int logNumber = 5; for (int i = 0; i < logNumber; i++) - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); /* Verify that n items have been persisted. */ verify(mockPersistence, times(logNumber)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); @@ -562,7 +563,7 @@ public void errorLogDiscarded() { DefaultChannel channel = new DefaultChannel(mock(Context.class), UUIDUtils.randomUUID().toString(), mock(Persistence.class), mock(Ingestion.class), mAppCenterHandler); channel.addGroup(TEST_GROUP, 1, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, mockListener); channel.setEnabled(false); - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); verify(mockListener).onFailure(any(Log.class), any(CancellationException.class)); } @@ -645,7 +646,7 @@ public void enqueuePersistenceFailure() throws Persistence.PersistenceException /* Verify no request is sent if Persistence fails. */ for (int i = 0; i < 50; i++) { - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); } verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); verify(mockIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -666,7 +667,7 @@ public void setEnabled() throws IOException { Channel.Listener listener = spy(new AbstractChannelListener()); channel.addListener(listener); channel.addGroup(TEST_GROUP, 50, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, null); - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); verify(mAppCenterHandler).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); /* Disable before timer is triggered. */ @@ -680,7 +681,7 @@ public void setEnabled() throws IOException { /* Enable and send a new log. */ AtomicReference runnable = catchPostRunnable(); channel.setEnabled(true); - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); assertNotNull(runnable.get()); runnable.get().run(); verify(ingestion).reopen(); @@ -706,7 +707,7 @@ public Void answer(InvocationOnMock invocation) { } }); channel.addGroup(TEST_GROUP, 1, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, null); - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); verify(mAppCenterHandler, never()).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); } @@ -785,7 +786,7 @@ public void packageManagerIsBroken() throws Persistence.PersistenceException, De /* Enqueue a log: listener is called before but then attaching device properties fails before saving the log. */ Log log = mock(Log.class); - channel.enqueue(log, TEST_GROUP); + channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); verify(listener).onPreparingLog(log, TEST_GROUP); verify(listener, never()).shouldFilter(log); verify(persistence, never()).putLog(eq(TEST_GROUP), eq(log), anyInt()); @@ -831,7 +832,7 @@ public void invokeCallbacksAfterSuspendFatal() { channel.addGroup(TEST_GROUP + "2", 1, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, null); /* Enqueuing 1 event. */ - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); /* Verify callbacks invoked (1 + DefaultChannel.CLEAR_BATCH_SIZE + DefaultChannel.CLEAR_BATCH_SIZE - 1) times. */ verify(mockListener, times(DefaultChannel.CLEAR_BATCH_SIZE * 2)).onBeforeSending(any(Log.class)); @@ -861,8 +862,8 @@ public void invokeCallbacksAfterSuspendFatalNoListener() { channel.addGroup(TEST_GROUP + "2", 1, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, mockListener); /* Enqueuing 2 events. */ - channel.enqueue(mock(Log.class), TEST_GROUP); - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); /* Verify callbacks not invoked. */ verify(mockListener, never()).onBeforeSending(any(Log.class)); @@ -887,7 +888,7 @@ public void invokeCallbacksAfterSuspendRecoverable() { channel.addGroup(TEST_GROUP + "2", 1, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, null); /* Enqueuing 1 event. */ - channel.enqueue(mock(Log.class), TEST_GROUP); + channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); /* Verify callbacks invoked only for the first log. */ verify(mockListener).onBeforeSending(any(Log.class)); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java index b97a42916e..224c32b87b 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java @@ -19,6 +19,7 @@ import java.util.HashSet; import java.util.UUID; +import static com.microsoft.appcenter.Flags.DEFAULT_FLAGS; import static com.microsoft.appcenter.channel.AbstractDefaultChannelTest.TEST_GROUP; import static com.microsoft.appcenter.channel.OneCollectorChannelListener.ONE_COLLECTOR_GROUP_NAME_SUFFIX; import static com.microsoft.appcenter.channel.OneCollectorChannelListener.ONE_COLLECTOR_TRIGGER_COUNT; @@ -30,6 +31,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; @@ -131,11 +133,11 @@ public void enqueueConvertedLogs() { assertEquals(installId, log2.getExt().getSdk().getInstallId()); /* Verify enqueue. */ - verify(channel).enqueue(log1, TEST_GROUP + ONE_COLLECTOR_GROUP_NAME_SUFFIX); - verify(channel).enqueue(log2, TEST_GROUP + ONE_COLLECTOR_GROUP_NAME_SUFFIX); + verify(channel).enqueue(log1, TEST_GROUP + ONE_COLLECTOR_GROUP_NAME_SUFFIX, DEFAULT_FLAGS); + verify(channel).enqueue(log2, TEST_GROUP + ONE_COLLECTOR_GROUP_NAME_SUFFIX, DEFAULT_FLAGS); /* We simulated that we see on prepared log on the enqueued log, verify no more enqueuing. */ - verify(channel, times(2)).enqueue(any(Log.class), anyString()); + verify(channel, times(2)).enqueue(any(Log.class), anyString(), eq(DEFAULT_FLAGS)); /* Mock log with another key to see new seq/epoch. */ when(originalLog.getTransmissionTargetTokens()).thenReturn(new HashSet<>(Collections.singletonList("t2"))); @@ -187,7 +189,7 @@ public void validateCommonSchemaLogs() { verify(logSerializer).toCommonSchemaLog(any(Log.class)); /* Verify no enqueuing as the log was invalid. */ - verify(channel, never()).enqueue(any(Log.class), anyString()); + verify(channel, never()).enqueue(any(Log.class), anyString(), anyInt()); } @Test @@ -205,7 +207,7 @@ public void dontConvertCommonSchemaLogs() { verify(logSerializer, never()).toCommonSchemaLog(any(Log.class)); /* Verify no enqueuing. */ - verify(channel, never()).enqueue(any(Log.class), anyString()); + verify(channel, never()).enqueue(any(Log.class), anyString(), anyInt()); } @Test From 8410ae0b10b2fcd541cecbbd000f03b15616004d Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Tue, 30 Oct 2018 15:21:10 -0700 Subject: [PATCH 24/68] Forward flags to one collector channel listener --- .../appcenter/channel/AbstractChannelListener.java | 2 +- .../com/microsoft/appcenter/channel/Channel.java | 3 ++- .../microsoft/appcenter/channel/DefaultChannel.java | 2 +- .../channel/OneCollectorChannelListener.java | 11 +++++------ .../channel/DefaultChannelOtherOperationsTest.java | 2 +- .../channel/OneCollectorChannelListenerTest.java | 12 ++++++------ 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/AbstractChannelListener.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/AbstractChannelListener.java index 2fdd5c5f78..24ab8b29c6 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/AbstractChannelListener.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/AbstractChannelListener.java @@ -23,7 +23,7 @@ public void onPreparingLog(@NonNull Log log, @NonNull String groupName) { } @Override - public void onPreparedLog(@NonNull Log log, @NonNull String groupName) { + public void onPreparedLog(@NonNull Log log, @NonNull String groupName, int flags) { } @Override diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/Channel.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/Channel.java index df8fbc641a..8d80d1f3cd 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/Channel.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/Channel.java @@ -165,8 +165,9 @@ interface Listener { * * @param log prepared log. * @param groupName group of the log. + * @param flags log flags. */ - void onPreparedLog(@NonNull Log log, @NonNull String groupName); + void onPreparedLog(@NonNull Log log, @NonNull String groupName, int flags); /** * Called after a log has been fully prepared and properties are now final. diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java index 8e0d30024e..caeb24203b 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java @@ -650,7 +650,7 @@ public synchronized void enqueue(@NonNull Log log, @NonNull final String groupNa /* Notify listeners that log is prepared and is in a final state. */ for (Listener listener : mListeners) { - listener.onPreparedLog(log, groupName); + listener.onPreparedLog(log, groupName, flags); } /* Call listeners so that they can filter the log. */ diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/OneCollectorChannelListener.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/OneCollectorChannelListener.java index 5599397bb5..c21e0c671d 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/OneCollectorChannelListener.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/OneCollectorChannelListener.java @@ -4,7 +4,6 @@ import android.support.annotation.NonNull; import android.support.annotation.VisibleForTesting; -import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.ingestion.Ingestion; import com.microsoft.appcenter.ingestion.OneCollectorIngestion; import com.microsoft.appcenter.ingestion.models.Log; @@ -78,10 +77,10 @@ public class OneCollectorChannelListener extends AbstractChannelListener { /** * Init with channel. * - * @param context context. - * @param channel channel. + * @param context context. + * @param channel channel. * @param logSerializer log serializer. - * @param installId installId. + * @param installId installId. */ public OneCollectorChannelListener(@NonNull Context context, @NonNull Channel channel, @NonNull LogSerializer logSerializer, @NonNull UUID installId) { mChannel = channel; @@ -109,7 +108,7 @@ public void onGroupRemoved(@NonNull String groupName) { } @Override - public void onPreparedLog(@NonNull Log log, @NonNull String groupName) { + public void onPreparedLog(@NonNull Log log, @NonNull String groupName, int flags) { /* Nothing to do on common schema log prepared. */ if (!isOneCollectorCompatible(log)) { @@ -141,7 +140,7 @@ public void onPreparedLog(@NonNull Log log, @NonNull String groupName) { /* Enqueue logs to one collector group. */ String oneCollectorGroupName = getOneCollectorGroupName(groupName); for (CommonSchemaLog commonSchemaLog : commonSchemaLogs) { - mChannel.enqueue(commonSchemaLog, oneCollectorGroupName, Flags.DEFAULT_FLAGS); + mChannel.enqueue(commonSchemaLog, oneCollectorGroupName, flags); } } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java index 314c36dd02..fd54adad4c 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java @@ -56,7 +56,7 @@ public void logCallbacks() { Log log = mock(Log.class); channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); verify(listener).onPreparingLog(log, TEST_GROUP); - verify(listener).onPreparedLog(log, TEST_GROUP); + verify(listener).onPreparedLog(log, TEST_GROUP, Flags.DEFAULT_FLAGS); verify(listener).shouldFilter(log); verifyNoMoreInteractions(listener); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java index 224c32b87b..6d7c1bdca3 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java @@ -113,8 +113,8 @@ public void enqueueConvertedLogs() { /* Init listener. */ UUID installId = UUIDUtils.randomUUID(); OneCollectorChannelListener listener = new OneCollectorChannelListener(mock(Context.class), channel, logSerializer, installId); - listener.onPreparedLog(originalLog, TEST_GROUP); - listener.onPreparedLog(mock(CommonSchemaLog.class), TEST_GROUP + ONE_COLLECTOR_GROUP_NAME_SUFFIX); + listener.onPreparedLog(originalLog, TEST_GROUP, DEFAULT_FLAGS); + listener.onPreparedLog(mock(CommonSchemaLog.class), TEST_GROUP + ONE_COLLECTOR_GROUP_NAME_SUFFIX, DEFAULT_FLAGS); /* Verify conversion. */ verify(logSerializer).toCommonSchemaLog(originalLog); @@ -147,7 +147,7 @@ public void enqueueConvertedLogs() { when(log3.getExt()).thenReturn(ext3); when(log3.getIKey()).thenReturn("t2"); when(logSerializer.toCommonSchemaLog(any(Log.class))).thenReturn(Collections.singletonList(log3)); - listener.onPreparedLog(originalLog, TEST_GROUP); + listener.onPreparedLog(originalLog, TEST_GROUP, DEFAULT_FLAGS); assertEquals((Long) 1L, log3.getExt().getSdk().getSeq()); assertNotNull(log3.getExt().getSdk().getEpoch()); assertNotEquals(log1.getExt().getSdk().getEpoch(), log3.getExt().getSdk().getEpoch()); @@ -163,7 +163,7 @@ public void enqueueConvertedLogs() { when(log4.getExt()).thenReturn(ext4); when(log4.getIKey()).thenReturn("t2"); when(logSerializer.toCommonSchemaLog(any(Log.class))).thenReturn(Collections.singletonList(log4)); - listener.onPreparedLog(originalLog, TEST_GROUP); + listener.onPreparedLog(originalLog, TEST_GROUP, DEFAULT_FLAGS); /* Verify reset of epoch/seq. */ assertEquals((Long) 1L, log4.getExt().getSdk().getSeq()); @@ -183,7 +183,7 @@ public void validateCommonSchemaLogs() { /* Init listener. */ OneCollectorChannelListener listener = new OneCollectorChannelListener(mock(Context.class), channel, logSerializer, UUIDUtils.randomUUID()); - listener.onPreparedLog(log, TEST_GROUP); + listener.onPreparedLog(log, TEST_GROUP, DEFAULT_FLAGS); /* Verify conversion attempted. */ verify(logSerializer).toCommonSchemaLog(any(Log.class)); @@ -201,7 +201,7 @@ public void dontConvertCommonSchemaLogs() { /* Init listener. */ OneCollectorChannelListener listener = new OneCollectorChannelListener(mock(Context.class), channel, logSerializer, UUIDUtils.randomUUID()); - listener.onPreparedLog(mock(CommonSchemaLog.class), TEST_GROUP); + listener.onPreparedLog(mock(CommonSchemaLog.class), TEST_GROUP, DEFAULT_FLAGS); /* Verify no conversion. */ verify(logSerializer, never()).toCommonSchemaLog(any(Log.class)); From 429bd60fb5712e7d704cfef8eaef16d30b1e3509 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Tue, 30 Oct 2018 16:23:18 -0700 Subject: [PATCH 25/68] Handle flags in enqueueLog and fix persistence flags processing --- .../DatabasePersistenceAndroidTest.java | 95 ++++++++++++------- .../persistence/PersistenceAndroidTest.java | 2 +- .../java/com/microsoft/appcenter/Flags.java | 24 ++++- .../appcenter/channel/DefaultChannel.java | 4 +- .../persistence/DatabasePersistence.java | 4 +- .../appcenter/persistence/Persistence.java | 10 +- .../com/microsoft/appcenter/FlagsTest.java | 79 +++++++++++++++ .../DefaultChannelAlternateIngestionTest.java | 6 +- .../DefaultChannelOtherOperationsTest.java | 6 +- .../DefaultChannelPauseResumeTest.java | 6 +- .../appcenter/channel/DefaultChannelTest.java | 55 ++++++++--- .../persistence/DatabasePersistenceTest.java | 2 +- 12 files changed, 224 insertions(+), 69 deletions(-) create mode 100644 sdk/appcenter/src/test/java/com/microsoft/appcenter/FlagsTest.java diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index 8e2c1d86e3..4768129bfa 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -14,6 +14,7 @@ import com.microsoft.appcenter.AndroidTestUtils; import com.microsoft.appcenter.AppCenter; import com.microsoft.appcenter.Constants; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.ingestion.models.Log; import com.microsoft.appcenter.ingestion.models.LogWithProperties; import com.microsoft.appcenter.ingestion.models.json.DefaultLogSerializer; @@ -50,6 +51,7 @@ import static com.microsoft.appcenter.Flags.PERSISTENCE_CRITICAL; import static com.microsoft.appcenter.Flags.PERSISTENCE_NORMAL; import static com.microsoft.appcenter.ingestion.models.json.MockLog.MOCK_LOG_TYPE; +import static com.microsoft.appcenter.persistence.DatabasePersistence.COLUMN_PRIORITY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -106,8 +108,7 @@ private ContentValues getContentValues(DatabasePersistence persistence, String g return values; } - @Test - public void putLog() throws PersistenceException { + private void putLog(int inputFlags, Integer persistedPriorityFlag) throws PersistenceException { /* Initialize database persistence. */ DatabasePersistence persistence = new DatabasePersistence(sContext); @@ -123,7 +124,7 @@ public void putLog() throws PersistenceException { /* Generate a log and persist. */ Log log = AndroidTestUtils.generateMockLog(); - persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); + persistence.putLog(log, "test-p1", inputFlags); /* Count logs. */ assertEquals(1, persistence.countLogs("test-p1")); @@ -134,11 +135,35 @@ public void putLog() throws PersistenceException { assertEquals(1, outputLogs.size()); assertEquals(log, outputLogs.get(0)); assertEquals(1, persistence.countLogs("test-p1")); + + /* Check priority flag. */ + ContentValues contentValues = getContentValues(persistence, "test-p1"); + assertEquals(persistedPriorityFlag, contentValues.getAsInteger(COLUMN_PRIORITY)); } finally { persistence.close(); } } + @Test + public void putLogDefaultFlags() throws PersistenceException { + putLog(Flags.DEFAULT_FLAGS, Flags.PERSISTENCE_NORMAL); + } + + @Test + public void putLogNormal() throws PersistenceException { + putLog(Flags.PERSISTENCE_NORMAL, Flags.PERSISTENCE_NORMAL); + } + + @Test + public void putLogCritical() throws PersistenceException { + putLog(Flags.PERSISTENCE_CRITICAL, Flags.PERSISTENCE_CRITICAL); + } + + @Test + public void putLogCriticalPlusOtherFlags() throws PersistenceException { + putLog(Flags.PERSISTENCE_CRITICAL | 0x0300, Flags.PERSISTENCE_CRITICAL); + } + @Test public void putLargeLogAndDeleteAll() throws PersistenceException { @@ -164,7 +189,7 @@ public void putLargeLogAndDeleteAll() throws PersistenceException { Map properties = new HashMap<>(); properties.put("key", largeValue.toString()); log.setProperties(properties); - long id = persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); + long id = persistence.putLog(log, "test-p1", PERSISTENCE_NORMAL); /* Count logs. */ assertEquals(1, persistence.countLogs("test-p1")); @@ -222,7 +247,7 @@ public void putLargeLogFails() { Map properties = new HashMap<>(); properties.put("key", largeValue.toString()); log.setProperties(properties); - persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); + persistence.putLog(log, "test-p1", PERSISTENCE_NORMAL); fail("putLog was expected to fail"); } catch (Persistence.PersistenceException e) { assertTrue(e.getCause() instanceof IOException); @@ -262,7 +287,7 @@ public void putLargeLogFailsToRead() throws PersistenceException { Map properties = new HashMap<>(); properties.put("key", largeValue.toString()); log.setProperties(properties); - long id = persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); + long id = persistence.putLog(log, "test-p1", PERSISTENCE_NORMAL); assertEquals(1, persistence.countLogs("test-p1")); /* Verify large file. */ @@ -317,7 +342,7 @@ public void putLargeLogNotSupportedOnCommonSchema() throws JSONException { /* Persisting that log should fail. */ try { - persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); + persistence.putLog(log, "test-p1", PERSISTENCE_NORMAL); fail("Inserting large common schema log is not supposed to work"); } catch (PersistenceException e) { @@ -345,7 +370,7 @@ public void putTooManyLogs() throws PersistenceException { /* Generate some logs that will be evicted. */ for (int i = 0; i < 10; i++) { - persistence.putLog("test-p1", AndroidTestUtils.generateMockLog(), PERSISTENCE_NORMAL); + persistence.putLog(AndroidTestUtils.generateMockLog(), "test-p1", PERSISTENCE_NORMAL); } /* @@ -355,7 +380,7 @@ public void putTooManyLogs() throws PersistenceException { List expectedLogs = new ArrayList<>(); for (int i = 0; i < 6; i++) { MockLog log = AndroidTestUtils.generateMockLog(); - persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); + persistence.putLog(log, "test-p1", PERSISTENCE_NORMAL); expectedLogs.add(log); } @@ -387,7 +412,7 @@ public void putLogLargerThanMaxSizeClearsEverything() throws PersistenceExceptio /* Generate some logs that will be evicted. */ int someLogCount = 3; for (int i = 0; i < someLogCount; i++) { - persistence.putLog("test-p1", AndroidTestUtils.generateMockLog(), PERSISTENCE_NORMAL); + persistence.putLog(AndroidTestUtils.generateMockLog(), "test-p1", PERSISTENCE_NORMAL); } assertEquals(someLogCount, persistence.countLogs("test-p1")); @@ -405,7 +430,7 @@ public void putLogLargerThanMaxSizeClearsEverything() throws PersistenceExceptio properties.put("key", largeValue.toString()); log.setProperties(properties); try { - persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); + persistence.putLog(log, "test-p1", PERSISTENCE_NORMAL); fail("Expected persistence exception"); } catch (PersistenceException ignore) { } @@ -433,7 +458,7 @@ public void putLogException() throws PersistenceException, JSONException { /* Generate a log and persist. */ Log log = AndroidTestUtils.generateMockLog(); - persistence.putLog("test-p1", log, PERSISTENCE_NORMAL); + persistence.putLog(log, "test-p1", PERSISTENCE_NORMAL); } finally { persistence.close(); } @@ -456,10 +481,10 @@ public void deleteLogs() throws PersistenceException { Log log2 = AndroidTestUtils.generateMockLog(); Log log3 = AndroidTestUtils.generateMockLog(); Log log4 = AndroidTestUtils.generateMockLog(); - persistence.putLog("test-p1", log1, PERSISTENCE_NORMAL); - persistence.putLog("test-p1", log2, PERSISTENCE_NORMAL); - persistence.putLog("test-p2", log3, PERSISTENCE_NORMAL); - persistence.putLog("test-p3", log4, PERSISTENCE_NORMAL); + persistence.putLog(log1, "test-p1", PERSISTENCE_NORMAL); + persistence.putLog(log2, "test-p1", PERSISTENCE_NORMAL); + persistence.putLog(log3, "test-p2", PERSISTENCE_NORMAL); + persistence.putLog(log4, "test-p3", PERSISTENCE_NORMAL); assertEquals(2, persistence.countLogs("test-p1")); assertEquals(1, persistence.countLogs("test-p2")); assertEquals(1, persistence.countLogs("test-p3")); @@ -550,10 +575,10 @@ public void deleteLogsForGroup() throws PersistenceException { Log log2 = AndroidTestUtils.generateMockLog(); Log log3 = AndroidTestUtils.generateMockLog(); Log log4 = AndroidTestUtils.generateMockLog(); - persistence.putLog("test-p1", log1, PERSISTENCE_NORMAL); - persistence.putLog("test-p1", log2, PERSISTENCE_NORMAL); - persistence.putLog("test-p2", log3, PERSISTENCE_NORMAL); - persistence.putLog("test-p3", log4, PERSISTENCE_NORMAL); + persistence.putLog(log1, "test-p1", PERSISTENCE_NORMAL); + persistence.putLog(log2, "test-p1", PERSISTENCE_NORMAL); + persistence.putLog(log3, "test-p2", PERSISTENCE_NORMAL); + persistence.putLog(log4, "test-p3", PERSISTENCE_NORMAL); /* Get a log from persistence. */ List outputLogs = new ArrayList<>(); @@ -616,7 +641,7 @@ public void getLogs() throws PersistenceException { Log[] logs = new Log[numberOfLogs]; for (int i = 0; i < logs.length; i++) { logs[i] = AndroidTestUtils.generateMockLog(); - persistence.putLog("test", logs[i], PERSISTENCE_NORMAL); + persistence.putLog(logs[i], "test", PERSISTENCE_NORMAL); } /* Get. */ @@ -732,7 +757,7 @@ private void generateCsLogsWithIKey(DatabasePersistence persistence, String iKey log.setTimestamp(new Date()); log.setIKey(iKey); log.addTransmissionTarget(iKey + "-token"); - persistence.putLog("test", log, PERSISTENCE_NORMAL); + persistence.putLog(log, "test", PERSISTENCE_NORMAL); } } @@ -767,7 +792,7 @@ public void getLogsException() throws PersistenceException, JSONException { /* Put. */ for (Log log : logs) - persistence.putLog("test", log, PERSISTENCE_NORMAL); + persistence.putLog(log, "test", PERSISTENCE_NORMAL); /* Get. */ List outputLogs = new ArrayList<>(); @@ -787,7 +812,7 @@ public void upgradeFromVersion1to4() throws PersistenceException, JSONException oldSchema.remove(DatabasePersistence.COLUMN_TARGET_TOKEN); oldSchema.remove(DatabasePersistence.COLUMN_DATA_TYPE); oldSchema.remove(DatabasePersistence.COLUMN_TARGET_KEY); - oldSchema.remove(DatabasePersistence.COLUMN_PRIORITY); + oldSchema.remove(COLUMN_PRIORITY); DatabaseManager databaseManager = new DatabaseManager(sContext, DatabasePersistence.DATABASE, DatabasePersistence.TABLE, 1, oldSchema, new DatabaseManager.Listener() { @Override @@ -836,10 +861,10 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /* Check priority migration. */ ContentValues values = getContentValues(persistence, "test"); - assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); + assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(COLUMN_PRIORITY)); /* Put new data with token. */ - persistence.putLog("test/one", commonSchemaLog, PERSISTENCE_NORMAL); + persistence.putLog(commonSchemaLog, "test/one", PERSISTENCE_NORMAL); } finally { persistence.close(); } @@ -868,7 +893,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { assertEquals(commonSchemaLog.getIKey(), "o:" + targetKey); /* Verify priority stored too. */ - assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); + assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(COLUMN_PRIORITY)); } finally { persistence.close(); } @@ -880,7 +905,7 @@ public void upgradeFromVersion2to4() throws PersistenceException, JSONException /* Initialize database persistence with old schema. */ ContentValues oldSchema = new ContentValues(DatabasePersistence.SCHEMA); oldSchema.remove(DatabasePersistence.COLUMN_TARGET_KEY); - oldSchema.remove(DatabasePersistence.COLUMN_PRIORITY); + oldSchema.remove(COLUMN_PRIORITY); DatabaseManager databaseManager = new DatabaseManager(sContext, DatabasePersistence.DATABASE, DatabasePersistence.TABLE, DatabasePersistence.VERSION_TYPE_API_KEY, oldSchema, new DatabaseManager.Listener() { @Override @@ -930,10 +955,10 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /* Check priority migration. */ ContentValues values = getContentValues(persistence, "test"); - assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); + assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(COLUMN_PRIORITY)); /* Put new data with token. */ - persistence.putLog("test/one", commonSchemaLog, PERSISTENCE_NORMAL); + persistence.putLog(commonSchemaLog, "test/one", PERSISTENCE_NORMAL); } finally { persistence.close(); } @@ -962,7 +987,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { assertEquals(commonSchemaLog.getIKey(), "o:" + targetKey); /* Verify priority stored too. */ - assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); + assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(COLUMN_PRIORITY)); } finally { persistence.close(); } @@ -973,7 +998,7 @@ public void upgradeFromVersion3to4() throws PersistenceException, JSONException /* Initialize database persistence with old schema. */ ContentValues oldSchema = new ContentValues(DatabasePersistence.SCHEMA); - oldSchema.remove(DatabasePersistence.COLUMN_PRIORITY); + oldSchema.remove(COLUMN_PRIORITY); DatabaseManager databaseManager = new DatabaseManager(sContext, DatabasePersistence.DATABASE, DatabasePersistence.TABLE, DatabasePersistence.VERSION_TARGET_KEY, oldSchema, new DatabaseManager.Listener() { @Override @@ -1023,10 +1048,10 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /* Check priority migration. */ ContentValues values = getContentValues(persistence, "test"); - assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); + assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(COLUMN_PRIORITY)); /* Put new data with token. */ - persistence.putLog("test/one", commonSchemaLog, PERSISTENCE_CRITICAL); + persistence.putLog(commonSchemaLog, "test/one", PERSISTENCE_CRITICAL); } finally { persistence.close(); } @@ -1055,7 +1080,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { assertEquals(commonSchemaLog.getIKey(), "o:" + targetKey); /* Verify priority stored too. */ - assertEquals((Integer) PERSISTENCE_CRITICAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); + assertEquals((Integer) PERSISTENCE_CRITICAL, values.getAsInteger(COLUMN_PRIORITY)); } finally { persistence.close(); } diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java index c4f3051cf3..7e23a37238 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java @@ -51,7 +51,7 @@ public void missingLogSerializer() throws Persistence.PersistenceException { //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) try { /* Generate a log and persist. */ - persistence.putLog("exception", new MockLog(), PERSISTENCE_NORMAL); + persistence.putLog(new MockLog(), "exception", PERSISTENCE_NORMAL); } finally { /* Close. */ //noinspection ThrowFromFinallyBlock diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java index adbf5ea81d..9b64ab0671 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java @@ -1,5 +1,9 @@ package com.microsoft.appcenter; +import com.microsoft.appcenter.utils.AppCenterLog; + +import static com.microsoft.appcenter.utils.AppCenterLog.LOG_TAG; + /** * Persistence and latency flags for telemetry. */ @@ -8,7 +12,7 @@ public final class Flags { /** * Mask for persistence within flags. */ - public static final int PERSISTENCE_MASK = 0xFF; + private static final int PERSISTENCE_MASK = 0xFF; /** * An event can be lost due to low bandwidth or disk space constraints. @@ -24,4 +28,22 @@ public final class Flags { * Default combination of flags. */ public static final int DEFAULT_FLAGS = PERSISTENCE_NORMAL; + + /** + * Get persistence priority flag. + * + * @param flags all flags to extract persistence priority from. + * @param warnFallback if true and falling back from an invalid value: print a warning. + * @return persistence priority flag. + */ + public static int getPersistencePriority(int flags, boolean warnFallback) { + int persistencePriority = flags & PERSISTENCE_MASK; + if (persistencePriority != PERSISTENCE_NORMAL && persistencePriority != PERSISTENCE_CRITICAL) { + if (persistencePriority != 0 && warnFallback) { + AppCenterLog.warn(LOG_TAG, "Invalid value=" + persistencePriority + " for persistence flag, using NORMAL as a default."); + } + persistencePriority = PERSISTENCE_NORMAL; + } + return persistencePriority; + } } diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java index caeb24203b..a6ecaa33d6 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/DefaultChannel.java @@ -7,7 +7,6 @@ import android.support.annotation.VisibleForTesting; import com.microsoft.appcenter.CancellationException; -import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.http.HttpUtils; import com.microsoft.appcenter.http.ServiceCallback; import com.microsoft.appcenter.ingestion.AppCenterIngestion; @@ -672,8 +671,7 @@ public synchronized void enqueue(@NonNull Log log, @NonNull final String groupNa try { /* Persist log. */ - // TODO introduce parameter for flags and extract persistence from mask. - mPersistence.putLog(groupName, log, Flags.PERSISTENCE_NORMAL); + mPersistence.putLog(log, groupName, flags); /* Nothing more to do if the log is from a paused transmission target. */ Iterator targetKeys = log.getTransmissionTargetTokens().iterator(); diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index e05648599b..2692b27f48 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -228,7 +228,7 @@ public boolean setMaxStorageSize(long maxStorageSizeInBytes) { } @Override - public long putLog(@NonNull String group, @NonNull Log log, @IntRange(from = Flags.PERSISTENCE_NORMAL, to = Flags.PERSISTENCE_CRITICAL) int priority) throws PersistenceException { + public long putLog(@NonNull Log log, @NonNull String group, @IntRange(from = Flags.PERSISTENCE_NORMAL, to = Flags.PERSISTENCE_CRITICAL) int flags) throws PersistenceException { /* Convert log to JSON string and put in the database. */ try { @@ -249,7 +249,7 @@ public long putLog(@NonNull String group, @NonNull Log log, @IntRange(from = Fla targetKey = null; targetToken = null; } - contentValues = getContentValues(group, isLargePayload ? null : payload, targetToken, log.getType(), targetKey, priority); + contentValues = getContentValues(group, isLargePayload ? null : payload, targetToken, log.getType(), targetKey, Flags.getPersistencePriority(flags, false)); long databaseId = mDatabaseManager.put(contentValues); if (databaseId == -1) { throw new PersistenceException("Failed to store a log to the Persistence database for log type " + log.getType() + "."); diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/Persistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/Persistence.java index a5104fd913..03c7b32a65 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/Persistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/Persistence.java @@ -4,6 +4,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.ingestion.models.Log; import com.microsoft.appcenter.ingestion.models.json.LogSerializer; @@ -24,13 +25,14 @@ public abstract class Persistence implements Closeable { /** * Writes a log to the storage with the given {@code group}. * - * @param group The group of the storage for the log. - * @param log The log to be placed in the storage. - * @param priority The persistence priority. + * @param log The log to be placed in the storage. + * @param group The group of the storage for the log. + * @param flags The persistence flags. * @return Log identifier from persistence after saving. * @throws PersistenceException Exception will be thrown if Persistence cannot write a log to the storage. */ - public abstract long putLog(@NonNull String group, @NonNull Log log, int priority) throws PersistenceException; + public abstract long putLog(@NonNull Log log, @NonNull String group, + @IntRange(from = Flags.PERSISTENCE_NORMAL, to = Flags.PERSISTENCE_CRITICAL) int flags) throws PersistenceException; /** * Deletes a log with the give ID from the {@code group}. diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/FlagsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/FlagsTest.java new file mode 100644 index 0000000000..c8468b2245 --- /dev/null +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/FlagsTest.java @@ -0,0 +1,79 @@ +package com.microsoft.appcenter; + +import com.microsoft.appcenter.utils.AppCenterLog; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.never; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(AppCenterLog.class) +public class FlagsTest { + + @Before + public void setUp() { + mockStatic(AppCenterLog.class); + } + + @Test + public void persistenceNone() { + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(0, false)); + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(0, true)); + verifyStatic(never()); + AppCenterLog.warn(anyString(), anyString()); + } + + @Test + public void persistenceNormal() { + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(Flags.PERSISTENCE_NORMAL, false)); + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(Flags.PERSISTENCE_NORMAL, true)); + verifyStatic(never()); + AppCenterLog.warn(anyString(), anyString()); + } + + @Test + public void persistenceCritical() { + assertEquals(Flags.PERSISTENCE_CRITICAL, Flags.getPersistencePriority(Flags.PERSISTENCE_CRITICAL, false)); + assertEquals(Flags.PERSISTENCE_CRITICAL, Flags.getPersistencePriority(Flags.PERSISTENCE_CRITICAL, true)); + verifyStatic(never()); + AppCenterLog.warn(anyString(), anyString()); + } + + @Test + public void persistenceDefaults() { + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(Flags.DEFAULT_FLAGS, false)); + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(Flags.DEFAULT_FLAGS, true)); + verifyStatic(never()); + AppCenterLog.warn(anyString(), anyString()); + } + + @Test + public void persistenceCriticalPlusOtherFlag() { + assertEquals(Flags.PERSISTENCE_CRITICAL, Flags.getPersistencePriority(Flags.PERSISTENCE_CRITICAL | 0x0100, false)); + assertEquals(Flags.PERSISTENCE_CRITICAL, Flags.getPersistencePriority(Flags.PERSISTENCE_CRITICAL | 0x0200, true)); + verifyStatic(never()); + AppCenterLog.warn(anyString(), anyString()); + } + + @Test + public void persistenceInvalidFlag() { + + /* Fallback without warning. */ + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(0x09, false)); + verifyStatic(never()); + AppCenterLog.warn(anyString(), anyString()); + + /* Fallback with warning. */ + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(0x09, true)); + verifyStatic(); + AppCenterLog.warn(anyString(), anyString()); + } +} diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java index f52963d43b..3e15f08952 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java @@ -52,9 +52,9 @@ public void nullAppSecretProvided() throws Persistence.PersistenceException { /* Check enqueue. */ Log log = mock(Log.class); channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); - verify(persistence, never()).putLog(eq(TEST_GROUP), eq(log), anyInt()); + verify(persistence, never()).putLog(eq(log), eq(TEST_GROUP), anyInt()); channel.enqueue(mock(Log.class), "other", Flags.DEFAULT_FLAGS); - verify(persistence, never()).putLog(anyString(), any(Log.class), anyInt()); + verify(persistence, never()).putLog(any(Log.class), anyString(), anyInt()); /* Check clear. Even without app secret it works as it could be logs from previous process. */ channel.clear(TEST_GROUP); @@ -129,7 +129,7 @@ public void startWithoutAppSecret() throws Persistence.PersistenceException { verify(defaultIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* Verify we didn't persist the log since AppCenter not started with app secret. */ - verify(mockPersistence, never()).putLog(eq(appCenterGroup), any(Log.class), eq(Flags.PERSISTENCE_NORMAL)); + verify(mockPersistence, never()).putLog(any(Log.class), eq(appCenterGroup), eq(Flags.PERSISTENCE_NORMAL)); /* Enqueuing 1 event from one collector group. */ channel.enqueue(mock(Log.class), oneCollectorGroup, Flags.DEFAULT_FLAGS); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java index fd54adad4c..8fc86e140a 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java @@ -137,7 +137,7 @@ public void filter() throws Persistence.PersistenceException { verify(listener1).shouldFilter(log); verify(listener2).onPreparingLog(log, TEST_GROUP); verify(listener2).shouldFilter(log); - verify(persistence, never()).putLog(eq(TEST_GROUP), eq(log), anyInt()); + verify(persistence, never()).putLog(eq(log), eq(TEST_GROUP), anyInt()); } /* Given 1 log. */ @@ -157,7 +157,7 @@ public void filter() throws Persistence.PersistenceException { /* Second listener skipped since first listener filtered out. */ verify(listener2, never()).shouldFilter(log); - verify(persistence, never()).putLog(eq(TEST_GROUP), eq(log), anyInt()); + verify(persistence, never()).putLog(eq(log), eq(TEST_GROUP), anyInt()); } /* Given 1 log. */ @@ -175,7 +175,7 @@ public void filter() throws Persistence.PersistenceException { verify(listener1).shouldFilter(log); verify(listener2).onPreparingLog(log, TEST_GROUP); verify(listener2).shouldFilter(log); - verify(persistence).putLog(TEST_GROUP, log, Flags.PERSISTENCE_NORMAL); + verify(persistence).putLog(log, TEST_GROUP, Flags.PERSISTENCE_NORMAL); } } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java index 785195e641..5ada13d658 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java @@ -60,7 +60,7 @@ public void pauseResumeGroup() throws Persistence.PersistenceException { /* 50 logs are persisted but never being sent to Ingestion. */ assertEquals(50, channel.getGroupState(TEST_GROUP).mPendingLogCount); - verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class), eq(Flags.PERSISTENCE_NORMAL)); + verify(mockPersistence, times(50)).putLog(any(Log.class), eq(TEST_GROUP), eq(Flags.PERSISTENCE_NORMAL)); verify(mockIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mockPersistence, never()).deleteLogs(any(String.class), any(String.class)); verify(mockListener, never()).onBeforeSending(any(Log.class)); @@ -145,7 +145,7 @@ public void pauseResumeTargetToken() throws Persistence.PersistenceException { channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); /* Verify persisted but not incrementing and checking logs. */ - verify(persistence).putLog(TEST_GROUP, log, Flags.PERSISTENCE_NORMAL); + verify(persistence).putLog(log, TEST_GROUP, Flags.PERSISTENCE_NORMAL); assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(persistence, never()).countLogs(TEST_GROUP); verify(ingestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -209,7 +209,7 @@ public void pauseGroupPauseTargetResumeGroupResumeTarget() throws Persistence.Pe channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); /* Verify persisted but not incrementing and checking logs. */ - verify(persistence).putLog(TEST_GROUP, log, Flags.PERSISTENCE_NORMAL); + verify(persistence).putLog(log, TEST_GROUP, Flags.PERSISTENCE_NORMAL); assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(ingestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java index 5a52b3e9b3..332a69a7e2 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java @@ -59,7 +59,7 @@ public void invalidGroup() throws Persistence.PersistenceException { channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); verify(log, never()).setDevice(any(Device.class)); verify(log, never()).setTimestamp(any(Date.class)); - verify(persistence, never()).putLog(eq(TEST_GROUP), eq(log), anyInt()); + verify(persistence, never()).putLog(eq(log), eq(TEST_GROUP), anyInt()); /* Trying remove group that not registered. */ channel.removeGroup(TEST_GROUP); @@ -94,7 +94,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); /* Verify that 5 items have been persisted. */ - verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); + verify(mockPersistence, times(50)).putLog(any(Log.class), eq(TEST_GROUP), eq(PERSISTENCE_NORMAL)); /* Verify that we have called sendAsync on the ingestion. */ verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -117,7 +117,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { /* Schedule only 1 log after that. */ channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); assertEquals(1, channel.getGroupState(TEST_GROUP).mPendingLogCount); - verify(mockPersistence, times(51)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); + verify(mockPersistence, times(51)).putLog(any(Log.class), eq(TEST_GROUP), eq(PERSISTENCE_NORMAL)); verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mockPersistence).deleteLogs(any(String.class), any(String.class)); verify(mockListener, times(50)).onSuccess(any(Log.class)); @@ -136,7 +136,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); assertEquals(2, channel.getGroupState(TEST_GROUP).mPendingLogCount); - verify(mockPersistence, times(53)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); + verify(mockPersistence, times(53)).putLog(any(Log.class), eq(TEST_GROUP), eq(PERSISTENCE_NORMAL)); verify(mockIngestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mockPersistence, times(2)).deleteLogs(any(String.class), any(String.class)); verify(mockListener, times(51)).onSuccess(any(Log.class)); @@ -232,7 +232,7 @@ public Object answer(InvocationOnMock invocation) { verify(mAppCenterHandler, times(4)).removeCallbacks(any(Runnable.class)); /* Verify all logs stored, N requests sent, not log deleted yet. */ - verify(mockPersistence, times(200)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); + verify(mockPersistence, times(200)).putLog(any(Log.class), eq(TEST_GROUP), eq(PERSISTENCE_NORMAL)); verify(mockIngestion, times(3)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mockPersistence, never()).deleteLogs(any(String.class), any(String.class)); @@ -281,7 +281,7 @@ public Object answer(InvocationOnMock invocation) { } /* Verify all logs stored, N requests sent, not log deleted yet. */ - verify(mockPersistence, times(100)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); + verify(mockPersistence, times(100)).putLog(any(Log.class), eq(TEST_GROUP), eq(PERSISTENCE_NORMAL)); verify(mockIngestion, times(3)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mockPersistence, never()).deleteLogs(any(String.class), any(String.class)); @@ -328,7 +328,7 @@ public void analyticsRecoverable() throws Persistence.PersistenceException { verify(mAppCenterHandler).removeCallbacks(any(Runnable.class)); /* Verify that 50 items have been persisted. */ - verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); + verify(mockPersistence, times(50)).putLog(any(Log.class), eq(TEST_GROUP), eq(PERSISTENCE_NORMAL)); /* Verify that we have called sendAsync on the ingestion. */ verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -406,7 +406,7 @@ public void analyticsFatal() throws Exception { verify(mAppCenterHandler).removeCallbacks(any(Runnable.class)); /* Verify that 50 items have been persisted. */ - verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); + verify(mockPersistence, times(50)).putLog(any(Log.class), eq(TEST_GROUP), eq(PERSISTENCE_NORMAL)); /* Verify that we have called sendAsync on the ingestion. */ verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -481,7 +481,7 @@ public void errorLogSuccess() throws Persistence.PersistenceException { channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); /* Verify that 2 items have been persisted. */ - verify(mockPersistence, times(2)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); + verify(mockPersistence, times(2)).putLog(any(Log.class), eq(TEST_GROUP), eq(PERSISTENCE_NORMAL)); /* Verify that we have called sendAsync on the ingestion twice as batch size is 1. */ verify(mockIngestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -521,7 +521,7 @@ public void errorLogRecoverable() throws Persistence.PersistenceException { channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); /* Verify that n items have been persisted. */ - verify(mockPersistence, times(logNumber)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); + verify(mockPersistence, times(logNumber)).putLog(any(Log.class), eq(TEST_GROUP), eq(PERSISTENCE_NORMAL)); /* Verify that we have called sendAsync on the ingestion once for the first item, but not more than that. */ verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -639,7 +639,7 @@ public void enqueuePersistenceFailure() throws Persistence.PersistenceException /* Simulate Persistence failing. */ doThrow(new Persistence.PersistenceException("mock", new IOException("mock"))). - when(mockPersistence).putLog(anyString(), any(Log.class), anyInt()); + when(mockPersistence).putLog(any(Log.class), anyString(), anyInt()); AppCenterIngestion mockIngestion = mock(AppCenterIngestion.class); DefaultChannel channel = new DefaultChannel(mock(Context.class), UUIDUtils.randomUUID().toString(), mockPersistence, mockIngestion, mAppCenterHandler); channel.addGroup(TEST_GROUP, 50, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, null); @@ -648,7 +648,7 @@ public void enqueuePersistenceFailure() throws Persistence.PersistenceException for (int i = 0; i < 50; i++) { channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULT_FLAGS); } - verify(mockPersistence, times(50)).putLog(eq(TEST_GROUP), any(Log.class), eq(PERSISTENCE_NORMAL)); + verify(mockPersistence, times(50)).putLog(any(Log.class), eq(TEST_GROUP), eq(PERSISTENCE_NORMAL)); verify(mockIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(mAppCenterHandler, never()).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); @@ -789,7 +789,7 @@ public void packageManagerIsBroken() throws Persistence.PersistenceException, De channel.enqueue(log, TEST_GROUP, Flags.DEFAULT_FLAGS); verify(listener).onPreparingLog(log, TEST_GROUP); verify(listener, never()).shouldFilter(log); - verify(persistence, never()).putLog(eq(TEST_GROUP), eq(log), anyInt()); + verify(persistence, never()).putLog(eq(log), eq(TEST_GROUP), anyInt()); } @Test @@ -902,4 +902,33 @@ public void invokeCallbacksAfterSuspendRecoverable() { /* But that we cleared batch state. */ verify(mockPersistence).clearPendingLogState(); } + + @Test + public void enqueueWithFlags() throws Persistence.PersistenceException { + + /* Other tests use default flags, test explicit flags here. */ + + /* Setup persistence, channel and a listener. */ + Persistence persistence = mock(Persistence.class); + Channel channel = new DefaultChannel(mock(Context.class), UUIDUtils.randomUUID().toString(), persistence, mock(AppCenterIngestion.class), mAppCenterHandler); + channel.addGroup(TEST_GROUP, 1, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, mock(Channel.GroupListener.class)); + Channel.Listener listener = mock(Channel.Listener.class); + channel.addListener(listener); + + /* Enqueuing 1 event with normal persistence. */ + Log normalLog = mock(Log.class); + channel.enqueue(normalLog, TEST_GROUP, Flags.PERSISTENCE_NORMAL); + + /* Verify listener and database get the same flags. */ + verify(listener).onPreparedLog(normalLog, TEST_GROUP, Flags.PERSISTENCE_NORMAL); + verify(persistence).putLog(normalLog, TEST_GROUP, Flags.PERSISTENCE_NORMAL); + + /* Enqueuing 1 event with critical persistence. */ + Log criticalLog = mock(Log.class); + channel.enqueue(criticalLog, TEST_GROUP, Flags.PERSISTENCE_CRITICAL); + + /* Verify listener and database get the same flags. */ + verify(listener).onPreparedLog(criticalLog, TEST_GROUP, Flags.PERSISTENCE_CRITICAL); + verify(persistence).putLog(criticalLog, TEST_GROUP, Flags.PERSISTENCE_CRITICAL); + } } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java index 2e729d5b65..f569551e1c 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java @@ -66,7 +66,7 @@ public void databaseOperationException() throws JSONException { /* Generate a log and persist. */ Log log = mock(Log.class); - mockPersistence.putLog("test-p1", log, Flags.PERSISTENCE_NORMAL); + mockPersistence.putLog(log, "test-p1", Flags.PERSISTENCE_NORMAL); fail("Expected persistence exception"); } catch (Persistence.PersistenceException ignore) { } finally { From 6a5336cd1a54d57b16ea02a67b4f27c5d75419ca Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Tue, 30 Oct 2018 17:08:11 -0700 Subject: [PATCH 26/68] Address feedbacks --- .../DatabasePersistenceAndroidTest.java | 21 ++++++++-------- .../persistence/PersistenceAndroidTest.java | 3 +++ .../java/com/microsoft/appcenter/Flags.java | 8 +++---- .../persistence/DatabasePersistence.java | 2 +- .../com/microsoft/appcenter/FlagsTest.java | 24 +++++++++---------- 5 files changed, 30 insertions(+), 28 deletions(-) diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index 4768129bfa..3338a7b1ef 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -51,7 +51,6 @@ import static com.microsoft.appcenter.Flags.PERSISTENCE_CRITICAL; import static com.microsoft.appcenter.Flags.PERSISTENCE_NORMAL; import static com.microsoft.appcenter.ingestion.models.json.MockLog.MOCK_LOG_TYPE; -import static com.microsoft.appcenter.persistence.DatabasePersistence.COLUMN_PRIORITY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -138,7 +137,7 @@ private void putLog(int inputFlags, Integer persistedPriorityFlag) throws Persis /* Check priority flag. */ ContentValues contentValues = getContentValues(persistence, "test-p1"); - assertEquals(persistedPriorityFlag, contentValues.getAsInteger(COLUMN_PRIORITY)); + assertEquals(persistedPriorityFlag, contentValues.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); } finally { persistence.close(); } @@ -812,7 +811,7 @@ public void upgradeFromVersion1to4() throws PersistenceException, JSONException oldSchema.remove(DatabasePersistence.COLUMN_TARGET_TOKEN); oldSchema.remove(DatabasePersistence.COLUMN_DATA_TYPE); oldSchema.remove(DatabasePersistence.COLUMN_TARGET_KEY); - oldSchema.remove(COLUMN_PRIORITY); + oldSchema.remove(DatabasePersistence.COLUMN_PRIORITY); DatabaseManager databaseManager = new DatabaseManager(sContext, DatabasePersistence.DATABASE, DatabasePersistence.TABLE, 1, oldSchema, new DatabaseManager.Listener() { @Override @@ -861,7 +860,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /* Check priority migration. */ ContentValues values = getContentValues(persistence, "test"); - assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(COLUMN_PRIORITY)); + assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); /* Put new data with token. */ persistence.putLog(commonSchemaLog, "test/one", PERSISTENCE_NORMAL); @@ -893,7 +892,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { assertEquals(commonSchemaLog.getIKey(), "o:" + targetKey); /* Verify priority stored too. */ - assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(COLUMN_PRIORITY)); + assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); } finally { persistence.close(); } @@ -905,7 +904,7 @@ public void upgradeFromVersion2to4() throws PersistenceException, JSONException /* Initialize database persistence with old schema. */ ContentValues oldSchema = new ContentValues(DatabasePersistence.SCHEMA); oldSchema.remove(DatabasePersistence.COLUMN_TARGET_KEY); - oldSchema.remove(COLUMN_PRIORITY); + oldSchema.remove(DatabasePersistence.COLUMN_PRIORITY); DatabaseManager databaseManager = new DatabaseManager(sContext, DatabasePersistence.DATABASE, DatabasePersistence.TABLE, DatabasePersistence.VERSION_TYPE_API_KEY, oldSchema, new DatabaseManager.Listener() { @Override @@ -955,7 +954,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /* Check priority migration. */ ContentValues values = getContentValues(persistence, "test"); - assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(COLUMN_PRIORITY)); + assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); /* Put new data with token. */ persistence.putLog(commonSchemaLog, "test/one", PERSISTENCE_NORMAL); @@ -987,7 +986,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { assertEquals(commonSchemaLog.getIKey(), "o:" + targetKey); /* Verify priority stored too. */ - assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(COLUMN_PRIORITY)); + assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); } finally { persistence.close(); } @@ -998,7 +997,7 @@ public void upgradeFromVersion3to4() throws PersistenceException, JSONException /* Initialize database persistence with old schema. */ ContentValues oldSchema = new ContentValues(DatabasePersistence.SCHEMA); - oldSchema.remove(COLUMN_PRIORITY); + oldSchema.remove(DatabasePersistence.COLUMN_PRIORITY); DatabaseManager databaseManager = new DatabaseManager(sContext, DatabasePersistence.DATABASE, DatabasePersistence.TABLE, DatabasePersistence.VERSION_TARGET_KEY, oldSchema, new DatabaseManager.Listener() { @Override @@ -1048,7 +1047,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /* Check priority migration. */ ContentValues values = getContentValues(persistence, "test"); - assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(COLUMN_PRIORITY)); + assertEquals((Integer) PERSISTENCE_NORMAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); /* Put new data with token. */ persistence.putLog(commonSchemaLog, "test/one", PERSISTENCE_CRITICAL); @@ -1080,7 +1079,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { assertEquals(commonSchemaLog.getIKey(), "o:" + targetKey); /* Verify priority stored too. */ - assertEquals((Integer) PERSISTENCE_CRITICAL, values.getAsInteger(COLUMN_PRIORITY)); + assertEquals((Integer) PERSISTENCE_CRITICAL, values.getAsInteger(DatabasePersistence.COLUMN_PRIORITY)); } finally { persistence.close(); } diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java index 7e23a37238..0586d2ee13 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/PersistenceAndroidTest.java @@ -38,6 +38,7 @@ public static void setUpClass() { @AfterClass public static void tearDownClass() { + /* Clean up database. */ sContext.deleteDatabase("test-persistence"); } @@ -50,9 +51,11 @@ public void missingLogSerializer() throws Persistence.PersistenceException { //noinspection TryFinallyCanBeTryWithResources (try with resources statement is API >= 19) try { + /* Generate a log and persist. */ persistence.putLog(new MockLog(), "exception", PERSISTENCE_NORMAL); } finally { + /* Close. */ //noinspection ThrowFromFinallyBlock persistence.close(); diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java index 9b64ab0671..bb0a979e01 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/Flags.java @@ -32,11 +32,11 @@ public final class Flags { /** * Get persistence priority flag. * - * @param flags all flags to extract persistence priority from. - * @param warnFallback if true and falling back from an invalid value: print a warning. - * @return persistence priority flag. + * @param flags All flags to extract persistence priority from. + * @param warnFallback If true and falling back from an invalid value: print a warning. + * @return persistence Priority flag for persistence. */ - public static int getPersistencePriority(int flags, boolean warnFallback) { + public static int getPersistenceFlag(int flags, boolean warnFallback) { int persistencePriority = flags & PERSISTENCE_MASK; if (persistencePriority != PERSISTENCE_NORMAL && persistencePriority != PERSISTENCE_CRITICAL) { if (persistencePriority != 0 && warnFallback) { diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index 2692b27f48..b88f1e9658 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -249,7 +249,7 @@ public long putLog(@NonNull Log log, @NonNull String group, @IntRange(from = Fla targetKey = null; targetToken = null; } - contentValues = getContentValues(group, isLargePayload ? null : payload, targetToken, log.getType(), targetKey, Flags.getPersistencePriority(flags, false)); + contentValues = getContentValues(group, isLargePayload ? null : payload, targetToken, log.getType(), targetKey, Flags.getPersistenceFlag(flags, false)); long databaseId = mDatabaseManager.put(contentValues); if (databaseId == -1) { throw new PersistenceException("Failed to store a log to the Persistence database for log type " + log.getType() + "."); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/FlagsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/FlagsTest.java index c8468b2245..4090504242 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/FlagsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/FlagsTest.java @@ -25,40 +25,40 @@ public void setUp() { @Test public void persistenceNone() { - assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(0, false)); - assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(0, true)); + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistenceFlag(0, false)); + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistenceFlag(0, true)); verifyStatic(never()); AppCenterLog.warn(anyString(), anyString()); } @Test public void persistenceNormal() { - assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(Flags.PERSISTENCE_NORMAL, false)); - assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(Flags.PERSISTENCE_NORMAL, true)); + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistenceFlag(Flags.PERSISTENCE_NORMAL, false)); + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistenceFlag(Flags.PERSISTENCE_NORMAL, true)); verifyStatic(never()); AppCenterLog.warn(anyString(), anyString()); } @Test public void persistenceCritical() { - assertEquals(Flags.PERSISTENCE_CRITICAL, Flags.getPersistencePriority(Flags.PERSISTENCE_CRITICAL, false)); - assertEquals(Flags.PERSISTENCE_CRITICAL, Flags.getPersistencePriority(Flags.PERSISTENCE_CRITICAL, true)); + assertEquals(Flags.PERSISTENCE_CRITICAL, Flags.getPersistenceFlag(Flags.PERSISTENCE_CRITICAL, false)); + assertEquals(Flags.PERSISTENCE_CRITICAL, Flags.getPersistenceFlag(Flags.PERSISTENCE_CRITICAL, true)); verifyStatic(never()); AppCenterLog.warn(anyString(), anyString()); } @Test public void persistenceDefaults() { - assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(Flags.DEFAULT_FLAGS, false)); - assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(Flags.DEFAULT_FLAGS, true)); + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistenceFlag(Flags.DEFAULT_FLAGS, false)); + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistenceFlag(Flags.DEFAULT_FLAGS, true)); verifyStatic(never()); AppCenterLog.warn(anyString(), anyString()); } @Test public void persistenceCriticalPlusOtherFlag() { - assertEquals(Flags.PERSISTENCE_CRITICAL, Flags.getPersistencePriority(Flags.PERSISTENCE_CRITICAL | 0x0100, false)); - assertEquals(Flags.PERSISTENCE_CRITICAL, Flags.getPersistencePriority(Flags.PERSISTENCE_CRITICAL | 0x0200, true)); + assertEquals(Flags.PERSISTENCE_CRITICAL, Flags.getPersistenceFlag(Flags.PERSISTENCE_CRITICAL | 0x0100, false)); + assertEquals(Flags.PERSISTENCE_CRITICAL, Flags.getPersistenceFlag(Flags.PERSISTENCE_CRITICAL | 0x0200, true)); verifyStatic(never()); AppCenterLog.warn(anyString(), anyString()); } @@ -67,12 +67,12 @@ public void persistenceCriticalPlusOtherFlag() { public void persistenceInvalidFlag() { /* Fallback without warning. */ - assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(0x09, false)); + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistenceFlag(0x09, false)); verifyStatic(never()); AppCenterLog.warn(anyString(), anyString()); /* Fallback with warning. */ - assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistencePriority(0x09, true)); + assertEquals(Flags.PERSISTENCE_NORMAL, Flags.getPersistenceFlag(0x09, true)); verifyStatic(); AppCenterLog.warn(anyString(), anyString()); } From 374bc5944ae1fcc830f6286daabcbbcc9380025e Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Tue, 30 Oct 2018 17:34:50 -0700 Subject: [PATCH 27/68] Add flags to common schema payload --- .../channel/OneCollectorChannelListener.java | 7 +++- .../ingestion/models/one/CommonSchemaLog.java | 3 +- .../OneCollectorChannelListenerTest.java | 39 ++++++++++++------- .../models/one/MockCommonSchemaLog.java | 2 +- 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/OneCollectorChannelListener.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/OneCollectorChannelListener.java index c21e0c671d..1d974b0c1b 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/OneCollectorChannelListener.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/channel/OneCollectorChannelListener.java @@ -124,8 +124,13 @@ public void onPreparedLog(@NonNull Log log, @NonNull String groupName, int flags return; } - /* Add SDK extension part A fields. libVer is already set. */ + /* Add additional part A fields that are not known by the modules during conversion. */ for (CommonSchemaLog commonSchemaLog : commonSchemaLogs) { + + /* Add flags. */ + commonSchemaLog.setFlags((long) flags); + + /* Add SDK extension missing fields: installId, epoch and seq. libVer is already set. */ EpochAndSeq epochAndSeq = mEpochsAndSeqsByIKey.get(commonSchemaLog.getIKey()); if (epochAndSeq == null) { epochAndSeq = new EpochAndSeq(UUIDUtils.randomUUID().toString()); diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/ingestion/models/one/CommonSchemaLog.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/ingestion/models/one/CommonSchemaLog.java index 6234937e5b..ba4b479e4b 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/ingestion/models/one/CommonSchemaLog.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/ingestion/models/one/CommonSchemaLog.java @@ -153,6 +153,7 @@ public Double getPopSample() { * * @param popSample pop sample. */ + @SuppressWarnings("WeakerAccess") public void setPopSample(Double popSample) { this.popSample = popSample; } @@ -181,7 +182,6 @@ public void setIKey(String iKey) { * * @return flags. */ - @SuppressWarnings("WeakerAccess") public Long getFlags() { return flags; } @@ -210,6 +210,7 @@ public String getCV() { * * @param cV correlation vector. */ + @SuppressWarnings("WeakerAccess") public void setCV(String cV) { this.cV = cV; } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java index 6d7c1bdca3..c6822bcf60 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java @@ -8,6 +8,7 @@ import com.microsoft.appcenter.ingestion.models.json.LogSerializer; import com.microsoft.appcenter.ingestion.models.one.CommonSchemaLog; import com.microsoft.appcenter.ingestion.models.one.Extensions; +import com.microsoft.appcenter.ingestion.models.one.MockCommonSchemaLog; import com.microsoft.appcenter.ingestion.models.one.SdkExtension; import com.microsoft.appcenter.utils.UUIDUtils; @@ -20,6 +21,8 @@ import java.util.UUID; import static com.microsoft.appcenter.Flags.DEFAULT_FLAGS; +import static com.microsoft.appcenter.Flags.PERSISTENCE_CRITICAL; +import static com.microsoft.appcenter.Flags.PERSISTENCE_NORMAL; import static com.microsoft.appcenter.channel.AbstractDefaultChannelTest.TEST_GROUP; import static com.microsoft.appcenter.channel.OneCollectorChannelListener.ONE_COLLECTOR_GROUP_NAME_SUFFIX; import static com.microsoft.appcenter.channel.OneCollectorChannelListener.ONE_COLLECTOR_TRIGGER_COUNT; @@ -92,18 +95,18 @@ public void enqueueConvertedLogs() { when(originalLog.getTransmissionTargetTokens()).thenReturn(new HashSet<>(Collections.singletonList("t1"))); /* Mock a log. */ - CommonSchemaLog log1 = mock(CommonSchemaLog.class); + CommonSchemaLog log1 = new MockCommonSchemaLog(); + log1.setIKey("t1"); Extensions ext1 = new Extensions(); ext1.setSdk(new SdkExtension()); - when(log1.getExt()).thenReturn(ext1); - when(log1.getIKey()).thenReturn("t1"); + log1.setExt(ext1); /* Mock another log. */ - CommonSchemaLog log2 = mock(CommonSchemaLog.class); + CommonSchemaLog log2 = new MockCommonSchemaLog(); + log2.setIKey("t1"); Extensions ext2 = new Extensions(); ext2.setSdk(new SdkExtension()); - when(log2.getExt()).thenReturn(ext2); - when(log2.getIKey()).thenReturn("t1"); + log2.setExt(ext2); /* Mock conversion of logs. */ Channel channel = mock(Channel.class); @@ -120,6 +123,10 @@ public void enqueueConvertedLogs() { verify(logSerializer).toCommonSchemaLog(originalLog); verifyNoMoreInteractions(logSerializer); + /* Verify flags. */ + assertEquals(new Long(DEFAULT_FLAGS), log1.getFlags()); + assertEquals(new Long(DEFAULT_FLAGS), log2.getFlags()); + /* Verify same epoch. */ assertNotNull(log1.getExt().getSdk().getEpoch()); assertEquals(log1.getExt().getSdk().getEpoch(), log2.getExt().getSdk().getEpoch()); @@ -141,13 +148,14 @@ public void enqueueConvertedLogs() { /* Mock log with another key to see new seq/epoch. */ when(originalLog.getTransmissionTargetTokens()).thenReturn(new HashSet<>(Collections.singletonList("t2"))); - CommonSchemaLog log3 = mock(CommonSchemaLog.class); + CommonSchemaLog log3 = new MockCommonSchemaLog(); + log3.setIKey("t2"); Extensions ext3 = new Extensions(); ext3.setSdk(new SdkExtension()); - when(log3.getExt()).thenReturn(ext3); - when(log3.getIKey()).thenReturn("t2"); + log3.setExt(ext3); when(logSerializer.toCommonSchemaLog(any(Log.class))).thenReturn(Collections.singletonList(log3)); - listener.onPreparedLog(originalLog, TEST_GROUP, DEFAULT_FLAGS); + listener.onPreparedLog(originalLog, TEST_GROUP, PERSISTENCE_CRITICAL); + assertEquals(new Long(PERSISTENCE_CRITICAL), log3.getFlags()); assertEquals((Long) 1L, log3.getExt().getSdk().getSeq()); assertNotNull(log3.getExt().getSdk().getEpoch()); assertNotEquals(log1.getExt().getSdk().getEpoch(), log3.getExt().getSdk().getEpoch()); @@ -157,15 +165,16 @@ public void enqueueConvertedLogs() { listener.onGloballyEnabled(true); /* Mock a 4rd log in first group to check reset. */ - CommonSchemaLog log4 = mock(CommonSchemaLog.class); + CommonSchemaLog log4 = new MockCommonSchemaLog(); + log4.setIKey("t2"); Extensions ext4 = new Extensions(); ext4.setSdk(new SdkExtension()); - when(log4.getExt()).thenReturn(ext4); - when(log4.getIKey()).thenReturn("t2"); + log4.setExt(ext4); when(logSerializer.toCommonSchemaLog(any(Log.class))).thenReturn(Collections.singletonList(log4)); - listener.onPreparedLog(originalLog, TEST_GROUP, DEFAULT_FLAGS); + listener.onPreparedLog(originalLog, TEST_GROUP, PERSISTENCE_NORMAL); - /* Verify reset of epoch/seq. */ + /* Verify flags and reset of epoch/seq. */ + assertEquals(new Long(PERSISTENCE_NORMAL), log4.getFlags()); assertEquals((Long) 1L, log4.getExt().getSdk().getSeq()); assertNotNull(log4.getExt().getSdk().getEpoch()); assertNotEquals(log3.getExt().getSdk().getEpoch(), log4.getExt().getSdk().getEpoch()); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/one/MockCommonSchemaLog.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/one/MockCommonSchemaLog.java index d7e75060a3..0abe948253 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/one/MockCommonSchemaLog.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/one/MockCommonSchemaLog.java @@ -1,6 +1,6 @@ package com.microsoft.appcenter.ingestion.models.one; -class MockCommonSchemaLog extends CommonSchemaLog { +public class MockCommonSchemaLog extends CommonSchemaLog { private static final String TYPE = "mock"; From f1d8753ff6871b30944ce08002c53ee89d2b80bd Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Tue, 30 Oct 2018 19:00:55 -0700 Subject: [PATCH 28/68] Work in progress to purge critical logs last --- .../DatabasePersistenceAndroidTest.java | 191 ++++++++++++++++-- .../storage/DatabaseManagerAndroidTest.java | 38 ++-- .../persistence/DatabasePersistence.java | 10 +- .../utils/storage/DatabaseManager.java | 28 ++- .../appcenter/AbstractAppCenterTest.java | 8 +- .../AppCenterInstrumentationTest.java | 20 +- .../persistence/DatabasePersistenceTest.java | 22 +- .../utils/storage/DatabaseManagerTest.java | 4 +- 8 files changed, 246 insertions(+), 75 deletions(-) diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index 3338a7b1ef..c67c9716cb 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -51,6 +51,7 @@ import static com.microsoft.appcenter.Flags.PERSISTENCE_CRITICAL; import static com.microsoft.appcenter.Flags.PERSISTENCE_NORMAL; import static com.microsoft.appcenter.ingestion.models.json.MockLog.MOCK_LOG_TYPE; +import static com.microsoft.appcenter.persistence.DatabasePersistence.SCHEMA; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -101,7 +102,7 @@ private ContentValues getContentValues(DatabasePersistence persistence, String g SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); builder.appendWhere(DatabasePersistence.COLUMN_GROUP + " = ?"); String[] selectionArgs = new String[]{group}; - Cursor cursor = persistence.mDatabaseManager.getCursor(builder, selectionArgs, false); + Cursor cursor = persistence.mDatabaseManager.getCursor(builder, selectionArgs, null, false); ContentValues values = persistence.mDatabaseManager.nextValues(cursor); assertNotNull(values); return values; @@ -265,7 +266,7 @@ public void putLargeLogFails() { public void putLargeLogFailsToRead() throws PersistenceException { /* Initialize database persistence. */ - DatabasePersistence persistence = new DatabasePersistence(sContext, 1, DatabasePersistence.SCHEMA); + DatabasePersistence persistence = new DatabasePersistence(sContext); /* Set a mock log serializer. */ LogSerializer logSerializer = new DefaultLogSerializer(); @@ -358,7 +359,7 @@ public void putLargeLogNotSupportedOnCommonSchema() throws JSONException { public void putTooManyLogs() throws PersistenceException { /* Initialize database persistence. */ - DatabasePersistence persistence = new DatabasePersistence(sContext, 2, DatabasePersistence.SCHEMA); + DatabasePersistence persistence = new DatabasePersistence(sContext); assertTrue(persistence.setMaxStorageSize(MAX_STORAGE_SIZE_IN_BYTES)); /* Set a mock log serializer. */ @@ -396,10 +397,58 @@ public void putTooManyLogs() throws PersistenceException { } @Test - public void putLogLargerThanMaxSizeClearsEverything() throws PersistenceException { + public void putTooManyLogsCritical() throws PersistenceException { /* Initialize database persistence. */ - DatabasePersistence persistence = new DatabasePersistence(sContext, 2, DatabasePersistence.SCHEMA); + DatabasePersistence persistence = new DatabasePersistence(sContext); + assertTrue(persistence.setMaxStorageSize(MAX_STORAGE_SIZE_IN_BYTES)); + + /* Set a mock log serializer. */ + LogSerializer logSerializer = new DefaultLogSerializer(); + logSerializer.addLogFactory(MOCK_LOG_TYPE, new MockLogFactory()); + persistence.setLogSerializer(logSerializer); + try { + + /* Generate 3 critical logs to be kept first. */ + List expectedLogs = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + MockLog log = AndroidTestUtils.generateMockLog(); + persistence.putLog(log, "test-p1", PERSISTENCE_CRITICAL); + expectedLogs.add(log); + } + + /* Generate some normal priority logs that will be evicted. */ + for (int i = 0; i < 10; i++) { + persistence.putLog(AndroidTestUtils.generateMockLog(), "test-p1", PERSISTENCE_NORMAL); + } + + /* + * Generate the maximum number of normal logs that we can store in this configuration. + * This will evict all previously stored logs that are not critical. + */ + for (int i = 0; i < 3; i++) { + MockLog log = AndroidTestUtils.generateMockLog(); + persistence.putLog(log, "test-p1", PERSISTENCE_NORMAL); + expectedLogs.add(log); + } + + /* Get logs from persistence. */ + List outputLogs = new ArrayList<>(); + persistence.getLogs("test-p1", Collections.emptyList(), 7, outputLogs); + assertEquals(expectedLogs.size(), persistence.countLogs("test-p1")); + assertEquals(expectedLogs, outputLogs); + } finally { + + //noinspection ThrowFromFinallyBlock + persistence.close(); + } + } + + @Test + public void putNormalLogLargerThanMaxSizeClearsEverythingWithNormalPriority() throws PersistenceException { + + /* Initialize database persistence. */ + DatabasePersistence persistence = new DatabasePersistence(sContext); assertTrue(persistence.setMaxStorageSize(MAX_STORAGE_SIZE_IN_BYTES)); /* Set a mock log serializer. */ @@ -443,6 +492,116 @@ public void putLogLargerThanMaxSizeClearsEverything() throws PersistenceExceptio } } + @Test + public void putCriticalLogLargerThanMaxSizeClearsEverything() throws PersistenceException { + + /* Initialize database persistence. */ + DatabasePersistence persistence = new DatabasePersistence(sContext); + assertTrue(persistence.setMaxStorageSize(MAX_STORAGE_SIZE_IN_BYTES)); + + /* Set a mock log serializer. */ + LogSerializer logSerializer = new DefaultLogSerializer(); + logSerializer.addLogFactory(MOCK_LOG_TYPE, new MockLogFactory()); + persistence.setLogSerializer(logSerializer); + try { + + /* Generate some logs of both priority that will be evicted. */ + int someLogCount = 3; + for (int i = 0; i < someLogCount; i++) { + persistence.putLog(AndroidTestUtils.generateMockLog(), "test-p1", PERSISTENCE_NORMAL); + } + for (int i = 0; i < someLogCount; i++) { + persistence.putLog(AndroidTestUtils.generateMockLog(), "test-p1", PERSISTENCE_CRITICAL); + } + assertEquals(someLogCount * 2, persistence.countLogs("test-p1")); + + /* + * Generate a log that is so large that will empty all the database and + * eventually fails. + */ + LogWithProperties log = AndroidTestUtils.generateMockLog(); + int size = 30 * 1024; + StringBuilder largeValue = new StringBuilder(size); + for (int i = 0; i < size; i++) { + largeValue.append("x"); + } + Map properties = new HashMap<>(); + properties.put("key", largeValue.toString()); + log.setProperties(properties); + try { + persistence.putLog(log, "test-p1", PERSISTENCE_CRITICAL); + fail("Expected persistence exception"); + } catch (PersistenceException ignore) { + } + + /* Verify the behavior: not inserted and database now empty. */ + assertEquals(0, persistence.countLogs("test-p1")); + } finally { + + //noinspection ThrowFromFinallyBlock + persistence.close(); + } + } + + @Test + public void putNormalLogLargerThanMaxSizeKeepsCritical() throws PersistenceException { + + /* Initialize database persistence. */ + DatabasePersistence persistence = new DatabasePersistence(sContext); + assertTrue(persistence.setMaxStorageSize(MAX_STORAGE_SIZE_IN_BYTES)); + + /* Set a mock log serializer. */ + LogSerializer logSerializer = new DefaultLogSerializer(); + logSerializer.addLogFactory(MOCK_LOG_TYPE, new MockLogFactory()); + persistence.setLogSerializer(logSerializer); + try { + + /* Generate some logs that will be kept as they are critical. */ + List expectedLogs = new ArrayList<>(); + int someLogCount = 3; + for (int i = 0; i < someLogCount; i++) { + MockLog log = AndroidTestUtils.generateMockLog(); + persistence.putLog(log, "test-p1", PERSISTENCE_CRITICAL); + expectedLogs.add(log); + } + assertEquals(someLogCount, persistence.countLogs("test-p1")); + + /* Generate some normal priority logs that will be evicted. */ + for (int i = 0; i < 10; i++) { + persistence.putLog(AndroidTestUtils.generateMockLog(), "test-p1", PERSISTENCE_NORMAL); + } + + /* + * Generate a normal priority log that is so large that will fail but not discard + * critical logs. + */ + LogWithProperties log = AndroidTestUtils.generateMockLog(); + int size = 30 * 1024; + StringBuilder largeValue = new StringBuilder(size); + for (int i = 0; i < size; i++) { + largeValue.append("x"); + } + Map properties = new HashMap<>(); + properties.put("key", largeValue.toString()); + log.setProperties(properties); + try { + persistence.putLog(log, "test-p1", PERSISTENCE_NORMAL); + fail("Expected persistence exception"); + } catch (PersistenceException ignore) { + } + + /* Get logs from persistence: critical were kept. */ + List outputLogs = new ArrayList<>(); + persistence.getLogs("test-p1", Collections.emptyList(), 7, outputLogs); + assertEquals(expectedLogs.size(), persistence.countLogs("test-p1")); + assertEquals(expectedLogs, outputLogs); + } finally { + + //noinspection ThrowFromFinallyBlock + persistence.close(); + } + } + @Test(expected = PersistenceException.class) public void putLogException() throws PersistenceException, JSONException { @@ -467,7 +626,7 @@ public void putLogException() throws PersistenceException, JSONException { public void deleteLogs() throws PersistenceException { /* Initialize database persistence. */ - DatabasePersistence persistence = new DatabasePersistence(sContext, 1, DatabasePersistence.SCHEMA); + DatabasePersistence persistence = new DatabasePersistence(sContext, 1, SCHEMA); /* Set a mock log serializer. */ LogSerializer logSerializer = new DefaultLogSerializer(); @@ -511,9 +670,9 @@ public void deleteLogs() throws PersistenceException { builder.appendWhere(DatabasePersistence.COLUMN_GROUP + " = ?"); /* Access DatabaseStorage directly to verify the deletions. */ - Cursor cursor1 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p1"}, false); - Cursor cursor2 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p2"}, false); - Cursor cursor3 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p3"}, false); + Cursor cursor1 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p1"}, null, false); + Cursor cursor2 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p2"}, null, false); + Cursor cursor3 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p3"}, null, false); //noinspection TryFinallyCanBeTryWithResources try { @@ -534,7 +693,7 @@ public void deleteLogs() throws PersistenceException { persistence.deleteLogs("test-p1", id); /* Access DatabaseStorage directly to verify the deletions. */ - Cursor cursor4 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p1"}, false); + Cursor cursor4 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p1"}, null, false); //noinspection TryFinallyCanBeTryWithResources try { @@ -807,7 +966,7 @@ public void getLogsException() throws PersistenceException, JSONException { public void upgradeFromVersion1to4() throws PersistenceException, JSONException { /* Initialize database persistence with old schema. */ - ContentValues oldSchema = new ContentValues(DatabasePersistence.SCHEMA); + ContentValues oldSchema = new ContentValues(SCHEMA); oldSchema.remove(DatabasePersistence.COLUMN_TARGET_TOKEN); oldSchema.remove(DatabasePersistence.COLUMN_DATA_TYPE); oldSchema.remove(DatabasePersistence.COLUMN_TARGET_KEY); @@ -831,7 +990,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { ContentValues contentValues = new ContentValues(); contentValues.put(DatabasePersistence.COLUMN_GROUP, "test"); contentValues.put(DatabasePersistence.COLUMN_LOG, logSerializer.serializeLog(oldLog)); - databaseManager.put(contentValues); + databaseManager.put(contentValues, DatabasePersistence.COLUMN_PRIORITY); } finally { databaseManager.close(); } @@ -902,7 +1061,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { public void upgradeFromVersion2to4() throws PersistenceException, JSONException { /* Initialize database persistence with old schema. */ - ContentValues oldSchema = new ContentValues(DatabasePersistence.SCHEMA); + ContentValues oldSchema = new ContentValues(SCHEMA); oldSchema.remove(DatabasePersistence.COLUMN_TARGET_KEY); oldSchema.remove(DatabasePersistence.COLUMN_PRIORITY); DatabaseManager databaseManager = new DatabaseManager(sContext, DatabasePersistence.DATABASE, DatabasePersistence.TABLE, DatabasePersistence.VERSION_TYPE_API_KEY, oldSchema, new DatabaseManager.Listener() { @@ -925,7 +1084,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { contentValues.put(DatabasePersistence.COLUMN_GROUP, "test"); contentValues.put(DatabasePersistence.COLUMN_LOG, logSerializer.serializeLog(oldLog)); contentValues.put(DatabasePersistence.COLUMN_DATA_TYPE, MOCK_LOG_TYPE); - databaseManager.put(contentValues); + databaseManager.put(contentValues, DatabasePersistence.COLUMN_PRIORITY); } finally { databaseManager.close(); } @@ -996,7 +1155,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { public void upgradeFromVersion3to4() throws PersistenceException, JSONException { /* Initialize database persistence with old schema. */ - ContentValues oldSchema = new ContentValues(DatabasePersistence.SCHEMA); + ContentValues oldSchema = new ContentValues(SCHEMA); oldSchema.remove(DatabasePersistence.COLUMN_PRIORITY); DatabaseManager databaseManager = new DatabaseManager(sContext, DatabasePersistence.DATABASE, DatabasePersistence.TABLE, DatabasePersistence.VERSION_TARGET_KEY, oldSchema, new DatabaseManager.Listener() { @@ -1018,7 +1177,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { contentValues.put(DatabasePersistence.COLUMN_GROUP, "test"); contentValues.put(DatabasePersistence.COLUMN_LOG, logSerializer.serializeLog(oldLog)); contentValues.put(DatabasePersistence.COLUMN_DATA_TYPE, MOCK_LOG_TYPE); - databaseManager.put(contentValues); + databaseManager.put(contentValues, DatabasePersistence.COLUMN_PRIORITY); } finally { databaseManager.close(); } diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java index 3f9bfbf07a..2d4d4f6e17 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java @@ -82,7 +82,7 @@ private static ContentValues get(DatabaseManager databaseManager, long id) { SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); builder.appendWhere(DatabaseManager.PRIMARY_KEY + " = ?"); String[] selectionArgs = new String[]{String.valueOf(id)}; - Cursor cursor = databaseManager.getCursor(builder, selectionArgs, false); + Cursor cursor = databaseManager.getCursor(builder, selectionArgs, null, false); try { return databaseManager.nextValues(cursor); } finally { @@ -100,11 +100,11 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { ContentValues value3 = generateContentValues(); /* Put. */ - Long value1Id = databaseManager.put(value1); + Long value1Id = databaseManager.put(value1, null); assertNotNull(value1Id); /* Put another. */ - Long value2Id = databaseManager.put(value2); + Long value2Id = databaseManager.put(value2, null); assertNotNull(value2Id); /* Generate an ID that is neither value1Id nor value2Id. */ @@ -119,12 +119,12 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { assertNull(nullValueFromDatabase); /* Count with scanner. */ - Cursor cursor = databaseManager.getCursor(null, null, false); + Cursor cursor = databaseManager.getCursor(null, null, null, false); assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount()); SQLiteQueryBuilder queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); queryBuilder.appendWhere("COL_STRING = ?"); - Cursor cursor1 = databaseManager.getCursor(queryBuilder, new String[]{value1.getAsString("COL_STRING")}, false); + Cursor cursor1 = databaseManager.getCursor(queryBuilder, new String[]{value1.getAsString("COL_STRING")}, null, false); assertEquals(1, cursor1.getCount()); assertTrue(cursor1.moveToNext()); assertContentValuesEquals(value1, databaseManager.buildValues(cursor1)); @@ -133,20 +133,20 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { /* Null value matching. */ queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); queryBuilder.appendWhere("COL_STRING IS NULL"); - assertEquals(0, databaseManager.getCursor(queryBuilder, null, false).getCount()); + assertEquals(0, databaseManager.getCursor(queryBuilder, null, null, false).getCount()); queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); queryBuilder.appendWhere("COL_STRING_NULL IS NULL"); - assertEquals(2, databaseManager.getCursor(queryBuilder, null, false).getCount()); + assertEquals(2, databaseManager.getCursor(queryBuilder, null, null, false).getCount()); /* Test null value filter does not exclude anything, so returns the 2 logs. */ queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); - cursor = databaseManager.getCursor(queryBuilder, null, false); + cursor = databaseManager.getCursor(queryBuilder, null, null, false); assertEquals(2, cursor.getCount()); /* Test filtering only with the second key parameter to get only the second log. */ queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); queryBuilder.appendWhere("COL_STRING NOT IN (?)"); - cursor = databaseManager.getCursor(queryBuilder, new String[]{value1.getAsString("COL_STRING")}, false); + cursor = databaseManager.getCursor(queryBuilder, new String[]{value1.getAsString("COL_STRING")}, null, false); assertEquals(1, cursor.getCount()); assertTrue(cursor.moveToNext()); assertContentValuesEquals(value2, databaseManager.buildValues(cursor)); @@ -155,13 +155,13 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { databaseManager.delete(value1Id); assertNull(get(databaseManager, value1Id)); assertEquals(1, databaseManager.getRowCount()); - assertEquals(1, databaseManager.getCursor(null, null, false).getCount()); + assertEquals(1, databaseManager.getCursor(null, null, null, false).getCount()); /* Put logs to delete multiple IDs. */ ContentValues value4 = generateContentValues(); ContentValues value5 = generateContentValues(); - Long value4Id = databaseManager.put(value4); - Long value5Id = databaseManager.put(value5); + Long value4Id = databaseManager.put(value4, null); + Long value5Id = databaseManager.put(value5, null); assertNotNull(value4Id); assertNotNull(value5Id); @@ -176,8 +176,8 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { ContentValues value7 = generateContentValues(); value6.put("COL_STRING", value2.getAsString("COL_STRING")); value7.put("COL_STRING", value2.getAsString("COL_STRING") + "A"); - Long value6Id = databaseManager.put(value6); - Long value7Id = databaseManager.put(value7); + Long value6Id = databaseManager.put(value6, null); + Long value7Id = databaseManager.put(value7, null); assertNotNull(value6Id); assertNotNull(value7Id); @@ -269,9 +269,9 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { try { /* Database will always create a column for identifiers so default length of all tables is 1. */ - Cursor cursor = databaseManager.getCursor(SQLiteUtils.newSQLiteQueryBuilder(), null, false); + Cursor cursor = databaseManager.getCursor(SQLiteUtils.newSQLiteQueryBuilder(), null, null, false); assertEquals(2, cursor.getColumnCount()); - long id = databaseManager.put(oldVersionValue); + long id = databaseManager.put(oldVersionValue, null); /* Put data. */ ContentValues actual = get(databaseManager, id); @@ -296,7 +296,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /* Verify data deleted since no handled upgrade. */ try { - Cursor cursor = databaseManager.getCursor(SQLiteUtils.newSQLiteQueryBuilder(), null, false); + Cursor cursor = databaseManager.getCursor(SQLiteUtils.newSQLiteQueryBuilder(), null, null, false); assertEquals(11, cursor.getColumnCount()); assertEquals(0, databaseManager.getRowCount()); } finally { @@ -329,7 +329,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /* Put data. */ long id; try { - id = databaseManager.put(oldVersionValue); + id = databaseManager.put(oldVersionValue, null); ContentValues actual = get(databaseManager, id); assertNotNull(actual); actual.remove("oid"); @@ -366,7 +366,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { ContentValues data = new ContentValues(); data.put("COL_STRING", "Hello World"); data.put("COL_INT", 2); - id = databaseManager.put(data); + id = databaseManager.put(data, null); actual = get(databaseManager, id); assertNotNull(actual); actual.remove("oid"); diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index b88f1e9658..e0cc90e309 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -173,7 +173,6 @@ public DatabasePersistence(Context context) { * @param version The version of current schema. * @param schema schema. */ - @SuppressWarnings("SameParameterValue") DatabasePersistence(Context context, int version, ContentValues schema) { mContext = context; mPendingDbIdentifiersGroups = new HashMap<>(); @@ -250,7 +249,7 @@ public long putLog(@NonNull Log log, @NonNull String group, @IntRange(from = Fla targetToken = null; } contentValues = getContentValues(group, isLargePayload ? null : payload, targetToken, log.getType(), targetKey, Flags.getPersistenceFlag(flags, false)); - long databaseId = mDatabaseManager.put(contentValues); + long databaseId = mDatabaseManager.put(contentValues, COLUMN_PRIORITY); if (databaseId == -1) { throw new PersistenceException("Failed to store a log to the Persistence database for log type " + log.getType() + "."); } @@ -293,6 +292,7 @@ File getLargePayloadFile(File directory, long databaseId) { } private void deleteLog(File groupLargePayloadDirectory, long id) { + //noinspection ResultOfMethodCallIgnored SQLite delete does not have return type either. getLargePayloadFile(groupLargePayloadDirectory, id).delete(); mDatabaseManager.delete(id); @@ -356,7 +356,7 @@ public int countLogs(@NonNull String group) { builder.appendWhere(COLUMN_GROUP + " = ?"); int count = 0; try { - Cursor cursor = mDatabaseManager.getCursor(builder, new String[]{group}, true); + Cursor cursor = mDatabaseManager.getCursor(builder, new String[]{group}, null, true); try { count = cursor.getCount(); } finally { @@ -399,7 +399,7 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT Cursor cursor = null; ContentValues values; try { - cursor = mDatabaseManager.getCursor(builder, selectionArgs, false); + cursor = mDatabaseManager.getCursor(builder, selectionArgs, null, false); } catch (RuntimeException e) { AppCenterLog.error(LOG_TAG, "Failed to get logs: ", e); } @@ -534,7 +534,7 @@ public void close() { private List getCorruptedIds(SQLiteQueryBuilder builder, String[] selectionArgs) { List result = new ArrayList<>(); try { - Cursor cursor = mDatabaseManager.getCursor(builder, selectionArgs, true); + Cursor cursor = mDatabaseManager.getCursor(builder, selectionArgs, null, true); try { while (cursor.moveToNext()) { ContentValues idValues = mDatabaseManager.buildValues(cursor); diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index 96f246fb75..aa5e5d0532 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -198,11 +198,12 @@ public ContentValues nextValues(Cursor cursor) { * new one can fit. If the log is larger than the max table size, database will be cleared and * the log is not inserted. * - * @param values The entry to be stored. + * @param values The entry to be stored. + * @param priorityColumn When storage full and deleting data,use this order by clause to determine which entries to delete first. * @return If a log was inserted, the database identifier. Otherwise -1. */ @SuppressWarnings("TryFinallyCanBeTryWithResources") - public long put(@NonNull ContentValues values) { + public long put(@NonNull ContentValues values, @NonNull String priorityColumn) { try { while (true) { try { @@ -212,11 +213,21 @@ public long put(@NonNull ContentValues values) { } catch (SQLiteFullException e) { /* Delete the oldest log. */ - Cursor cursor = getCursor(SQLiteUtils.newSQLiteQueryBuilder(), null, true); + int priority = values.getAsInteger(priorityColumn); + + // TODO this boolean for id only is all or nothing which is not efficient, just expose projection directly instead of using a boolean (and make it first parameter, pass through). + Cursor cursor = getCursor(SQLiteUtils.newSQLiteQueryBuilder(), null, priorityColumn, false); try { - if (cursor.moveToNext()) { - delete(cursor.getLong(0)); - } else { + boolean deletedEntry = false; + while (cursor.moveToNext()) { + int existingPriority = cursor.getInt(cursor.getColumnIndex(priorityColumn)); + if (priority >= existingPriority) { + delete(cursor.getLong(0)); + deletedEntry = true; + break; + } + } + if (!deletedEntry) { return -1; } } finally { @@ -311,17 +322,18 @@ public final long getRowCount() { * * @param queryBuilder The query builder that contains SQL query. * @param selectionArgs The array of values for selection. + * @param sortOrder Sorting order (ORDER BY clause without ORDER BY itself). * @param idOnly Return only row identifier if true, return all fields otherwise. * @return A cursor for all rows that matches the given criteria. * @throws RuntimeException If an error occurs. */ - public Cursor getCursor(@Nullable SQLiteQueryBuilder queryBuilder, @Nullable String[] selectionArgs, boolean idOnly) throws RuntimeException { + public Cursor getCursor(@Nullable SQLiteQueryBuilder queryBuilder, @Nullable String[] selectionArgs, @Nullable String sortOrder, boolean idOnly) throws RuntimeException { if (queryBuilder == null) { queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); } queryBuilder.setTables(mTable); String[] projectionIn = idOnly ? new String[]{PRIMARY_KEY} : null; - return queryBuilder.query(getDatabase(), projectionIn, null, selectionArgs, null, null, PRIMARY_KEY); + return queryBuilder.query(getDatabase(), projectionIn, null, selectionArgs, null, null, sortOrder); } /** diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java index 5f4ce5d06e..8d03e62759 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java @@ -92,9 +92,9 @@ public class AbstractAppCenterTest { ApplicationInfo mApplicationInfo; - static void addArgumentToRegistry(String key, String value) { + static void addArgumentToRegistry(String value) { Bundle mockBundle = mock(Bundle.class); - when(mockBundle.getString(key)).thenReturn(value); + when(mockBundle.getString(ServiceInstrumentationUtils.DISABLE_SERVICES)).thenReturn(value); when(InstrumentationRegistryHelper.getArguments()).thenReturn(mockBundle); } @@ -137,7 +137,7 @@ public Void answer(InvocationOnMock invocation) { HandlerThread handlerThread = mock(HandlerThread.class); whenNew(HandlerThread.class).withAnyArguments().thenReturn(handlerThread); when(handlerThread.getLooper()).thenReturn(mock(Looper.class)); - addArgumentToRegistry(ServiceInstrumentationUtils.DISABLE_SERVICES, null); + addArgumentToRegistry(null); /* First call to com.microsoft.appcenter.AppCenter.isEnabled shall return true, initial state. */ when(SharedPreferencesManager.getBoolean(anyString(), eq(true))).thenReturn(true); @@ -160,7 +160,7 @@ public Void answer(InvocationOnMock invocation) { /* Mock empty database. */ DatabaseManager databaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyBoolean())) + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), anyBoolean())) .thenReturn(mock(Cursor.class)); /* Mock network state helper. */ diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterInstrumentationTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterInstrumentationTest.java index 9e970f88be..42131e9e9e 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterInstrumentationTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterInstrumentationTest.java @@ -14,13 +14,13 @@ public class AppCenterInstrumentationTest extends AbstractAppCenterTest { public void disableServicesViaInstrumentation() { /* Verify that when variable is not set, services are started. */ - addArgumentToRegistry(ServiceInstrumentationUtils.DISABLE_SERVICES, null); + addArgumentToRegistry(null); AppCenter.start(mApplication, "app-secret", DummyService.class, AnotherDummyService.class); assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); assertTrue(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); /* Verify that when "All" is set, no service is started. */ - addArgumentToRegistry(ServiceInstrumentationUtils.DISABLE_SERVICES, ServiceInstrumentationUtils.DISABLE_ALL_SERVICES); + addArgumentToRegistry(ServiceInstrumentationUtils.DISABLE_ALL_SERVICES); AppCenter.unsetInstance(); AppCenter.start(mApplication, "app-secret", DummyService.class, AnotherDummyService.class); assertFalse(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); @@ -28,13 +28,13 @@ public void disableServicesViaInstrumentation() { /* Verify single service can be disabled. */ AppCenter.unsetInstance(); - addArgumentToRegistry(ServiceInstrumentationUtils.DISABLE_SERVICES, DummyService.getInstance().getServiceName()); + addArgumentToRegistry(DummyService.getInstance().getServiceName()); AppCenter.start(mApplication, "app-secret", DummyService.class, AnotherDummyService.class); assertFalse(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); assertTrue(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); /* Verify that multiple services can be disabled. */ - addArgumentToRegistry(ServiceInstrumentationUtils.DISABLE_SERVICES, DummyService.getInstance().getServiceName() + addArgumentToRegistry(DummyService.getInstance().getServiceName() + ",anotherService," + AnotherDummyService.getInstance().getServiceName()); AppCenter.unsetInstance(); AppCenter.start(mApplication, "app-secret", DummyService.class, AnotherDummyService.class); @@ -42,7 +42,7 @@ public void disableServicesViaInstrumentation() { assertFalse(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); /* Repeat last test with whitespace. */ - addArgumentToRegistry(ServiceInstrumentationUtils.DISABLE_SERVICES, " " + DummyService.getInstance().getServiceName() + addArgumentToRegistry(" " + DummyService.getInstance().getServiceName() + " , anotherService, " + AnotherDummyService.getInstance().getServiceName() + " "); AppCenter.unsetInstance(); AppCenter.start(mApplication, "app-secret", DummyService.class, AnotherDummyService.class); @@ -97,31 +97,31 @@ public void doNotDisableServicesInNonTestEnvironment() { public void disableServicesFromLibraryViaInstrumentation() { /* Verify that when variable is not set, services are started. */ - addArgumentToRegistry(ServiceInstrumentationUtils.DISABLE_SERVICES, null); + addArgumentToRegistry(null); AppCenter.startFromLibrary(mApplication, DummyService.class); assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); /* Verify that when "All" is set, no service is started. */ - addArgumentToRegistry(ServiceInstrumentationUtils.DISABLE_SERVICES, ServiceInstrumentationUtils.DISABLE_ALL_SERVICES); + addArgumentToRegistry(ServiceInstrumentationUtils.DISABLE_ALL_SERVICES); AppCenter.unsetInstance(); AppCenter.startFromLibrary(mApplication, DummyService.class); assertFalse(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); /* Verify single service can be disabled. */ AppCenter.unsetInstance(); - addArgumentToRegistry(ServiceInstrumentationUtils.DISABLE_SERVICES, DummyService.getInstance().getServiceName()); + addArgumentToRegistry(DummyService.getInstance().getServiceName()); AppCenter.startFromLibrary(mApplication, DummyService.class); assertFalse(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); /* Verify that multiple services can be disabled. */ - addArgumentToRegistry(ServiceInstrumentationUtils.DISABLE_SERVICES, DummyService.getInstance().getServiceName() + addArgumentToRegistry(DummyService.getInstance().getServiceName() + ",anotherService," + AnotherDummyService.getInstance().getServiceName()); AppCenter.unsetInstance(); AppCenter.startFromLibrary(mApplication, DummyService.class); assertFalse(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); /* Repeat last test with whitespace. */ - addArgumentToRegistry(ServiceInstrumentationUtils.DISABLE_SERVICES, " " + DummyService.getInstance().getServiceName() + addArgumentToRegistry(" " + DummyService.getInstance().getServiceName() + " , anotherService, " + AnotherDummyService.getInstance().getServiceName() + " "); AppCenter.unsetInstance(); AppCenter.startFromLibrary(mApplication, DummyService.class); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java index f569551e1c..aa6d49c978 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java @@ -90,7 +90,7 @@ public void countLogsWithGetCountException() throws Exception { whenNew(DatabaseManager.class).withAnyArguments().thenReturn(mockDatabaseManager); Cursor mockCursor = mock(Cursor.class); when(mockCursor.getCount()).thenThrow(new RuntimeException()); - when(mockDatabaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(true))).thenReturn(mockCursor); + when(mockDatabaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(true))).thenReturn(mockCursor); DatabasePersistence persistence = new DatabasePersistence(mock(Context.class), 1, DatabasePersistence.SCHEMA); /* Try to get logs count. */ @@ -137,7 +137,7 @@ public void clearPendingLogState() throws Exception { for (int i = 0; i < groupCount; i++) { MockCursor mockCursor = new MockCursor(list.get(i)); mockCursor.mockBuildValues(mockDatabaseManager); - when(mockDatabaseManager.getCursor(any(SQLiteQueryBuilder.class), eq(new String[]{String.valueOf(i)}), eq(false))) + when(mockDatabaseManager.getCursor(any(SQLiteQueryBuilder.class), eq(new String[]{String.valueOf(i)}), anyString(), eq(false))) .thenReturn(mockCursor); } @@ -171,7 +171,7 @@ public void getLogsWithGetCursorException() throws Exception { DatabaseManager databaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); when(databaseManager.nextValues(any(Cursor.class))).thenCallRealMethod(); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(false))).thenThrow(new RuntimeException()); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(false))).thenThrow(new RuntimeException()); DatabasePersistence persistence = new DatabasePersistence(mock(Context.class), 1, DatabasePersistence.SCHEMA); /* Try to get logs. */ @@ -194,7 +194,7 @@ public void getLogsWithMoveNextException() throws Exception { when(databaseManager.nextValues(any(Cursor.class))).thenCallRealMethod(); Cursor mockCursor = mock(Cursor.class); when(mockCursor.moveToNext()).thenThrow(new RuntimeException()); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(false))).thenReturn(mockCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(false))).thenReturn(mockCursor); DatabasePersistence persistence = new DatabasePersistence(mock(Context.class), 1, DatabasePersistence.SCHEMA); /* Try to get logs. */ @@ -228,12 +228,12 @@ public void getLogsWithGetCorruptedIdsException() throws Exception { /* Mock log sequence retrieved from cursor. */ MockCursor mockCursor = new MockCursor(fieldValues); mockCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(false))).thenReturn(mockCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(false))).thenReturn(mockCursor); /* Mock second cursor with identifiers only. */ Cursor failingCursor = mock(Cursor.class); when(failingCursor.moveToNext()).thenThrow(new SQLiteDiskIOException()); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(true))).thenReturn(failingCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(true))).thenReturn(failingCursor); /* Get logs and verify we get only non corrupted logs. */ DatabasePersistence persistence = new DatabasePersistence(mock(Context.class)); @@ -281,7 +281,7 @@ public void getLogsWithCorruption() throws Exception { /* Mock log sequence retrieved from cursor. */ MockCursor mockCursor = new MockCursor(fieldValues); mockCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(false))).thenReturn(mockCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(false))).thenReturn(mockCursor); /* Mock second cursor with identifiers only. */ List idValues = new ArrayList<>(logCount); @@ -292,7 +292,7 @@ public void getLogsWithCorruption() throws Exception { } MockCursor mockIdCursor = new MockCursor(idValues); mockIdCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(true))).thenReturn(mockIdCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(true))).thenReturn(mockIdCursor); /* Mock serializer and eventually the database. */ LogSerializer logSerializer = mock(LogSerializer.class); @@ -370,7 +370,7 @@ public void close() { } }; mockCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(false))).thenReturn(mockCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(false))).thenReturn(mockCursor); idValues = new ArrayList<>(4); /* Here the id cursor will also skip the new corrupted log which id would be 3. */ @@ -389,7 +389,7 @@ public void close() { } }; mockIdCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(true))).thenReturn(mockIdCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(true))).thenReturn(mockIdCursor); /* Verify next call is only the new valid log as others are marked pending. */ outLogs = new ArrayList<>(); @@ -407,7 +407,7 @@ public void checkSetStorageSizeForwarding() throws Exception { /* The real Android test for checking size is in DatabaseManagerAndroidTest. */ DatabaseManager databaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyBoolean())) + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), anyBoolean())) .thenReturn(mock(Cursor.class)); when(databaseManager.setMaxSize(anyLong())).thenReturn(true).thenReturn(false); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java index 555ef7ffc1..246cefeb77 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java @@ -53,7 +53,7 @@ public void putFailed() { mockStatic(AppCenterLog.class); DatabaseManager databaseManagerMock; databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.put(new ContentValues()); + databaseManagerMock.put(new ContentValues(), null); verifyStatic(); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -172,6 +172,6 @@ public void failsToDeleteLogDuringPutWhenFull() { databaseManager.setSQLiteOpenHelper(helperMock); /* When we put a log, it will fail to purge. */ - assertEquals(-1, databaseManager.put(mock(ContentValues.class))); + assertEquals(-1, databaseManager.put(mock(ContentValues.class), null)); } } \ No newline at end of file From 19f6c5a8fc58d269061b85190c50d6fbd2d01151 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Tue, 30 Oct 2018 19:27:08 -0700 Subject: [PATCH 29/68] Replace idOnly by list of columns to select in database manager --- .../DatabasePersistenceAndroidTest.java | 10 ++++---- .../storage/DatabaseManagerAndroidTest.java | 20 +++++++-------- .../persistence/DatabasePersistence.java | 11 ++++---- .../utils/storage/DatabaseManager.java | 19 ++++++-------- .../appcenter/AbstractAppCenterTest.java | 2 +- .../persistence/DatabasePersistenceTest.java | 25 ++++++++++--------- 6 files changed, 43 insertions(+), 44 deletions(-) diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index c67c9716cb..e06d786996 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -102,7 +102,7 @@ private ContentValues getContentValues(DatabasePersistence persistence, String g SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); builder.appendWhere(DatabasePersistence.COLUMN_GROUP + " = ?"); String[] selectionArgs = new String[]{group}; - Cursor cursor = persistence.mDatabaseManager.getCursor(builder, selectionArgs, null, false); + Cursor cursor = persistence.mDatabaseManager.getCursor(builder, null, selectionArgs, null); ContentValues values = persistence.mDatabaseManager.nextValues(cursor); assertNotNull(values); return values; @@ -670,9 +670,9 @@ public void deleteLogs() throws PersistenceException { builder.appendWhere(DatabasePersistence.COLUMN_GROUP + " = ?"); /* Access DatabaseStorage directly to verify the deletions. */ - Cursor cursor1 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p1"}, null, false); - Cursor cursor2 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p2"}, null, false); - Cursor cursor3 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p3"}, null, false); + Cursor cursor1 = persistence.mDatabaseManager.getCursor(builder, null, new String[]{"test-p1"}, null); + Cursor cursor2 = persistence.mDatabaseManager.getCursor(builder, null, new String[]{"test-p2"}, null); + Cursor cursor3 = persistence.mDatabaseManager.getCursor(builder, null, new String[]{"test-p3"}, null); //noinspection TryFinallyCanBeTryWithResources try { @@ -693,7 +693,7 @@ public void deleteLogs() throws PersistenceException { persistence.deleteLogs("test-p1", id); /* Access DatabaseStorage directly to verify the deletions. */ - Cursor cursor4 = persistence.mDatabaseManager.getCursor(builder, new String[]{"test-p1"}, null, false); + Cursor cursor4 = persistence.mDatabaseManager.getCursor(builder, null, new String[]{"test-p1"}, null); //noinspection TryFinallyCanBeTryWithResources try { diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java index 2d4d4f6e17..2d26065b1e 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java @@ -82,7 +82,7 @@ private static ContentValues get(DatabaseManager databaseManager, long id) { SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); builder.appendWhere(DatabaseManager.PRIMARY_KEY + " = ?"); String[] selectionArgs = new String[]{String.valueOf(id)}; - Cursor cursor = databaseManager.getCursor(builder, selectionArgs, null, false); + Cursor cursor = databaseManager.getCursor(builder, null, selectionArgs, null); try { return databaseManager.nextValues(cursor); } finally { @@ -119,12 +119,12 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { assertNull(nullValueFromDatabase); /* Count with scanner. */ - Cursor cursor = databaseManager.getCursor(null, null, null, false); + Cursor cursor = databaseManager.getCursor(null, null, null, null); assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount()); SQLiteQueryBuilder queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); queryBuilder.appendWhere("COL_STRING = ?"); - Cursor cursor1 = databaseManager.getCursor(queryBuilder, new String[]{value1.getAsString("COL_STRING")}, null, false); + Cursor cursor1 = databaseManager.getCursor(queryBuilder, null, new String[]{value1.getAsString("COL_STRING")}, null); assertEquals(1, cursor1.getCount()); assertTrue(cursor1.moveToNext()); assertContentValuesEquals(value1, databaseManager.buildValues(cursor1)); @@ -133,20 +133,20 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { /* Null value matching. */ queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); queryBuilder.appendWhere("COL_STRING IS NULL"); - assertEquals(0, databaseManager.getCursor(queryBuilder, null, null, false).getCount()); + assertEquals(0, databaseManager.getCursor(queryBuilder, null, null, null).getCount()); queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); queryBuilder.appendWhere("COL_STRING_NULL IS NULL"); - assertEquals(2, databaseManager.getCursor(queryBuilder, null, null, false).getCount()); + assertEquals(2, databaseManager.getCursor(queryBuilder, null, null, null).getCount()); /* Test null value filter does not exclude anything, so returns the 2 logs. */ queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); - cursor = databaseManager.getCursor(queryBuilder, null, null, false); + cursor = databaseManager.getCursor(queryBuilder, null, null, null); assertEquals(2, cursor.getCount()); /* Test filtering only with the second key parameter to get only the second log. */ queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); queryBuilder.appendWhere("COL_STRING NOT IN (?)"); - cursor = databaseManager.getCursor(queryBuilder, new String[]{value1.getAsString("COL_STRING")}, null, false); + cursor = databaseManager.getCursor(queryBuilder, null, new String[]{value1.getAsString("COL_STRING")}, null); assertEquals(1, cursor.getCount()); assertTrue(cursor.moveToNext()); assertContentValuesEquals(value2, databaseManager.buildValues(cursor)); @@ -155,7 +155,7 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { databaseManager.delete(value1Id); assertNull(get(databaseManager, value1Id)); assertEquals(1, databaseManager.getRowCount()); - assertEquals(1, databaseManager.getCursor(null, null, null, false).getCount()); + assertEquals(1, databaseManager.getCursor(null, null, null, null).getCount()); /* Put logs to delete multiple IDs. */ ContentValues value4 = generateContentValues(); @@ -269,7 +269,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { try { /* Database will always create a column for identifiers so default length of all tables is 1. */ - Cursor cursor = databaseManager.getCursor(SQLiteUtils.newSQLiteQueryBuilder(), null, null, false); + Cursor cursor = databaseManager.getCursor(SQLiteUtils.newSQLiteQueryBuilder(), null, null, null); assertEquals(2, cursor.getColumnCount()); long id = databaseManager.put(oldVersionValue, null); @@ -296,7 +296,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /* Verify data deleted since no handled upgrade. */ try { - Cursor cursor = databaseManager.getCursor(SQLiteUtils.newSQLiteQueryBuilder(), null, null, false); + Cursor cursor = databaseManager.getCursor(SQLiteUtils.newSQLiteQueryBuilder(), null, null, null); assertEquals(11, cursor.getColumnCount()); assertEquals(0, databaseManager.getRowCount()); } finally { diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index e0cc90e309..9d245b8f43 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -38,6 +38,7 @@ import static com.microsoft.appcenter.AppCenter.LOG_TAG; import static com.microsoft.appcenter.Flags.PERSISTENCE_NORMAL; +import static com.microsoft.appcenter.utils.storage.DatabaseManager.PRIMARY_KEY; @SuppressWarnings("TryFinallyCanBeTryWithResources") public class DatabasePersistence extends Persistence { @@ -356,7 +357,7 @@ public int countLogs(@NonNull String group) { builder.appendWhere(COLUMN_GROUP + " = ?"); int count = 0; try { - Cursor cursor = mDatabaseManager.getCursor(builder, new String[]{group}, null, true); + Cursor cursor = mDatabaseManager.getCursor(builder, new String[]{PRIMARY_KEY}, new String[]{group}, null); try { count = cursor.getCount(); } finally { @@ -399,14 +400,14 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT Cursor cursor = null; ContentValues values; try { - cursor = mDatabaseManager.getCursor(builder, selectionArgs, null, false); + cursor = mDatabaseManager.getCursor(builder, null, selectionArgs, null); } catch (RuntimeException e) { AppCenterLog.error(LOG_TAG, "Failed to get logs: ", e); } while (cursor != null && (values = mDatabaseManager.nextValues(cursor)) != null && count < limit) { - Long dbIdentifier = values.getAsLong(DatabaseManager.PRIMARY_KEY); + Long dbIdentifier = values.getAsLong(PRIMARY_KEY); /* * When we can't even read the identifier (in this case ContentValues is most likely empty). @@ -534,11 +535,11 @@ public void close() { private List getCorruptedIds(SQLiteQueryBuilder builder, String[] selectionArgs) { List result = new ArrayList<>(); try { - Cursor cursor = mDatabaseManager.getCursor(builder, selectionArgs, null, true); + Cursor cursor = mDatabaseManager.getCursor(builder, new String[]{PRIMARY_KEY}, selectionArgs, null); try { while (cursor.moveToNext()) { ContentValues idValues = mDatabaseManager.buildValues(cursor); - Long invalidId = idValues.getAsLong(DatabaseManager.PRIMARY_KEY); + Long invalidId = idValues.getAsLong(PRIMARY_KEY); result.add(invalidId); } } finally { diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index aa5e5d0532..a2ddb9f76a 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -199,11 +199,11 @@ public ContentValues nextValues(Cursor cursor) { * the log is not inserted. * * @param values The entry to be stored. - * @param priorityColumn When storage full and deleting data,use this order by clause to determine which entries to delete first. + * @param priorityColumn When storage full and deleting data, optionally use this order by clause to determine which entries to delete first. * @return If a log was inserted, the database identifier. Otherwise -1. */ @SuppressWarnings("TryFinallyCanBeTryWithResources") - public long put(@NonNull ContentValues values, @NonNull String priorityColumn) { + public long put(@NonNull ContentValues values, @Nullable String priorityColumn) { try { while (true) { try { @@ -213,14 +213,12 @@ public long put(@NonNull ContentValues values, @NonNull String priorityColumn) { } catch (SQLiteFullException e) { /* Delete the oldest log. */ - int priority = values.getAsInteger(priorityColumn); - - // TODO this boolean for id only is all or nothing which is not efficient, just expose projection directly instead of using a boolean (and make it first parameter, pass through). - Cursor cursor = getCursor(SQLiteUtils.newSQLiteQueryBuilder(), null, priorityColumn, false); + int priority = priorityColumn == null ? 0 : values.getAsInteger(priorityColumn); + Cursor cursor = getCursor(SQLiteUtils.newSQLiteQueryBuilder(), new String[]{PRIMARY_KEY, priorityColumn}, null, priorityColumn); try { boolean deletedEntry = false; while (cursor.moveToNext()) { - int existingPriority = cursor.getInt(cursor.getColumnIndex(priorityColumn)); + int existingPriority = priorityColumn == null ? 0 : cursor.getInt(cursor.getColumnIndex(priorityColumn)); if (priority >= existingPriority) { delete(cursor.getLong(0)); deletedEntry = true; @@ -321,19 +319,18 @@ public final long getRowCount() { * Gets a cursor for all rows in the table, all rows where key matches value if specified. * * @param queryBuilder The query builder that contains SQL query. + * @param columns Columns to select, null for all. * @param selectionArgs The array of values for selection. * @param sortOrder Sorting order (ORDER BY clause without ORDER BY itself). - * @param idOnly Return only row identifier if true, return all fields otherwise. * @return A cursor for all rows that matches the given criteria. * @throws RuntimeException If an error occurs. */ - public Cursor getCursor(@Nullable SQLiteQueryBuilder queryBuilder, @Nullable String[] selectionArgs, @Nullable String sortOrder, boolean idOnly) throws RuntimeException { + public Cursor getCursor(@Nullable SQLiteQueryBuilder queryBuilder, String[] columns, @Nullable String[] selectionArgs, @Nullable String sortOrder) throws RuntimeException { if (queryBuilder == null) { queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); } queryBuilder.setTables(mTable); - String[] projectionIn = idOnly ? new String[]{PRIMARY_KEY} : null; - return queryBuilder.query(getDatabase(), projectionIn, null, selectionArgs, null, null, sortOrder); + return queryBuilder.query(getDatabase(), columns, null, selectionArgs, null, null, sortOrder); } /** diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java index 8d03e62759..30aaefee6f 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java @@ -160,7 +160,7 @@ public Void answer(InvocationOnMock invocation) { /* Mock empty database. */ DatabaseManager databaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), anyBoolean())) + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), any(String[].class), anyString())) .thenReturn(mock(Cursor.class)); /* Mock network state helper. */ diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java index aa6d49c978..73ea172a76 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java @@ -32,10 +32,11 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isNotNull; +import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -90,7 +91,7 @@ public void countLogsWithGetCountException() throws Exception { whenNew(DatabaseManager.class).withAnyArguments().thenReturn(mockDatabaseManager); Cursor mockCursor = mock(Cursor.class); when(mockCursor.getCount()).thenThrow(new RuntimeException()); - when(mockDatabaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(true))).thenReturn(mockCursor); + when(mockDatabaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), any(String[].class), anyString())).thenReturn(mockCursor); DatabasePersistence persistence = new DatabasePersistence(mock(Context.class), 1, DatabasePersistence.SCHEMA); /* Try to get logs count. */ @@ -137,7 +138,7 @@ public void clearPendingLogState() throws Exception { for (int i = 0; i < groupCount; i++) { MockCursor mockCursor = new MockCursor(list.get(i)); mockCursor.mockBuildValues(mockDatabaseManager); - when(mockDatabaseManager.getCursor(any(SQLiteQueryBuilder.class), eq(new String[]{String.valueOf(i)}), anyString(), eq(false))) + when(mockDatabaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(new String[]{String.valueOf(i)}), anyString())) .thenReturn(mockCursor); } @@ -171,7 +172,7 @@ public void getLogsWithGetCursorException() throws Exception { DatabaseManager databaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); when(databaseManager.nextValues(any(Cursor.class))).thenCallRealMethod(); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(false))).thenThrow(new RuntimeException()); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), any(String[].class), anyString())).thenThrow(new RuntimeException()); DatabasePersistence persistence = new DatabasePersistence(mock(Context.class), 1, DatabasePersistence.SCHEMA); /* Try to get logs. */ @@ -194,7 +195,7 @@ public void getLogsWithMoveNextException() throws Exception { when(databaseManager.nextValues(any(Cursor.class))).thenCallRealMethod(); Cursor mockCursor = mock(Cursor.class); when(mockCursor.moveToNext()).thenThrow(new RuntimeException()); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(false))).thenReturn(mockCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), any(String[].class), anyString())).thenReturn(mockCursor); DatabasePersistence persistence = new DatabasePersistence(mock(Context.class), 1, DatabasePersistence.SCHEMA); /* Try to get logs. */ @@ -228,12 +229,12 @@ public void getLogsWithGetCorruptedIdsException() throws Exception { /* Mock log sequence retrieved from cursor. */ MockCursor mockCursor = new MockCursor(fieldValues); mockCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(false))).thenReturn(mockCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNull(String[].class), any(String[].class), anyString())).thenReturn(mockCursor); /* Mock second cursor with identifiers only. */ Cursor failingCursor = mock(Cursor.class); when(failingCursor.moveToNext()).thenThrow(new SQLiteDiskIOException()); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(true))).thenReturn(failingCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNotNull(String[].class), any(String[].class), anyString())).thenReturn(failingCursor); /* Get logs and verify we get only non corrupted logs. */ DatabasePersistence persistence = new DatabasePersistence(mock(Context.class)); @@ -281,7 +282,7 @@ public void getLogsWithCorruption() throws Exception { /* Mock log sequence retrieved from cursor. */ MockCursor mockCursor = new MockCursor(fieldValues); mockCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(false))).thenReturn(mockCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNull(String[].class), any(String[].class), anyString())).thenReturn(mockCursor); /* Mock second cursor with identifiers only. */ List idValues = new ArrayList<>(logCount); @@ -292,7 +293,7 @@ public void getLogsWithCorruption() throws Exception { } MockCursor mockIdCursor = new MockCursor(idValues); mockIdCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(true))).thenReturn(mockIdCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNotNull(String[].class), any(String[].class), anyString())).thenReturn(mockIdCursor); /* Mock serializer and eventually the database. */ LogSerializer logSerializer = mock(LogSerializer.class); @@ -370,7 +371,7 @@ public void close() { } }; mockCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(false))).thenReturn(mockCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNull(String[].class), any(String[].class), anyString())).thenReturn(mockCursor); idValues = new ArrayList<>(4); /* Here the id cursor will also skip the new corrupted log which id would be 3. */ @@ -389,7 +390,7 @@ public void close() { } }; mockIdCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), eq(true))).thenReturn(mockIdCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNotNull(String[].class), any(String[].class), anyString())).thenReturn(mockIdCursor); /* Verify next call is only the new valid log as others are marked pending. */ outLogs = new ArrayList<>(); @@ -407,7 +408,7 @@ public void checkSetStorageSizeForwarding() throws Exception { /* The real Android test for checking size is in DatabaseManagerAndroidTest. */ DatabaseManager databaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), anyString(), anyBoolean())) + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), any(String[].class), anyString())) .thenReturn(mock(Cursor.class)); when(databaseManager.setMaxSize(anyLong())).thenReturn(true).thenReturn(false); From a602a27947e819b48ff0e5f4032b90463b114790 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Tue, 30 Oct 2018 19:37:54 -0700 Subject: [PATCH 30/68] Refactor database purge query code --- .../utils/storage/DatabaseManager.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index a2ddb9f76a..3f53a02790 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -213,19 +213,19 @@ public long put(@NonNull ContentValues values, @Nullable String priorityColumn) } catch (SQLiteFullException e) { /* Delete the oldest log. */ - int priority = priorityColumn == null ? 0 : values.getAsInteger(priorityColumn); - Cursor cursor = getCursor(SQLiteUtils.newSQLiteQueryBuilder(), new String[]{PRIMARY_KEY, priorityColumn}, null, priorityColumn); + Cursor cursor; + if (priorityColumn == null) { + cursor = getCursor(SQLiteUtils.newSQLiteQueryBuilder(), new String[]{PRIMARY_KEY}, null, null); + } else { + String priority = values.getAsString(priorityColumn); + SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); + builder.appendWhere(priorityColumn + " <= ?"); + cursor = getCursor(builder, new String[]{PRIMARY_KEY, priorityColumn}, new String[]{priority}, priorityColumn); + } try { - boolean deletedEntry = false; - while (cursor.moveToNext()) { - int existingPriority = priorityColumn == null ? 0 : cursor.getInt(cursor.getColumnIndex(priorityColumn)); - if (priority >= existingPriority) { - delete(cursor.getLong(0)); - deletedEntry = true; - break; - } - } - if (!deletedEntry) { + if (cursor.moveToNext()) { + delete(cursor.getLong(0)); + } else { return -1; } } finally { From c62b861e786f78c1747b415a4602264eb16d8692 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Tue, 30 Oct 2018 19:59:37 -0700 Subject: [PATCH 31/68] Make priority mandatory in database put to simplify code --- .../storage/DatabaseManagerAndroidTest.java | 18 +++++++++--------- .../utils/storage/DatabaseManager.java | 16 ++++++---------- .../utils/storage/DatabaseManagerTest.java | 4 ++-- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java index 2d26065b1e..187b66785c 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/DatabaseManagerAndroidTest.java @@ -100,11 +100,11 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { ContentValues value3 = generateContentValues(); /* Put. */ - Long value1Id = databaseManager.put(value1, null); + Long value1Id = databaseManager.put(value1, "COL_INTEGER"); assertNotNull(value1Id); /* Put another. */ - Long value2Id = databaseManager.put(value2, null); + Long value2Id = databaseManager.put(value2, "COL_INTEGER"); assertNotNull(value2Id); /* Generate an ID that is neither value1Id nor value2Id. */ @@ -160,8 +160,8 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { /* Put logs to delete multiple IDs. */ ContentValues value4 = generateContentValues(); ContentValues value5 = generateContentValues(); - Long value4Id = databaseManager.put(value4, null); - Long value5Id = databaseManager.put(value5, null); + Long value4Id = databaseManager.put(value4, "COL_INTEGER"); + Long value5Id = databaseManager.put(value5, "COL_INTEGER"); assertNotNull(value4Id); assertNotNull(value5Id); @@ -176,8 +176,8 @@ private static void runDatabaseManagerTest(DatabaseManager databaseManager) { ContentValues value7 = generateContentValues(); value6.put("COL_STRING", value2.getAsString("COL_STRING")); value7.put("COL_STRING", value2.getAsString("COL_STRING") + "A"); - Long value6Id = databaseManager.put(value6, null); - Long value7Id = databaseManager.put(value7, null); + Long value6Id = databaseManager.put(value6, "COL_INTEGER"); + Long value7Id = databaseManager.put(value7, "COL_INTEGER"); assertNotNull(value6Id); assertNotNull(value7Id); @@ -271,7 +271,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /* Database will always create a column for identifiers so default length of all tables is 1. */ Cursor cursor = databaseManager.getCursor(SQLiteUtils.newSQLiteQueryBuilder(), null, null, null); assertEquals(2, cursor.getColumnCount()); - long id = databaseManager.put(oldVersionValue, null); + long id = databaseManager.put(oldVersionValue, "COL_INTEGER"); /* Put data. */ ContentValues actual = get(databaseManager, id); @@ -329,7 +329,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /* Put data. */ long id; try { - id = databaseManager.put(oldVersionValue, null); + id = databaseManager.put(oldVersionValue, "COL_INTEGER"); ContentValues actual = get(databaseManager, id); assertNotNull(actual); actual.remove("oid"); @@ -366,7 +366,7 @@ public boolean onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { ContentValues data = new ContentValues(); data.put("COL_STRING", "Hello World"); data.put("COL_INT", 2); - id = databaseManager.put(data, null); + id = databaseManager.put(data, "COL_INTEGER"); actual = get(databaseManager, id); assertNotNull(actual); actual.remove("oid"); diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index 3f53a02790..4e1d1494c4 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -199,11 +199,11 @@ public ContentValues nextValues(Cursor cursor) { * the log is not inserted. * * @param values The entry to be stored. - * @param priorityColumn When storage full and deleting data, optionally use this order by clause to determine which entries to delete first. + * @param priorityColumn When storage full and deleting data, use this column to determine which entries to delete first. * @return If a log was inserted, the database identifier. Otherwise -1. */ @SuppressWarnings("TryFinallyCanBeTryWithResources") - public long put(@NonNull ContentValues values, @Nullable String priorityColumn) { + public long put(@NonNull ContentValues values, @NonNull String priorityColumn) { try { while (true) { try { @@ -214,14 +214,10 @@ public long put(@NonNull ContentValues values, @Nullable String priorityColumn) /* Delete the oldest log. */ Cursor cursor; - if (priorityColumn == null) { - cursor = getCursor(SQLiteUtils.newSQLiteQueryBuilder(), new String[]{PRIMARY_KEY}, null, null); - } else { - String priority = values.getAsString(priorityColumn); - SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); - builder.appendWhere(priorityColumn + " <= ?"); - cursor = getCursor(builder, new String[]{PRIMARY_KEY, priorityColumn}, new String[]{priority}, priorityColumn); - } + String priority = values.getAsString(priorityColumn); + SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); + builder.appendWhere(priorityColumn + " <= ?"); + cursor = getCursor(builder, new String[]{PRIMARY_KEY, priorityColumn}, new String[]{priority}, priorityColumn); try { if (cursor.moveToNext()) { delete(cursor.getLong(0)); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java index 246cefeb77..509cdba34b 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java @@ -53,7 +53,7 @@ public void putFailed() { mockStatic(AppCenterLog.class); DatabaseManager databaseManagerMock; databaseManagerMock = getDatabaseManagerMock(); - databaseManagerMock.put(new ContentValues(), null); + databaseManagerMock.put(new ContentValues(), "priority"); verifyStatic(); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -172,6 +172,6 @@ public void failsToDeleteLogDuringPutWhenFull() { databaseManager.setSQLiteOpenHelper(helperMock); /* When we put a log, it will fail to purge. */ - assertEquals(-1, databaseManager.put(mock(ContentValues.class), null)); + assertEquals(-1, databaseManager.put(mock(ContentValues.class), "priority")); } } \ No newline at end of file From 92214c065698ba1a50dd8596c32a210781cb74bb Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Tue, 30 Oct 2018 20:00:52 -0700 Subject: [PATCH 32/68] Minor refactoring --- .../microsoft/appcenter/utils/storage/DatabaseManager.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index 4e1d1494c4..78c0275619 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -213,11 +213,10 @@ public long put(@NonNull ContentValues values, @NonNull String priorityColumn) { } catch (SQLiteFullException e) { /* Delete the oldest log. */ - Cursor cursor; String priority = values.getAsString(priorityColumn); - SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder(); - builder.appendWhere(priorityColumn + " <= ?"); - cursor = getCursor(builder, new String[]{PRIMARY_KEY, priorityColumn}, new String[]{priority}, priorityColumn); + SQLiteQueryBuilder queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); + queryBuilder.appendWhere(priorityColumn + " <= ?"); + Cursor cursor = getCursor(queryBuilder, new String[]{PRIMARY_KEY, priorityColumn}, new String[]{priority}, priorityColumn); try { if (cursor.moveToNext()) { delete(cursor.getLong(0)); From 69598b97859a0ed092ea9cccdf08893fb3f08730 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Wed, 31 Oct 2018 11:57:04 -0700 Subject: [PATCH 33/68] Use Long.valueOf in the test for every Long cast --- .../channel/OneCollectorChannelListenerTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java index c6822bcf60..33df314611 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java @@ -124,16 +124,16 @@ public void enqueueConvertedLogs() { verifyNoMoreInteractions(logSerializer); /* Verify flags. */ - assertEquals(new Long(DEFAULT_FLAGS), log1.getFlags()); - assertEquals(new Long(DEFAULT_FLAGS), log2.getFlags()); + assertEquals(Long.valueOf(DEFAULT_FLAGS), log1.getFlags()); + assertEquals(Long.valueOf(DEFAULT_FLAGS), log2.getFlags()); /* Verify same epoch. */ assertNotNull(log1.getExt().getSdk().getEpoch()); assertEquals(log1.getExt().getSdk().getEpoch(), log2.getExt().getSdk().getEpoch()); /* Verify incremented sequence numbers. */ - assertEquals((Long) 1L, log1.getExt().getSdk().getSeq()); - assertEquals((Long) 2L, log2.getExt().getSdk().getSeq()); + assertEquals(Long.valueOf(1), log1.getExt().getSdk().getSeq()); + assertEquals(Long.valueOf(2), log2.getExt().getSdk().getSeq()); /* Verify install ID set. */ assertEquals(installId, log1.getExt().getSdk().getInstallId()); @@ -155,8 +155,8 @@ public void enqueueConvertedLogs() { log3.setExt(ext3); when(logSerializer.toCommonSchemaLog(any(Log.class))).thenReturn(Collections.singletonList(log3)); listener.onPreparedLog(originalLog, TEST_GROUP, PERSISTENCE_CRITICAL); - assertEquals(new Long(PERSISTENCE_CRITICAL), log3.getFlags()); - assertEquals((Long) 1L, log3.getExt().getSdk().getSeq()); + assertEquals(Long.valueOf(PERSISTENCE_CRITICAL), log3.getFlags()); + assertEquals(Long.valueOf(1), log3.getExt().getSdk().getSeq()); assertNotNull(log3.getExt().getSdk().getEpoch()); assertNotEquals(log1.getExt().getSdk().getEpoch(), log3.getExt().getSdk().getEpoch()); @@ -174,8 +174,8 @@ public void enqueueConvertedLogs() { listener.onPreparedLog(originalLog, TEST_GROUP, PERSISTENCE_NORMAL); /* Verify flags and reset of epoch/seq. */ - assertEquals(new Long(PERSISTENCE_NORMAL), log4.getFlags()); - assertEquals((Long) 1L, log4.getExt().getSdk().getSeq()); + assertEquals(Long.valueOf(PERSISTENCE_NORMAL), log4.getFlags()); + assertEquals(Long.valueOf(1), log4.getExt().getSdk().getSeq()); assertNotNull(log4.getExt().getSdk().getEpoch()); assertNotEquals(log3.getExt().getSdk().getEpoch(), log4.getExt().getSdk().getEpoch()); } From ab38841e0e6694ea5e36f402b7e4397048519afe Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Wed, 31 Oct 2018 14:00:06 -0700 Subject: [PATCH 34/68] Extract constant and also optimize count query now that we have column selection --- .../appcenter/persistence/DatabasePersistence.java | 8 +++++--- .../appcenter/utils/storage/DatabaseManager.java | 7 ++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index 9d245b8f43..341fe18c38 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -39,6 +39,7 @@ import static com.microsoft.appcenter.AppCenter.LOG_TAG; import static com.microsoft.appcenter.Flags.PERSISTENCE_NORMAL; import static com.microsoft.appcenter.utils.storage.DatabaseManager.PRIMARY_KEY; +import static com.microsoft.appcenter.utils.storage.DatabaseManager.SELECT_PRIMARY_KEY; @SuppressWarnings("TryFinallyCanBeTryWithResources") public class DatabasePersistence extends Persistence { @@ -357,9 +358,10 @@ public int countLogs(@NonNull String group) { builder.appendWhere(COLUMN_GROUP + " = ?"); int count = 0; try { - Cursor cursor = mDatabaseManager.getCursor(builder, new String[]{PRIMARY_KEY}, new String[]{group}, null); + Cursor cursor = mDatabaseManager.getCursor(builder, new String[]{"COUNT(*)"}, new String[]{group}, null); try { - count = cursor.getCount(); + cursor.moveToNext(); + count = cursor.getInt(0); } finally { cursor.close(); } @@ -535,7 +537,7 @@ public void close() { private List getCorruptedIds(SQLiteQueryBuilder builder, String[] selectionArgs) { List result = new ArrayList<>(); try { - Cursor cursor = mDatabaseManager.getCursor(builder, new String[]{PRIMARY_KEY}, selectionArgs, null); + Cursor cursor = mDatabaseManager.getCursor(builder, SELECT_PRIMARY_KEY, selectionArgs, null); try { while (cursor.moveToNext()) { ContentValues idValues = mDatabaseManager.buildValues(cursor); diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index 78c0275619..dfd380b95f 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -34,6 +34,11 @@ public class DatabaseManager implements Closeable { */ public static final String PRIMARY_KEY = "oid"; + /** + * Primary key selection for {@link #getCursor(SQLiteQueryBuilder, String[], String[], String)}. + */ + public static final String[] SELECT_PRIMARY_KEY = {PRIMARY_KEY}; + /** * Allowed multiple for maximum sizes. */ @@ -216,7 +221,7 @@ public long put(@NonNull ContentValues values, @NonNull String priorityColumn) { String priority = values.getAsString(priorityColumn); SQLiteQueryBuilder queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); queryBuilder.appendWhere(priorityColumn + " <= ?"); - Cursor cursor = getCursor(queryBuilder, new String[]{PRIMARY_KEY, priorityColumn}, new String[]{priority}, priorityColumn); + Cursor cursor = getCursor(queryBuilder, SELECT_PRIMARY_KEY, new String[]{priority}, priorityColumn); try { if (cursor.moveToNext()) { delete(cursor.getLong(0)); From 73bcc3d2cca28d1f0af7664ebac6eab5698e6c83 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Wed, 31 Oct 2018 15:14:18 -0700 Subject: [PATCH 35/68] Fix a unit test --- .../appcenter/persistence/DatabasePersistenceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java index 73ea172a76..e0b76beeb8 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java @@ -90,7 +90,7 @@ public void countLogsWithGetCountException() throws Exception { DatabaseManager mockDatabaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(mockDatabaseManager); Cursor mockCursor = mock(Cursor.class); - when(mockCursor.getCount()).thenThrow(new RuntimeException()); + when(mockCursor.moveToNext()).thenThrow(new RuntimeException()); when(mockDatabaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), any(String[].class), anyString())).thenReturn(mockCursor); DatabasePersistence persistence = new DatabasePersistence(mock(Context.class), 1, DatabasePersistence.SCHEMA); From d615a4a39659cc9a112169a09de27d6d13ee7898 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Wed, 31 Oct 2018 15:35:55 -0700 Subject: [PATCH 36/68] Purge: uses only 1 select command instead of multiple for every delete --- .../utils/storage/DatabaseManager.java | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index dfd380b95f..465912ee38 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -209,34 +209,38 @@ public ContentValues nextValues(Cursor cursor) { */ @SuppressWarnings("TryFinallyCanBeTryWithResources") public long put(@NonNull ContentValues values, @NonNull String priorityColumn) { + Long id = null; + Cursor cursor = null; try { - while (true) { + while (id == null) { try { /* Insert data. */ - return getDatabase().insertOrThrow(mTable, null, values); + id = getDatabase().insertOrThrow(mTable, null, values); } catch (SQLiteFullException e) { /* Delete the oldest log. */ - String priority = values.getAsString(priorityColumn); - SQLiteQueryBuilder queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); - queryBuilder.appendWhere(priorityColumn + " <= ?"); - Cursor cursor = getCursor(queryBuilder, SELECT_PRIMARY_KEY, new String[]{priority}, priorityColumn); - try { - if (cursor.moveToNext()) { - delete(cursor.getLong(0)); - } else { - return -1; - } - } finally { - cursor.close(); + if (cursor == null) { + String priority = values.getAsString(priorityColumn); + SQLiteQueryBuilder queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); + queryBuilder.appendWhere(priorityColumn + " <= ?"); + cursor = getCursor(queryBuilder, SELECT_PRIMARY_KEY, new String[]{priority}, priorityColumn); + } + if (cursor.moveToNext()) { + delete(cursor.getLong(0)); + } else { + throw e; } } } } catch (RuntimeException e) { + id = -1L; AppCenterLog.error(AppCenter.LOG_TAG, String.format("Failed to insert values (%s) to database.", values.toString()), e); } - return -1; + if (cursor != null) { + cursor.close(); + } + return id; } /** From c742f342917fb6e0111cd295602158d47b549a52 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Wed, 31 Oct 2018 16:13:04 -0700 Subject: [PATCH 37/68] Catch cursor close error --- .../utils/storage/DatabaseManager.java | 5 ++- .../utils/storage/DatabaseManagerTest.java | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index 465912ee38..9801010e05 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -238,7 +238,10 @@ public long put(@NonNull ContentValues values, @NonNull String priorityColumn) { AppCenterLog.error(AppCenter.LOG_TAG, String.format("Failed to insert values (%s) to database.", values.toString()), e); } if (cursor != null) { - cursor.close(); + try { + cursor.close(); + } catch (RuntimeException ignore) { + } } return id; } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java index 509cdba34b..8f15b2ae4a 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java @@ -26,6 +26,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -174,4 +175,34 @@ public void failsToDeleteLogDuringPutWhenFull() { /* When we put a log, it will fail to purge. */ assertEquals(-1, databaseManager.put(mock(ContentValues.class), "priority")); } + + @Test + public void cursorFailsToCloseAfterPut() { + + /* Mocking instances. */ + Context contextMock = mock(Context.class); + SQLiteOpenHelper helperMock = mock(SQLiteOpenHelper.class); + SQLiteDatabase sqLiteDatabase = mock(SQLiteDatabase.class); + when(helperMock.getWritableDatabase()).thenReturn(sqLiteDatabase); + + /* Mock the select cursor we are using to find logs to evict to fail. */ + mockStatic(SQLiteUtils.class); + Cursor cursor = mock(Cursor.class); + SQLiteDiskIOException exception = new SQLiteDiskIOException(); + doThrow(exception).when(cursor).close(); + when(cursor.moveToNext()).thenReturn(true).thenReturn(false); + SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class, new Returns(cursor)); + when(SQLiteUtils.newSQLiteQueryBuilder()).thenReturn(sqLiteQueryBuilder); + + /* Simulate that database is full only once (will work after purging 1 log). */ + when(sqLiteDatabase.insertOrThrow(anyString(), anyString(), any(ContentValues.class))).thenThrow(new SQLiteFullException()).thenReturn(1L); + + /* Instantiate real instance for DatabaseManager. */ + DatabaseManager databaseManager = spy(new DatabaseManager(contextMock, "database", "table", 1, null, null)); + databaseManager.setSQLiteOpenHelper(helperMock); + + /* When we put a log, it succeeds even if a problem occurred while closing purge cursor. */ + long id = databaseManager.put(mock(ContentValues.class), "priority"); + assertEquals(1, id); + } } \ No newline at end of file From 9a5c0b275942784e0c82091124ce886cde3125ec Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Wed, 31 Oct 2018 18:18:36 -0700 Subject: [PATCH 38/68] Improve purge test coverage so that 1 test fails if not using order by --- .../persistence/DatabasePersistenceAndroidTest.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index e06d786996..812b067dda 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -397,7 +397,7 @@ public void putTooManyLogs() throws PersistenceException { } @Test - public void putTooManyLogsCritical() throws PersistenceException { + public void putTooManyLogsMixedPriorities() throws PersistenceException { /* Initialize database persistence. */ DatabasePersistence persistence = new DatabasePersistence(sContext); @@ -409,9 +409,9 @@ public void putTooManyLogsCritical() throws PersistenceException { persistence.setLogSerializer(logSerializer); try { - /* Generate 3 critical logs to be kept first. */ - List expectedLogs = new ArrayList<>(); - for (int i = 0; i < 3; i++) { + /* Generate 2 critical logs to be kept first. */ + ArrayList expectedLogs = new ArrayList<>(); + for (int i = 0; i < 2; i++) { MockLog log = AndroidTestUtils.generateMockLog(); persistence.putLog(log, "test-p1", PERSISTENCE_CRITICAL); expectedLogs.add(log); @@ -432,6 +432,11 @@ public void putTooManyLogsCritical() throws PersistenceException { expectedLogs.add(log); } + /* Add one more critical log should clean a normal log first. */ + MockLog log = AndroidTestUtils.generateMockLog(); + persistence.putLog(log, "test-p1", PERSISTENCE_CRITICAL); + expectedLogs.add(log); + /* Get logs from persistence. */ List outputLogs = new ArrayList<>(); persistence.getLogs("test-p1", Collections.emptyList(), 7, outputLogs); From db388d3ea23f30f43f8ed81b8819ade91660c4f3 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Wed, 31 Oct 2018 18:20:08 -0700 Subject: [PATCH 39/68] Implement order by priority when getting logs from storage --- .../DatabasePersistenceAndroidTest.java | 53 +++++++++++++++++-- .../persistence/DatabasePersistence.java | 12 +++-- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index 812b067dda..428ba25c6d 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -411,7 +411,8 @@ public void putTooManyLogsMixedPriorities() throws PersistenceException { /* Generate 2 critical logs to be kept first. */ ArrayList expectedLogs = new ArrayList<>(); - for (int i = 0; i < 2; i++) { + int criticalLogCount = 2; + for (int i = 0; i < criticalLogCount; i++) { MockLog log = AndroidTestUtils.generateMockLog(); persistence.putLog(log, "test-p1", PERSISTENCE_CRITICAL); expectedLogs.add(log); @@ -435,7 +436,7 @@ public void putTooManyLogsMixedPriorities() throws PersistenceException { /* Add one more critical log should clean a normal log first. */ MockLog log = AndroidTestUtils.generateMockLog(); persistence.putLog(log, "test-p1", PERSISTENCE_CRITICAL); - expectedLogs.add(log); + expectedLogs.add(criticalLogCount, log); /* Get logs from persistence. */ List outputLogs = new ArrayList<>(); @@ -785,7 +786,7 @@ public void deleteLogsForGroup() throws PersistenceException { } @Test - public void getLogs() throws PersistenceException { + public void getLogsWithNormalPriority() throws PersistenceException { /* Initialize database persistence. */ DatabasePersistence persistence = new DatabasePersistence(sContext); @@ -846,6 +847,52 @@ private void getAllLogs(DatabasePersistence persistence, int numberOfLogs, int s assertEquals(0, outputLogs.size()); } + @Test + public void getLogsWithMixedPriorities() throws PersistenceException { + + /* Initialize database persistence. */ + DatabasePersistence persistence = new DatabasePersistence(sContext); + + /* Set a mock log serializer. */ + LogSerializer logSerializer = new DefaultLogSerializer(); + logSerializer.addLogFactory(MOCK_LOG_TYPE, new MockLogFactory()); + persistence.setLogSerializer(logSerializer); + try { + + /* Put a normal log. */ + Log log1 = AndroidTestUtils.generateMockLog(); + persistence.putLog(log1, "test", PERSISTENCE_NORMAL); + + /* Put a critical log. */ + Log log2 = AndroidTestUtils.generateMockLog(); + persistence.putLog(log2, "test", PERSISTENCE_CRITICAL); + + /* Put a normal log again. */ + Log log3 = AndroidTestUtils.generateMockLog(); + persistence.putLog(log3, "test", PERSISTENCE_NORMAL); + + /* Put a critical log again. */ + Log log4 = AndroidTestUtils.generateMockLog(); + persistence.putLog(log4, "test", PERSISTENCE_CRITICAL); + + /* Expected order. */ + List expectedLogs = new ArrayList<>(); + expectedLogs.add(log2); + expectedLogs.add(log4); + expectedLogs.add(log1); + expectedLogs.add(log3); + + /* Get logs and check order. */ + List actualLogs = new ArrayList<>(); + persistence.getLogs("test", Collections.emptyList(), expectedLogs.size(), actualLogs); + assertEquals(expectedLogs, actualLogs); + } finally { + + //noinspection ThrowFromFinallyBlock + persistence.close(); + } + } + @Test public void getLogsFilteringOutPausedTargetKeys() throws PersistenceException { diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index 341fe18c38..14dcb0a79a 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -31,10 +31,10 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeMap; import static com.microsoft.appcenter.AppCenter.LOG_TAG; import static com.microsoft.appcenter.Flags.PERSISTENCE_NORMAL; @@ -115,6 +115,11 @@ public class DatabasePersistence extends Persistence { */ private static final int VERSION = 4; + /** + * Order by clause to select logs. + */ + private static final String GET_SORT_ORDER = COLUMN_PRIORITY + " DESC"; + /** * Size limit (in bytes) for a database row log payload. * A separate file is used if payload is larger. @@ -396,13 +401,13 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT /* Add logs to output parameter after deserialization if logs are not already sent. */ int count = 0; - Map candidates = new TreeMap<>(); + Map candidates = new LinkedHashMap<>(); List failedDbIdentifiers = new ArrayList<>(); File largePayloadGroupDirectory = getLargePayloadGroupDirectory(group); Cursor cursor = null; ContentValues values; try { - cursor = mDatabaseManager.getCursor(builder, null, selectionArgs, null); + cursor = mDatabaseManager.getCursor(builder, null, selectionArgs, GET_SORT_ORDER); } catch (RuntimeException e) { AppCenterLog.error(LOG_TAG, "Failed to get logs: ", e); } @@ -499,7 +504,6 @@ public String getLogs(@NonNull String group, @NonNull Collection pausedT /* Log. */ AppCenterLog.debug(LOG_TAG, "Returning " + candidates.size() + " log(s) with an ID, " + id); AppCenterLog.debug(LOG_TAG, "The SID/ID pairs for returning log(s) is/are:"); - List pendingDbIdentifiersGroup = new ArrayList<>(); for (Map.Entry entry : candidates.entrySet()) { Long dbIdentifier = entry.getKey(); From f7736227b1d8f0e3e875d34825c107ed87db365c Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Wed, 31 Oct 2018 18:45:21 -0700 Subject: [PATCH 40/68] Work in progress for trackEvent with flags --- .../appcenter/analytics/Analytics.java | 53 ++++++++++++++++--- .../AnalyticsTransmissionTarget.java | 28 ++++++++-- 2 files changed, 71 insertions(+), 10 deletions(-) diff --git a/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/Analytics.java b/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/Analytics.java index 088cec04f5..399667391c 100644 --- a/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/Analytics.java +++ b/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/Analytics.java @@ -278,7 +278,7 @@ protected static void trackPage(String name, Map properties) { * @param name An event name. */ public static void trackEvent(String name) { - trackEvent(name, null, null); + trackEvent(name, null, null, Flags.DEFAULT_FLAGS); } /** @@ -309,7 +309,7 @@ public static void trackEvent(String name) { * @param properties Optional properties. */ public static void trackEvent(String name, Map properties) { - getInstance().trackEventAsync(name, convertProperties(properties), null); + getInstance().trackEventAsync(name, convertProperties(properties), null, Flags.DEFAULT_FLAGS); } /** @@ -342,14 +342,49 @@ public static void trackEvent(String name, Map properties) { * @param properties Optional properties. */ public static void trackEvent(String name, EventProperties properties) { - trackEvent(name, properties, null); + trackEvent(name, properties, Flags.DEFAULT_FLAGS); + } + + /** + * Track a custom event with name and optional typed properties. + *

+ * The name cannot be null or empty. + *

+ * The property names or values cannot be null. + *

+ * Double values must be finite (NaN or Infinite values are discarded). + *

+ * Additional validation rules apply depending on the configured secret. + *

+ * For App Center: + *

    + *
  • The event name cannot be longer than 256 and is truncated otherwise.
  • + *
  • The property names cannot be empty.
  • + *
  • The property names and values are limited to 125 characters each (truncated).
  • + *
  • The number of properties per event is limited to 20 (truncated).
  • + *
+ *

+ * For One Collector: + *

    + *
  • The event name needs to match the [a-zA-Z0-9]((\.(?!(\.|$)))|[_a-zA-Z0-9]){3,99} regular expression.
  • + *
  • The baseData and baseDataType properties are reserved and thus discarded.
  • + *
  • The full event size when encoded as a JSON string cannot be larger than 1.9MB.
  • + *
+ * + * @param name An event name. + * @param properties Optional properties. + * @param flags Optional flags. Use {@link Flags#PERSISTENCE_CRITICAL} to send this event + * before events using that use default flags or {@link Flags#PERSISTENCE_NORMAL}. + */ + public static void trackEvent(String name, EventProperties properties, int flags) { + trackEvent(name, properties, null, flags); } /** * Internal method redirection for trackEvent. */ - static void trackEvent(String name, EventProperties properties, AnalyticsTransmissionTarget transmissionTarget) { - getInstance().trackEventAsync(name, convertProperties(properties), transmissionTarget); + static void trackEvent(String name, EventProperties properties, AnalyticsTransmissionTarget transmissionTarget, int flags) { + getInstance().trackEventAsync(name, convertProperties(properties), transmissionTarget, flags); } /** @@ -672,8 +707,9 @@ private void queuePage(String name, Map properties) { * @param name event name. * @param properties optional properties. * @param transmissionTarget optional target. + * @param flags optional flags. */ - private synchronized void trackEventAsync(final String name, final List properties, final AnalyticsTransmissionTarget transmissionTarget) { + private synchronized void trackEventAsync(final String name, final List properties, final AnalyticsTransmissionTarget transmissionTarget, final int flags) { post(new Runnable() { @Override @@ -695,7 +731,10 @@ public void run() { eventLog.setId(UUIDUtils.randomUUID()); eventLog.setName(name); eventLog.setTypedProperties(properties); - mChannel.enqueue(eventLog, ANALYTICS_GROUP, Flags.DEFAULT_FLAGS); + + /* Filter and validate flags. For now we support only persistence. */ + int filteredFlags = Flags.getPersistenceFlag(flags, true); + mChannel.enqueue(eventLog, ANALYTICS_GROUP, filteredFlags); } }); } diff --git a/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTarget.java b/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTarget.java index f6914f398c..cae7c2e1c6 100644 --- a/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTarget.java +++ b/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTarget.java @@ -6,6 +6,7 @@ import android.support.annotation.WorkerThread; import com.microsoft.appcenter.AppCenter; +import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.channel.AbstractChannelListener; import com.microsoft.appcenter.channel.Channel; import com.microsoft.appcenter.ingestion.models.Log; @@ -153,7 +154,7 @@ private static void updateProvider(AuthenticationProvider authenticationProvider * @param name An event name. */ public void trackEvent(String name) { - trackEvent(name, (EventProperties) null); + trackEvent(name, null, Flags.DEFAULT_FLAGS); } /** @@ -178,7 +179,7 @@ public void trackEvent(String name, Map properties) { eventProperties.set(entry.getKey(), entry.getValue()); } } - trackEvent(name, eventProperties); + trackEvent(name, eventProperties, Flags.DEFAULT_FLAGS); } /** @@ -197,6 +198,27 @@ public void trackEvent(String name, Map properties) { * @param properties Optional properties. */ public void trackEvent(String name, EventProperties properties) { + trackEvent(name, properties, Flags.DEFAULT_FLAGS); + } + + /** + * Track a custom event with name and optional string properties. + *

+ * The following rules apply: + *

    + *
  • The event name needs to match the [a-zA-Z0-9]((\.(?!(\.|$)))|[_a-zA-Z0-9]){3,99} regular expression.
  • + *
  • The property names or values cannot be null.
  • + *
  • Double values must be finite (NaN or Infinite values are discarded).
  • + *
  • The baseData and baseDataType properties are reserved and thus discarded.
  • + *
  • The full event size when encoded as a JSON string cannot be larger than 1.9MB.
  • + *
+ * + * @param name An event name. + * @param properties Optional properties. + * @param flags Optional flags. Use {@link Flags#PERSISTENCE_CRITICAL} to send this event + * before events using that use default flags or {@link Flags#PERSISTENCE_NORMAL}. + */ + public void trackEvent(String name, EventProperties properties, int flags) { /* Merge common properties. More specific target wins conflicts. */ EventProperties mergedProperties = new EventProperties(); @@ -218,7 +240,7 @@ else if (mergedProperties.getProperties().isEmpty()) { } /* Track event with merged properties. */ - Analytics.trackEvent(name, mergedProperties, this); + Analytics.trackEvent(name, mergedProperties, this, flags); } /** From 99ea488636a870f6e829abef3815ddf4cbfb4348 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Thu, 1 Nov 2018 17:40:08 -0700 Subject: [PATCH 41/68] Add order by id to be sure in purge logs --- .../com/microsoft/appcenter/utils/storage/DatabaseManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java index 9801010e05..e67ee5ab37 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/storage/DatabaseManager.java @@ -224,7 +224,7 @@ public long put(@NonNull ContentValues values, @NonNull String priorityColumn) { String priority = values.getAsString(priorityColumn); SQLiteQueryBuilder queryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); queryBuilder.appendWhere(priorityColumn + " <= ?"); - cursor = getCursor(queryBuilder, SELECT_PRIMARY_KEY, new String[]{priority}, priorityColumn); + cursor = getCursor(queryBuilder, SELECT_PRIMARY_KEY, new String[]{priority}, priorityColumn + " , " + PRIMARY_KEY); } if (cursor.moveToNext()) { delete(cursor.getLong(0)); From 5b4bc45a0db7f399807b8113f5a3691b6f15abe2 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Thu, 1 Nov 2018 17:45:54 -0700 Subject: [PATCH 42/68] Add one more test to address feedbacks --- .../DatabasePersistenceAndroidTest.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index 812b067dda..e2330f5dd8 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -607,6 +607,48 @@ public void putNormalLogLargerThanMaxSizeKeepsCritical() throws PersistenceExcep } } + @Test + public void putNormalLogFailsIfFullOfCritical() throws PersistenceException { + + /* Initialize database persistence. */ + DatabasePersistence persistence = new DatabasePersistence(sContext); + assertTrue(persistence.setMaxStorageSize(MAX_STORAGE_SIZE_IN_BYTES)); + + /* Set a mock log serializer. */ + LogSerializer logSerializer = new DefaultLogSerializer(); + logSerializer.addLogFactory(MOCK_LOG_TYPE, new MockLogFactory()); + persistence.setLogSerializer(logSerializer); + try { + + /* Fill storage with critical logs. */ + List expectedLogs = new ArrayList<>(); + int someLogCount = 6; + for (int i = 0; i < someLogCount; i++) { + MockLog log = AndroidTestUtils.generateMockLog(); + persistence.putLog(log, "test-p1", PERSISTENCE_CRITICAL); + expectedLogs.add(log); + } + assertEquals(someLogCount, persistence.countLogs("test-p1")); + + /* Try to insert a normal log: that will fail and no log deleted. */ + try { + persistence.putLog(AndroidTestUtils.generateMockLog(), "test-p1", PERSISTENCE_NORMAL); + fail("Put log was supposed to fail."); + } catch (PersistenceException ignore) { + } + + /* Get logs from persistence: critical were kept. */ + List outputLogs = new ArrayList<>(); + persistence.getLogs("test-p1", Collections.emptyList(), 7, outputLogs); + assertEquals(expectedLogs.size(), persistence.countLogs("test-p1")); + assertEquals(expectedLogs, outputLogs); + } finally { + + //noinspection ThrowFromFinallyBlock + persistence.close(); + } + } + @Test(expected = PersistenceException.class) public void putLogException() throws PersistenceException, JSONException { From b83bbc3e126963c3b500eac48123a45ed4b0f049 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Thu, 1 Nov 2018 17:48:50 -0700 Subject: [PATCH 43/68] Add primary key in sorting to make sure --- .../microsoft/appcenter/persistence/DatabasePersistence.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java index 14dcb0a79a..c37afa0759 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java @@ -118,7 +118,7 @@ public class DatabasePersistence extends Persistence { /** * Order by clause to select logs. */ - private static final String GET_SORT_ORDER = COLUMN_PRIORITY + " DESC"; + private static final String GET_SORT_ORDER = COLUMN_PRIORITY + " DESC, " + PRIMARY_KEY; /** * Size limit (in bytes) for a database row log payload. From 3a9cc5f3aa3357aab6a7ed7f4496509980d6c232 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Thu, 1 Nov 2018 18:11:56 -0700 Subject: [PATCH 44/68] Add unit tests --- .../appcenter/analytics/AnalyticsTest.java | 37 ++++++++++++++++++- .../AnalyticsTransmissionTargetTest.java | 25 +++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java index 3a70fb73ee..998baa1141 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java @@ -44,6 +44,8 @@ import java.util.concurrent.TimeUnit; import static com.microsoft.appcenter.Flags.DEFAULT_FLAGS; +import static com.microsoft.appcenter.Flags.PERSISTENCE_CRITICAL; +import static com.microsoft.appcenter.Flags.PERSISTENCE_NORMAL; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -99,12 +101,13 @@ public void notInit() { Analytics.trackEvent("test", new HashMap()); Analytics.trackEvent("test", (Map) null); Analytics.trackEvent("test", (EventProperties) null); + Analytics.trackEvent("test", null, 0); Analytics.trackPage("test"); Analytics.trackPage("test", new HashMap()); Analytics.trackPage("test", null); /* Verify we just get an error every time. */ - verifyStatic(times(7)); + verifyStatic(times(8)); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString()); } @@ -328,6 +331,38 @@ public void trackEventFromAppWithEventProperties() { assertEquals(booleanTypedProperty, argumentCaptor.getValue().getTypedProperties().get(4)); } + @Test + public void trackEventWithNormalPersistenceFlag() { + Analytics analytics = Analytics.getInstance(); + Channel channel = mock(Channel.class); + analytics.onStarting(mAppCenterHandler); + analytics.onStarted(mock(Context.class), channel, "", null, true); + Analytics.trackEvent("eventName", null, PERSISTENCE_NORMAL); + verify(channel).enqueue(isA(EventLog.class), anyString(), eq(PERSISTENCE_NORMAL)); + } + + @Test + public void trackEventWithNormalCriticalPersistenceFlag() { + Analytics analytics = Analytics.getInstance(); + Channel channel = mock(Channel.class); + analytics.onStarting(mAppCenterHandler); + analytics.onStarted(mock(Context.class), channel, "", null, true); + Analytics.trackEvent("eventName", null, PERSISTENCE_CRITICAL); + verify(channel).enqueue(isA(EventLog.class), anyString(), eq(PERSISTENCE_CRITICAL)); + } + + @Test + public void trackEventWithInvalidFlags() { + Analytics analytics = Analytics.getInstance(); + Channel channel = mock(Channel.class); + analytics.onStarting(mAppCenterHandler); + analytics.onStarted(mock(Context.class), channel, "", null, true); + Analytics.trackEvent("eventName", null, 0x03); + verify(channel).enqueue(isA(EventLog.class), anyString(), eq(DEFAULT_FLAGS)); + verifyStatic(); + AppCenterLog.warn(eq(AppCenter.LOG_TAG), anyString()); + } + @Test public void trackEventFromLibrary() { Analytics analytics = Analytics.getInstance(); diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java index 26daabe257..40ab8646d1 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java @@ -29,6 +29,8 @@ import java.util.List; import static com.microsoft.appcenter.Flags.DEFAULT_FLAGS; +import static com.microsoft.appcenter.Flags.PERSISTENCE_CRITICAL; +import static com.microsoft.appcenter.Flags.PERSISTENCE_NORMAL; import static com.microsoft.appcenter.analytics.Analytics.ANALYTICS_GROUP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -601,4 +603,27 @@ public void pauseResume() { verify(mChannel, never()).pauseGroup(anyString(), anyString()); verify(mChannel, never()).resumeGroup(anyString(), anyString()); } + + @Test + public void trackEventWithNormalPersistenceFlag() { + AnalyticsTransmissionTarget target = Analytics.getTransmissionTarget("token"); + target.trackEvent("eventName", null, PERSISTENCE_NORMAL); + verify(mChannel).enqueue(isA(EventLog.class), anyString(), eq(PERSISTENCE_NORMAL)); + } + + @Test + public void trackEventWithNormalCriticalPersistenceFlag() { + AnalyticsTransmissionTarget target = Analytics.getTransmissionTarget("token"); + target.trackEvent("eventName", null, PERSISTENCE_CRITICAL); + verify(mChannel).enqueue(isA(EventLog.class), anyString(), eq(PERSISTENCE_CRITICAL)); + } + + @Test + public void trackEventWithInvalidFlags() { + AnalyticsTransmissionTarget target = Analytics.getTransmissionTarget("token"); + target.trackEvent("eventName", null, 0x03); + verify(mChannel).enqueue(isA(EventLog.class), anyString(), eq(DEFAULT_FLAGS)); + verifyStatic(); + AppCenterLog.warn(eq(AppCenter.LOG_TAG), anyString()); + } } From c2c9329c1202327c7a351657f0efde242a6df002 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Thu, 1 Nov 2018 19:05:08 -0700 Subject: [PATCH 45/68] Add UI for trackEvent with flags --- .../sasquatch/activities/EventActivity.java | 8 + .../src/main/res/layout/activity_event.xml | 13 + .../sasquatch/src/main/res/values/strings.xml | 7 + .../sasquatch/activities/EventActivity.java | 293 ++++++++++++++++++ 4 files changed, 321 insertions(+) rename apps/sasquatch/src/{main => jcenterDependency}/java/com/microsoft/appcenter/sasquatch/activities/EventActivity.java (96%) create mode 100644 apps/sasquatch/src/projectDependency/java/com/microsoft/appcenter/sasquatch/activities/EventActivity.java diff --git a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/EventActivity.java b/apps/sasquatch/src/jcenterDependency/java/com/microsoft/appcenter/sasquatch/activities/EventActivity.java similarity index 96% rename from apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/EventActivity.java rename to apps/sasquatch/src/jcenterDependency/java/com/microsoft/appcenter/sasquatch/activities/EventActivity.java index fd84e18d29..76861fb21d 100644 --- a/apps/sasquatch/src/main/java/com/microsoft/appcenter/sasquatch/activities/EventActivity.java +++ b/apps/sasquatch/src/jcenterDependency/java/com/microsoft/appcenter/sasquatch/activities/EventActivity.java @@ -29,6 +29,10 @@ import java.util.Map; import java.util.Set; + +/** + * TODO delete this version and move projectDependency version to main folder during release process. + */ public class EventActivity extends AppCompatActivity { /** @@ -135,6 +139,10 @@ public void onClick(View v) { getSelectedTarget().resume(); } }); + + /* Hide persistence flag UI as not supported by jCenter */ + findViewById(R.id.persistence_flag_label).setVisibility(View.GONE); + findViewById(R.id.persistence_flag_spinner).setVisibility(View.GONE); } diff --git a/apps/sasquatch/src/main/res/layout/activity_event.xml b/apps/sasquatch/src/main/res/layout/activity_event.xml index 1266db6de5..4f86b7426c 100644 --- a/apps/sasquatch/src/main/res/layout/activity_event.xml +++ b/apps/sasquatch/src/main/res/layout/activity_event.xml @@ -74,6 +74,19 @@ android:onClick="toggleDeviceIdEnabled" android:visibility="gone" /> + + + + diff --git a/apps/sasquatch/src/main/res/values/strings.xml b/apps/sasquatch/src/main/res/values/strings.xml index d2ce69e0c4..545e389afd 100644 --- a/apps/sasquatch/src/main/res/values/strings.xml +++ b/apps/sasquatch/src/main/res/values/strings.xml @@ -76,4 +76,11 @@ Max storage size has changed to %s successfully. Check SDK logs for actual size of storage. Failed to change max storage size. Size in bytes + Persistence Flag + + Default + Normal + Critical + Invalid + diff --git a/apps/sasquatch/src/projectDependency/java/com/microsoft/appcenter/sasquatch/activities/EventActivity.java b/apps/sasquatch/src/projectDependency/java/com/microsoft/appcenter/sasquatch/activities/EventActivity.java new file mode 100644 index 0000000000..dcae76f496 --- /dev/null +++ b/apps/sasquatch/src/projectDependency/java/com/microsoft/appcenter/sasquatch/activities/EventActivity.java @@ -0,0 +1,293 @@ +package com.microsoft.appcenter.sasquatch.activities; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.Spinner; +import android.widget.TextView; + +import com.microsoft.appcenter.AppCenter; +import com.microsoft.appcenter.Flags; +import com.microsoft.appcenter.analytics.Analytics; +import com.microsoft.appcenter.analytics.AnalyticsTransmissionTarget; +import com.microsoft.appcenter.analytics.EventProperties; +import com.microsoft.appcenter.sasquatch.R; +import com.microsoft.appcenter.sasquatch.fragments.TypedPropertyFragment; +import com.microsoft.appcenter.sasquatch.util.EventActivityUtil; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +/** + * TODO move to main resource folder and delete jCenter version during release process. + */ +public class EventActivity extends AppCompatActivity { + + /** + * Remember for what targets the device id was enabled. + * It shouldn't be lost on recreate activity. + */ + private static final Set DEVICE_ID_ENABLED = new HashSet<>(); + private final List mProperties = new ArrayList<>(); + private Spinner mTransmissionTargetSpinner; + private CheckBox mTransmissionEnabledCheckBox; + private CheckBox mDeviceIdEnabledCheckBox; + private Button mConfigureTargetPropertiesButton; + private Button mOverrideCommonSchemaButton; + private Button mPauseTransmissionButton; + private Button mResumeTransmissionButton; + private Spinner mPersistenceFlagSpinner; + private List mTransmissionTargets = new ArrayList<>(); + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_event); + + /* Test start from library. */ + AppCenter.startFromLibrary(this, Analytics.class); + + /* Transmission target views init. */ + mTransmissionTargetSpinner = findViewById(R.id.transmission_target); + ArrayAdapter targetAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, getResources().getStringArray(R.array.target_id_names)); + mTransmissionTargetSpinner.setAdapter(targetAdapter); + mTransmissionTargetSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + updateButtonStates(getSelectedTarget()); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + + /* Init Configure target properties button. */ + mConfigureTargetPropertiesButton = findViewById(R.id.configure_button); + mConfigureTargetPropertiesButton.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + Intent intent = new Intent(EventActivity.this, EventPropertiesActivity.class); + intent.putExtra(ActivityConstants.EXTRA_TARGET_SELECTED, mTransmissionTargetSpinner.getSelectedItemPosition() - 1); + startActivity(intent); + } + }); + + /* Init enabled check boxes. */ + mTransmissionEnabledCheckBox = findViewById(R.id.transmission_enabled); + mDeviceIdEnabledCheckBox = findViewById(R.id.device_id_enabled); + + /* + * The first element is a placeholder for default transmission. + * The second one is the parent transmission target, the third one is a child, + * the forth is a grandchild, etc... + */ + mTransmissionTargets = EventActivityUtil.getAnalyticTransmissionTargetList(this); + + /* Init common schema properties button. */ + mOverrideCommonSchemaButton = findViewById(R.id.override_cs_button); + + /* Init override common schema properties button. */ + mOverrideCommonSchemaButton.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + Intent intent = new Intent(EventActivity.this, CommonSchemaPropertiesActivity.class); + intent.putExtra(ActivityConstants.EXTRA_TARGET_SELECTED, mTransmissionTargetSpinner.getSelectedItemPosition()); + startActivity(intent); + } + }); + + /* Init pause/resume buttons. */ + mPauseTransmissionButton = findViewById(R.id.pause_transmission_button); + mPauseTransmissionButton.setOnClickListener(new View.OnClickListener() { + + @Override + @SuppressWarnings("ConstantConditions") + public void onClick(View v) { + getSelectedTarget().pause(); + } + }); + mResumeTransmissionButton = findViewById(R.id.resume_transmission_button); + mResumeTransmissionButton.setOnClickListener(new View.OnClickListener() { + + @Override + @SuppressWarnings("ConstantConditions") + public void onClick(View v) { + getSelectedTarget().resume(); + } + }); + + /* Persistence flag. */ + mPersistenceFlagSpinner = findViewById(R.id.persistence_flag_spinner); + ArrayAdapter persistenceFlagAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, getResources().getStringArray(R.array.persistence_flag_values)); + mPersistenceFlagSpinner.setAdapter(persistenceFlagAdapter); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.add, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_add: + addProperty(); + break; + } + return true; + } + + private void addProperty() { + TypedPropertyFragment fragment = new TypedPropertyFragment(); + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.add(R.id.list, fragment).commit(); + mProperties.add(fragment); + } + + private boolean onlyStringProperties() { + for (TypedPropertyFragment fragment : mProperties) { + if (fragment.getType() != TypedPropertyFragment.EventPropertyType.STRING) { + return false; + } + } + return true; + } + + @SuppressWarnings("unused") + public void send(@SuppressWarnings("UnusedParameters") View view) { + PersistenceFlag persistenceFlag = PersistenceFlag.values()[mPersistenceFlagSpinner.getSelectedItemPosition()]; + int flags = getFlags(persistenceFlag); + String name = ((TextView) findViewById(R.id.name)).getText().toString(); + Map properties = null; + EventProperties typedProperties = null; + if (mProperties.size() > 0) { + if (onlyStringProperties() && persistenceFlag == PersistenceFlag.DEFAULT) { + properties = new HashMap<>(); + for (TypedPropertyFragment fragment : mProperties) { + fragment.set(properties); + } + } else { + typedProperties = new EventProperties(); + for (TypedPropertyFragment fragment : mProperties) { + fragment.set(typedProperties); + } + } + } + + /* First item is always empty as it's default value which means either AppCenter, one collector or both. */ + AnalyticsTransmissionTarget target = getSelectedTarget(); + if (target == null) { + if (persistenceFlag != PersistenceFlag.DEFAULT) { + Analytics.trackEvent(name, typedProperties, flags); + } else if (typedProperties != null) { + Analytics.trackEvent(name, typedProperties); + } else if (properties != null) { + Analytics.trackEvent(name, properties); + } else { + Analytics.trackEvent(name); + } + } else { + if (persistenceFlag != PersistenceFlag.DEFAULT) { + target.trackEvent(name, typedProperties, flags); + } else if (typedProperties != null) { + target.trackEvent(name, typedProperties); + } else if (properties != null) { + target.trackEvent(name, properties); + } else { + target.trackEvent(name); + } + } + } + + private int getFlags(PersistenceFlag persistenceFlag) { + switch (persistenceFlag) { + case DEFAULT: + return Flags.getPersistenceFlag(Flags.DEFAULT_FLAGS, true); + + case NORMAL: + return Flags.PERSISTENCE_NORMAL; + + case CRITICAL: + return Flags.PERSISTENCE_CRITICAL; + + case INVALID: + return Flags.PERSISTENCE_CRITICAL + 1; + } + throw new IllegalArgumentException(); + } + + private AnalyticsTransmissionTarget getSelectedTarget() { + return mTransmissionTargets.get(mTransmissionTargetSpinner.getSelectedItemPosition()); + } + + public void toggleTransmissionEnabled(View view) { + boolean checked = mTransmissionEnabledCheckBox.isChecked(); + final AnalyticsTransmissionTarget target = getSelectedTarget(); + if (target != null) { + target.setEnabledAsync(checked); + updateButtonStates(target); + } + } + + public void toggleDeviceIdEnabled(View view) { + final AnalyticsTransmissionTarget target = getSelectedTarget(); + if (target != null) { + updateButtonStates(target); + target.getPropertyConfigurator().collectDeviceId(); + mDeviceIdEnabledCheckBox.setChecked(true); + mDeviceIdEnabledCheckBox.setText(R.string.device_id_enabled); + mDeviceIdEnabledCheckBox.setEnabled(false); + DEVICE_ID_ENABLED.add(target); + } + } + + private void updateButtonStates(AnalyticsTransmissionTarget target) { + if (target == null) { + mTransmissionEnabledCheckBox.setVisibility(View.GONE); + mConfigureTargetPropertiesButton.setVisibility(View.GONE); + mOverrideCommonSchemaButton.setVisibility(View.GONE); + mDeviceIdEnabledCheckBox.setVisibility(View.GONE); + mPauseTransmissionButton.setVisibility(View.GONE); + mResumeTransmissionButton.setVisibility(View.GONE); + } else { + mTransmissionEnabledCheckBox.setVisibility(View.VISIBLE); + mConfigureTargetPropertiesButton.setVisibility(View.VISIBLE); + mOverrideCommonSchemaButton.setVisibility(View.VISIBLE); + mPauseTransmissionButton.setVisibility(View.VISIBLE); + mResumeTransmissionButton.setVisibility(View.VISIBLE); + boolean enabled = target.isEnabledAsync().get(); + mTransmissionEnabledCheckBox.setChecked(enabled); + mTransmissionEnabledCheckBox.setText(enabled ? R.string.transmission_enabled : R.string.transmission_disabled); + boolean deviceIdEnabled = DEVICE_ID_ENABLED.contains(target); + mDeviceIdEnabledCheckBox.setVisibility(View.VISIBLE); + mDeviceIdEnabledCheckBox.setChecked(deviceIdEnabled); + mDeviceIdEnabledCheckBox.setText(deviceIdEnabled ? R.string.device_id_enabled : R.string.device_id_disabled); + mDeviceIdEnabledCheckBox.setEnabled(!deviceIdEnabled); + } + } + + private enum PersistenceFlag { + DEFAULT, + NORMAL, + CRITICAL, + INVALID + } +} From 832925812400c746c85f1b0d7d816334a2997570 Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Thu, 1 Nov 2018 19:18:07 -0700 Subject: [PATCH 46/68] Fix scrolling issues in event page now that have a lot of controls --- .../src/main/res/layout/activity_event.xml | 162 ++++++++++-------- .../main/res/layout/activity_log_bottom.xml | 28 --- .../src/main/res/layout/activity_page.xml | 24 ++- 3 files changed, 118 insertions(+), 96 deletions(-) delete mode 100644 apps/sasquatch/src/main/res/layout/activity_log_bottom.xml diff --git a/apps/sasquatch/src/main/res/layout/activity_event.xml b/apps/sasquatch/src/main/res/layout/activity_event.xml index 4f86b7426c..ef677f0b3b 100644 --- a/apps/sasquatch/src/main/res/layout/activity_event.xml +++ b/apps/sasquatch/src/main/res/layout/activity_event.xml @@ -9,84 +9,112 @@ - + android:layout_height="0dp" + android:layout_weight="1"> - + -