deleted file mode 100644
--- a/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/db/TestClientsDatabase.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-package org.mozilla.gecko.background.db;
-
-import java.util.ArrayList;
-
-import org.json.simple.JSONArray;
-import org.mozilla.gecko.sync.Utils;
-import org.mozilla.gecko.sync.repositories.NullCursorException;
-import org.mozilla.gecko.sync.repositories.android.ClientsDatabase;
-import org.mozilla.gecko.sync.repositories.android.RepoUtils;
-import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
-import org.mozilla.gecko.sync.setup.Constants;
-
-import android.database.Cursor;
-import android.test.AndroidTestCase;
-
-public class TestClientsDatabase extends AndroidTestCase {
-
- protected ClientsDatabase db;
-
- public void setUp() {
- db = new ClientsDatabase(mContext);
- db.wipeDB();
- }
-
- public void testStoreAndFetch() {
- ClientRecord record = new ClientRecord();
- String profileConst = Constants.DEFAULT_PROFILE;
- db.store(profileConst, record);
-
- Cursor cur = null;
- try {
- // Test stored item gets fetched correctly.
- cur = db.fetchClientsCursor(record.guid, profileConst);
- assertTrue(cur.moveToFirst());
- assertEquals(1, cur.getCount());
-
- String guid = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_ACCOUNT_GUID);
- String profileId = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_PROFILE);
- String clientName = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_NAME);
- String clientType = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_TYPE);
-
- assertEquals(record.guid, guid);
- assertEquals(profileConst, profileId);
- assertEquals(record.name, clientName);
- assertEquals(record.type, clientType);
- } catch (NullCursorException e) {
- fail("Should not have NullCursorException");
- } finally {
- if (cur != null) {
- cur.close();
- }
- }
- }
-
- public void testStoreAndFetchSpecificCommands() {
- String accountGUID = Utils.generateGuid();
- ArrayList<String> args = new ArrayList<String>();
- args.add("URI of Page");
- args.add("Sender GUID");
- args.add("Title of Page");
- String jsonArgs = JSONArray.toJSONString(args);
-
- Cursor cur = null;
- try {
- db.store(accountGUID, "displayURI", jsonArgs);
-
- // This row should not show up in the fetch.
- args.add("Another arg.");
- db.store(accountGUID, "displayURI", JSONArray.toJSONString(args));
-
- // Test stored item gets fetched correctly.
- cur = db.fetchSpecificCommand(accountGUID, "displayURI", jsonArgs);
- assertTrue(cur.moveToFirst());
- assertEquals(1, cur.getCount());
-
- String guid = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_ACCOUNT_GUID);
- String commandType = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_COMMAND);
- String fetchedArgs = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_ARGS);
-
- assertEquals(accountGUID, guid);
- assertEquals("displayURI", commandType);
- assertEquals(jsonArgs, fetchedArgs);
- } catch (NullCursorException e) {
- fail("Should not have NullCursorException");
- } finally {
- if (cur != null) {
- cur.close();
- }
- }
- }
-
- public void testFetchCommandsForClient() {
- String accountGUID = Utils.generateGuid();
- ArrayList<String> args = new ArrayList<String>();
- args.add("URI of Page");
- args.add("Sender GUID");
- args.add("Title of Page");
- String jsonArgs = JSONArray.toJSONString(args);
-
- Cursor cur = null;
- try {
- db.store(accountGUID, "displayURI", jsonArgs);
-
- // This row should ALSO show up in the fetch.
- args.add("Another arg.");
- db.store(accountGUID, "displayURI", JSONArray.toJSONString(args));
-
- // Test both stored items with the same GUID but different command are fetched.
- cur = db.fetchCommandsForClient(accountGUID);
- assertTrue(cur.moveToFirst());
- assertEquals(2, cur.getCount());
- } catch (NullCursorException e) {
- fail("Should not have NullCursorException");
- } finally {
- if (cur != null) {
- cur.close();
- }
- }
- }
-
- @SuppressWarnings("resource")
- public void testDelete() {
- ClientRecord record1 = new ClientRecord();
- ClientRecord record2 = new ClientRecord();
- String profileConst = Constants.DEFAULT_PROFILE;
-
- db.store(profileConst, record1);
- db.store(profileConst, record2);
-
- Cursor cur = null;
- try {
- // Test record doesn't exist after delete.
- db.deleteClient(record1.guid, profileConst);
- cur = db.fetchClientsCursor(record1.guid, profileConst);
- assertFalse(cur.moveToFirst());
- assertEquals(0, cur.getCount());
-
- // Test record2 still there after deleting record1.
- cur = db.fetchClientsCursor(record2.guid, profileConst);
- assertTrue(cur.moveToFirst());
- assertEquals(1, cur.getCount());
-
- String guid = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_ACCOUNT_GUID);
- String profileId = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_PROFILE);
- String clientName = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_NAME);
- String clientType = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_TYPE);
-
- assertEquals(record2.guid, guid);
- assertEquals(profileConst, profileId);
- assertEquals(record2.name, clientName);
- assertEquals(record2.type, clientType);
- } catch (NullCursorException e) {
- fail("Should not have NullCursorException");
- } finally {
- if (cur != null) {
- cur.close();
- }
- }
- }
-
- @SuppressWarnings("resource")
- public void testWipe() {
- ClientRecord record1 = new ClientRecord();
- ClientRecord record2 = new ClientRecord();
- String profileConst = Constants.DEFAULT_PROFILE;
-
- db.store(profileConst, record1);
- db.store(profileConst, record2);
-
-
- Cursor cur = null;
- try {
- // Test before wipe the records are there.
- cur = db.fetchClientsCursor(record2.guid, profileConst);
- assertTrue(cur.moveToFirst());
- assertEquals(1, cur.getCount());
- cur = db.fetchClientsCursor(record2.guid, profileConst);
- assertTrue(cur.moveToFirst());
- assertEquals(1, cur.getCount());
-
- // Test after wipe neither record exists.
- db.wipeClientsTable();
- cur = db.fetchClientsCursor(record2.guid, profileConst);
- assertFalse(cur.moveToFirst());
- assertEquals(0, cur.getCount());
- cur = db.fetchClientsCursor(record1.guid, profileConst);
- assertFalse(cur.moveToFirst());
- assertEquals(0, cur.getCount());
- } catch (NullCursorException e) {
- fail("Should not have NullCursorException");
- } finally {
- if (cur != null) {
- cur.close();
- }
- }
- }
-}
deleted file mode 100644
--- a/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/db/TestClientsDatabaseAccessor.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-package org.mozilla.gecko.background.db;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-import org.mozilla.gecko.background.testhelpers.CommandHelpers;
-import org.mozilla.gecko.sync.CommandProcessor.Command;
-import org.mozilla.gecko.sync.Utils;
-import org.mozilla.gecko.sync.repositories.NullCursorException;
-import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
-import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.test.AndroidTestCase;
-
-public class TestClientsDatabaseAccessor extends AndroidTestCase {
-
- public class StubbedClientsDatabaseAccessor extends ClientsDatabaseAccessor {
- public StubbedClientsDatabaseAccessor(Context mContext) {
- super(mContext);
- }
- }
-
- StubbedClientsDatabaseAccessor db;
-
- public void setUp() {
- db = new StubbedClientsDatabaseAccessor(mContext);
- db.wipeDB();
- }
-
- public void tearDown() {
- db.close();
- }
-
- public void testStoreArrayListAndFetch() throws NullCursorException {
- ArrayList<ClientRecord> list = new ArrayList<ClientRecord>();
- ClientRecord record1 = new ClientRecord(Utils.generateGuid());
- ClientRecord record2 = new ClientRecord(Utils.generateGuid());
- ClientRecord record3 = new ClientRecord(Utils.generateGuid());
-
- list.add(record1);
- list.add(record2);
- db.store(list);
-
- ClientRecord r1 = db.fetchClient(record1.guid);
- ClientRecord r2 = db.fetchClient(record2.guid);
- ClientRecord r3 = db.fetchClient(record3.guid);
-
- assertNotNull(r1);
- assertNotNull(r2);
- assertNull(r3);
- assertTrue(record1.equals(r1));
- assertTrue(record2.equals(r2));
- assertFalse(record3.equals(r3));
- }
-
- public void testStoreAndFetchCommandsForClient() {
- String accountGUID1 = Utils.generateGuid();
- String accountGUID2 = Utils.generateGuid();
-
- Command command1 = CommandHelpers.getCommand1();
- Command command2 = CommandHelpers.getCommand2();
- Command command3 = CommandHelpers.getCommand3();
-
- Cursor cur = null;
- try {
- db.store(accountGUID1, command1);
- db.store(accountGUID1, command2);
- db.store(accountGUID2, command3);
-
- List<Command> commands = db.fetchCommandsForClient(accountGUID1);
- assertEquals(2, commands.size());
- assertEquals(1, commands.get(0).args.size());
- assertEquals(1, commands.get(1).args.size());
- } catch (NullCursorException e) {
- fail("Should not have NullCursorException");
- } finally {
- if (cur != null) {
- cur.close();
- }
- }
- }
-
- public void testNumClients() {
- final int COUNT = 5;
- ArrayList<ClientRecord> list = new ArrayList<ClientRecord>();
- for (int i = 0; i < 5; i++) {
- list.add(new ClientRecord());
- }
- db.store(list);
- assertEquals(COUNT, db.clientsCount());
- }
-
- public void testFetchAll() throws NullCursorException {
- ArrayList<ClientRecord> list = new ArrayList<ClientRecord>();
- ClientRecord record1 = new ClientRecord(Utils.generateGuid());
- ClientRecord record2 = new ClientRecord(Utils.generateGuid());
-
- list.add(record1);
- list.add(record2);
-
- boolean thrown = false;
- try {
- Map<String, ClientRecord> records = db.fetchAllClients();
-
- assertNotNull(records);
- assertEquals(0, records.size());
-
- db.store(list);
- records = db.fetchAllClients();
- assertNotNull(records);
- assertEquals(2, records.size());
- assertTrue(record1.equals(records.get(record1.guid)));
- assertTrue(record2.equals(records.get(record2.guid)));
-
- // put() should throw an exception since records is immutable.
- records.put(null, null);
- } catch (UnsupportedOperationException e) {
- thrown = true;
- }
- assertTrue(thrown);
- }
-
- public void testFetchNonStaleClients() throws NullCursorException {
- String goodRecord1 = Utils.generateGuid();
- ClientRecord record1 = new ClientRecord(goodRecord1);
- record1.fxaDeviceId = "fxa1";
- ClientRecord record2 = new ClientRecord(Utils.generateGuid());
- record2.fxaDeviceId = "fxa2";
- String goodRecord2 = Utils.generateGuid();
- ClientRecord record3 = new ClientRecord(goodRecord2);
- record3.fxaDeviceId = "fxa4";
-
- ArrayList<ClientRecord> list = new ArrayList<>();
- list.add(record1);
- list.add(record2);
- list.add(record3);
- db.store(list);
-
- assertTrue(db.hasNonStaleClients(new String[]{"fxa1", "fxa-unknown"}));
- assertFalse(db.hasNonStaleClients(new String[]{}));
-
- String noFxADeviceId = Utils.generateGuid();
- ClientRecord record4 = new ClientRecord(noFxADeviceId);
- record4.fxaDeviceId = null;
- list.clear();
- list.add(record4);
- db.store(list);
-
- assertTrue(db.hasNonStaleClients(new String[]{}));
-
- Collection<ClientRecord> filtered = db.fetchNonStaleClients(new String[]{"fxa1", "fxa4", "fxa-unknown"});
- ClientRecord[] filteredArr = filtered.toArray(new ClientRecord[0]);
- assertEquals(3, filteredArr.length);
- assertEquals(filteredArr[0].guid, goodRecord1);
- assertEquals(filteredArr[1].guid, goodRecord2);
- assertEquals(filteredArr[2].guid, noFxADeviceId);
- }
-}
deleted file mode 100644
--- a/mobile/android/services/src/androidTest/java/org/mozilla/gecko/background/testhelpers/CommandHelpers.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-package org.mozilla.gecko.background.testhelpers;
-
-import org.json.simple.JSONArray;
-import org.mozilla.gecko.sync.CommandProcessor.Command;
-
-public class CommandHelpers {
-
- @SuppressWarnings("unchecked")
- public static Command getCommand1() {
- JSONArray args = new JSONArray();
- args.add("argsA");
- return new Command("displayURI", args);
- }
-
- @SuppressWarnings("unchecked")
- public static Command getCommand2() {
- JSONArray args = new JSONArray();
- args.add("argsB");
- return new Command("displayURI", args);
- }
-
- @SuppressWarnings("unchecked")
- public static Command getCommand3() {
- JSONArray args = new JSONArray();
- args.add("argsC");
- return new Command("displayURI", args);
- }
-
- @SuppressWarnings("unchecked")
- public static Command getCommand4() {
- JSONArray args = new JSONArray();
- args.add("URI of Page");
- args.add("Sender ID");
- args.add("Title of Page");
- return new Command("displayURI", args);
- }
-}
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/CommandProcessor.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/CommandProcessor.java
@@ -3,16 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.support.annotation.Nullable;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.sync.repositories.NullCursorException;
import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
@@ -50,20 +51,22 @@ public class CommandProcessor {
public static CommandProcessor getProcessor() {
return processor;
}
public static class Command {
public final String commandType;
public final JSONArray args;
private List<String> argsList;
+ @Nullable public String flowID;
- public Command(String commandType, JSONArray args) {
+ public Command(String commandType, JSONArray args, @Nullable String flowID) {
this.commandType = commandType;
this.args = args;
+ this.flowID = flowID;
}
/**
* Get list of arguments as strings. Individual arguments may be null.
*
* @return list of strings.
*/
public synchronized List<String> getArgsList() {
@@ -83,16 +86,19 @@ public class CommandProcessor {
return this.argsList;
}
@SuppressWarnings("unchecked")
public JSONObject asJSONObject() {
JSONObject out = new JSONObject();
out.put("command", this.commandType);
out.put("args", this.args);
+ if (this.flowID != null) {
+ out.put("flowID", this.flowID);
+ }
return out;
}
}
/**
* Register a command.
* <p>
* Any existing registration is overwritten.
@@ -144,18 +150,19 @@ public class CommandProcessor {
return null;
}
try {
JSONArray unparsedArgs = unparsedCommand.getArray("args");
if (unparsedArgs == null) {
return null;
}
+ final String flowID = unparsedCommand.getString("flowID");
- return new Command(type, unparsedArgs);
+ return new Command(type, unparsedArgs, flowID);
} catch (NonArrayJSONException e) {
Logger.debug(LOG_TAG, "Unable to parse args array. Invalid command");
return null;
}
}
@SuppressWarnings("unchecked")
public void sendURIToClientForDisplay(String uri, String clientID, String title, String sender, Context context) {
@@ -164,17 +171,18 @@ public class CommandProcessor {
Logger.pii(LOG_TAG, "URI is " + uri + "; title is '" + title + "'.");
}
final JSONArray args = new JSONArray();
args.add(uri);
args.add(sender);
args.add(title);
- final Command displayURICommand = new Command("displayURI", args);
+ final String flowID = Utils.generateGuid();
+ final Command displayURICommand = new Command("displayURI", args, flowID);
this.sendCommand(clientID, displayURICommand, context);
}
/**
* Validates and sends a command to a client or all clients.
*
* Calling this does not actually sync the command data to the server. If the
* client already has the command/args pair, it won't receive a duplicate
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/ClientsDatabase.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/ClientsDatabase.java
@@ -14,17 +14,17 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
public class ClientsDatabase extends CachedSQLiteOpenHelper {
public static final String LOG_TAG = "ClientsDatabase";
// Database Specifications.
protected static final String DB_NAME = "clients_database";
- protected static final int SCHEMA_VERSION = 4;
+ protected static final int SCHEMA_VERSION = 5;
// Clients Table.
public static final String TBL_CLIENTS = "clients";
public static final String COL_ACCOUNT_GUID = "guid";
public static final String COL_PROFILE = "profile";
public static final String COL_NAME = "name";
public static final String COL_TYPE = "device_type";
@@ -41,18 +41,19 @@ public class ClientsDatabase extends Cac
COL_DEVICE, COL_FXA_DEVICE_ID};
public static final String TBL_CLIENTS_KEY = COL_ACCOUNT_GUID + " = ? AND " +
COL_PROFILE + " = ?";
// Commands Table.
public static final String TBL_COMMANDS = "commands";
public static final String COL_COMMAND = "command";
public static final String COL_ARGS = "args";
+ public static final String COL_FLOW_ID = "flow_id";
- public static final String[] TBL_COMMANDS_COLUMNS = new String[] { COL_ACCOUNT_GUID, COL_COMMAND, COL_ARGS };
+ public static final String[] TBL_COMMANDS_COLUMNS = new String[] { COL_ACCOUNT_GUID, COL_COMMAND, COL_ARGS, COL_FLOW_ID };
public static final String TBL_COMMANDS_KEY = COL_ACCOUNT_GUID + " = ? AND " +
COL_COMMAND + " = ? AND " +
COL_ARGS + " = ?";
public static final String TBL_COMMANDS_GUID_QUERY = COL_ACCOUNT_GUID + " = ? ";
private final RepoUtils.QueryHelper queryHelper;
public ClientsDatabase(Context context) {
@@ -86,16 +87,17 @@ public class ClientsDatabase extends Cac
}
public static void createCommandsTable(SQLiteDatabase db) {
Logger.debug(LOG_TAG, "ClientsDatabase.createCommandsTable().");
String createCommandsTableSql = "CREATE TABLE " + TBL_COMMANDS + " ("
+ COL_ACCOUNT_GUID + " TEXT, "
+ COL_COMMAND + " TEXT, "
+ COL_ARGS + " TEXT, "
+ + COL_FLOW_ID + " TEXT, "
+ "PRIMARY KEY (" + COL_ACCOUNT_GUID + ", " + COL_COMMAND + ", " + COL_ARGS + "), "
+ "FOREIGN KEY (" + COL_ACCOUNT_GUID + ") REFERENCES " + TBL_CLIENTS + " (" + COL_ACCOUNT_GUID + "))";
db.execSQL(createCommandsTableSql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Logger.debug(LOG_TAG, "ClientsDatabase.onUpgrade(" + oldVersion + ", " + newVersion + ").");
@@ -115,16 +117,20 @@ public class ClientsDatabase extends Cac
db.execSQL("ALTER TABLE " + TBL_CLIENTS + " ADD COLUMN " + COL_APP_PACKAGE + " TEXT");
db.execSQL("ALTER TABLE " + TBL_CLIENTS + " ADD COLUMN " + COL_DEVICE + " TEXT");
}
if (oldVersion < 4 && newVersion >= 4) {
db.execSQL("ALTER TABLE " + TBL_CLIENTS + " ADD COLUMN " + COL_FXA_DEVICE_ID + " TEXT");
db.execSQL("CREATE INDEX idx_fxa_device_id ON " + TBL_CLIENTS + "(" + COL_FXA_DEVICE_ID + ")");
}
+
+ if (oldVersion < 5 && newVersion >= 5) {
+ db.execSQL("ALTER TABLE " + TBL_COMMANDS + " ADD COLUMN " + COL_FLOW_ID + " TEXT");
+ }
}
public void wipeDB() {
SQLiteDatabase db = this.getCachedWritableDatabase();
onUpgrade(db, 0, SCHEMA_VERSION);
}
public void wipeClientsTable() {
@@ -184,34 +190,38 @@ public class ClientsDatabase extends Cac
}
/**
* Store a command in the commands database if it doesn't already exist.
*
* @param accountGUID
* @param command - The command type
* @param args - A JSON string of args
+ * @param flowID - Optional - The flowID
* @throws NullCursorException
*/
- public void store(String accountGUID, String command, String args) throws NullCursorException {
+ public void store(String accountGUID, String command, String args, String flowID) throws NullCursorException {
if (Logger.LOG_PERSONAL_INFORMATION) {
Logger.pii(LOG_TAG, "Storing command " + command + " with args " + args);
} else {
Logger.trace(LOG_TAG, "Storing command " + command + ".");
}
SQLiteDatabase db = this.getCachedWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(COL_ACCOUNT_GUID, accountGUID);
cv.put(COL_COMMAND, command);
if (args == null) {
cv.put(COL_ARGS, "[]");
} else {
cv.put(COL_ARGS, args);
}
+ if (flowID != null) {
+ cv.put(COL_FLOW_ID, flowID);
+ }
Cursor cur = this.fetchSpecificCommand(accountGUID, command, args);
try {
if (cur.moveToFirst()) {
Logger.debug(LOG_TAG, "Command already exists in database.");
return;
}
} finally {
@@ -224,16 +234,18 @@ public class ClientsDatabase extends Cac
public Cursor fetchClientsCursor(String accountGUID, String profileId) throws NullCursorException {
String[] args = new String[] { accountGUID, profileId };
SQLiteDatabase db = this.getCachedReadableDatabase();
return queryHelper.safeQuery(db, ".fetchClientsCursor", TBL_CLIENTS, TBL_CLIENTS_COLUMNS, TBL_CLIENTS_KEY, args);
}
+ // This method does not check flowID on purpose because we do not want to take it into account
+ // when de-duping commands.
public Cursor fetchSpecificCommand(String accountGUID, String command, String commandArgs) throws NullCursorException {
String[] args = new String[] { accountGUID, command, commandArgs };
SQLiteDatabase db = this.getCachedReadableDatabase();
return queryHelper.safeQuery(db, ".fetchSpecificCommand", TBL_COMMANDS, TBL_COMMANDS_COLUMNS, TBL_COMMANDS_KEY, args);
}
public Cursor fetchCommandsForClient(String accountGUID) throws NullCursorException {
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/ClientsDatabaseAccessor.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/ClientsDatabaseAccessor.java
@@ -45,17 +45,17 @@ public class ClientsDatabaseAccessor {
public void store(Collection<ClientRecord> records) {
for (ClientRecord record : records) {
this.store(record);
}
}
public void store(String accountGUID, Command command) throws NullCursorException {
- db.store(accountGUID, command.commandType, command.args.toJSONString());
+ db.store(accountGUID, command.commandType, command.args.toJSONString(), command.flowID);
}
public ClientRecord fetchClient(String accountGUID) throws NullCursorException {
final Cursor cur = db.fetchClientsCursor(accountGUID, getProfileId());
try {
if (!cur.moveToFirst()) {
return null;
}
@@ -190,19 +190,20 @@ public class ClientsDatabaseAccessor {
record.fxaDeviceId = RepoUtils.optStringFromCursor(cur, ClientsDatabase.COL_FXA_DEVICE_ID);
record.appPackage = RepoUtils.optStringFromCursor(cur, ClientsDatabase.COL_APP_PACKAGE);
record.application = RepoUtils.optStringFromCursor(cur, ClientsDatabase.COL_APPLICATION);
return record;
}
protected static Command commandFromCursor(Cursor cur) {
- String commandType = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_COMMAND);
- JSONArray commandArgs = RepoUtils.getJSONArrayFromCursor(cur, ClientsDatabase.COL_ARGS);
- return new Command(commandType, commandArgs);
+ final String commandType = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_COMMAND);
+ final JSONArray commandArgs = RepoUtils.getJSONArrayFromCursor(cur, ClientsDatabase.COL_ARGS);
+ final String flowID = RepoUtils.optStringFromCursor(cur, ClientsDatabase.COL_FLOW_ID);
+ return new Command(commandType, commandArgs, flowID);
}
public int clientsCount() {
try {
final Cursor cur = db.fetchAllClients();
try {
return cur.getCount();
} finally {
new file mode 100644
--- /dev/null
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/background/db/TestClientsDatabase.java
@@ -0,0 +1,220 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.db;
+
+import android.content.Context;
+import android.database.Cursor;
+
+import org.json.simple.JSONArray;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mozilla.gecko.background.testhelpers.TestRunner;
+import org.mozilla.gecko.sync.Utils;
+import org.mozilla.gecko.sync.repositories.NullCursorException;
+import org.mozilla.gecko.sync.repositories.android.ClientsDatabase;
+import org.mozilla.gecko.sync.repositories.android.RepoUtils;
+import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
+import org.mozilla.gecko.sync.setup.Constants;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+
+@RunWith(TestRunner.class)
+public class TestClientsDatabase {
+
+ protected ClientsDatabase db;
+
+ @Before
+ public void setUp() {
+ final Context context = RuntimeEnvironment.application;
+ db = new ClientsDatabase(context);
+ db.wipeDB();
+ }
+
+ @After
+ public void tearDown() {
+ db.close();
+ }
+
+ @Test
+ public void testStoreAndFetch() {
+ ClientRecord record = new ClientRecord();
+ String profileConst = Constants.DEFAULT_PROFILE;
+ db.store(profileConst, record);
+
+ Cursor cur = null;
+ try {
+ // Test stored item gets fetched correctly.
+ cur = db.fetchClientsCursor(record.guid, profileConst);
+ Assert.assertTrue(cur.moveToFirst());
+ Assert.assertEquals(1, cur.getCount());
+
+ String guid = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_ACCOUNT_GUID);
+ String profileId = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_PROFILE);
+ String clientName = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_NAME);
+ String clientType = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_TYPE);
+
+ Assert.assertEquals(record.guid, guid);
+ Assert.assertEquals(profileConst, profileId);
+ Assert.assertEquals(record.name, clientName);
+ Assert.assertEquals(record.type, clientType);
+ } catch (NullCursorException e) {
+ Assert.fail("Should not have NullCursorException");
+ } finally {
+ if (cur != null) {
+ cur.close();
+ }
+ }
+ }
+
+ @Test
+ public void testStoreAndFetchSpecificCommands() {
+ String accountGUID = Utils.generateGuid();
+ ArrayList<String> args = new ArrayList<>();
+ args.add("URI of Page");
+ args.add("Sender GUID");
+ args.add("Title of Page");
+ String jsonArgs = JSONArray.toJSONString(args);
+
+ Cursor cur = null;
+ try {
+ db.store(accountGUID, "displayURI", jsonArgs, "flowID");
+
+ // This row should not show up in the fetch.
+ args.add("Another arg.");
+ db.store(accountGUID, "displayURI", JSONArray.toJSONString(args), "flowID");
+
+ // Test stored item gets fetched correctly.
+ cur = db.fetchSpecificCommand(accountGUID, "displayURI", jsonArgs);
+ Assert.assertTrue(cur.moveToFirst());
+ Assert.assertEquals(1, cur.getCount());
+
+ String guid = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_ACCOUNT_GUID);
+ String commandType = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_COMMAND);
+ String fetchedArgs = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_ARGS);
+
+ Assert.assertEquals(accountGUID, guid);
+ Assert.assertEquals("displayURI", commandType);
+ Assert.assertEquals(jsonArgs, fetchedArgs);
+ } catch (NullCursorException e) {
+ Assert.fail("Should not have NullCursorException");
+ } finally {
+ if (cur != null) {
+ cur.close();
+ }
+ }
+ }
+
+ @Test
+ public void testFetchCommandsForClient() {
+ String accountGUID = Utils.generateGuid();
+ ArrayList<String> args = new ArrayList<>();
+ args.add("URI of Page");
+ args.add("Sender GUID");
+ args.add("Title of Page");
+ String jsonArgs = JSONArray.toJSONString(args);
+
+ Cursor cur = null;
+ try {
+ db.store(accountGUID, "displayURI", jsonArgs, "flowID");
+
+ // This row should ALSO show up in the fetch.
+ args.add("Another arg.");
+ db.store(accountGUID, "displayURI", JSONArray.toJSONString(args), "flowID");
+
+ // Test both stored items with the same GUID but different command are fetched.
+ cur = db.fetchCommandsForClient(accountGUID);
+ Assert.assertTrue(cur.moveToFirst());
+ Assert.assertEquals(2, cur.getCount());
+ } catch (NullCursorException e) {
+ Assert.fail("Should not have NullCursorException");
+ } finally {
+ if (cur != null) {
+ cur.close();
+ }
+ }
+ }
+
+ @Test
+ @SuppressWarnings("resource")
+ public void testDelete() {
+ ClientRecord record1 = new ClientRecord();
+ ClientRecord record2 = new ClientRecord();
+ String profileConst = Constants.DEFAULT_PROFILE;
+
+ db.store(profileConst, record1);
+ db.store(profileConst, record2);
+
+ Cursor cur = null;
+ try {
+ // Test record doesn't exist after delete.
+ db.deleteClient(record1.guid, profileConst);
+ cur = db.fetchClientsCursor(record1.guid, profileConst);
+ Assert.assertFalse(cur.moveToFirst());
+ Assert.assertEquals(0, cur.getCount());
+
+ // Test record2 still there after deleting record1.
+ cur = db.fetchClientsCursor(record2.guid, profileConst);
+ Assert.assertTrue(cur.moveToFirst());
+ Assert.assertEquals(1, cur.getCount());
+
+ String guid = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_ACCOUNT_GUID);
+ String profileId = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_PROFILE);
+ String clientName = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_NAME);
+ String clientType = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_TYPE);
+
+ Assert.assertEquals(record2.guid, guid);
+ Assert.assertEquals(profileConst, profileId);
+ Assert.assertEquals(record2.name, clientName);
+ Assert.assertEquals(record2.type, clientType);
+ } catch (NullCursorException e) {
+ Assert.fail("Should not have NullCursorException");
+ } finally {
+ if (cur != null) {
+ cur.close();
+ }
+ }
+ }
+
+ @Test
+ @SuppressWarnings("resource")
+ public void testWipe() {
+ ClientRecord record1 = new ClientRecord();
+ ClientRecord record2 = new ClientRecord();
+ String profileConst = Constants.DEFAULT_PROFILE;
+
+ db.store(profileConst, record1);
+ db.store(profileConst, record2);
+
+
+ Cursor cur = null;
+ try {
+ // Test before wipe the records are there.
+ cur = db.fetchClientsCursor(record2.guid, profileConst);
+ Assert.assertTrue(cur.moveToFirst());
+ Assert.assertEquals(1, cur.getCount());
+ cur = db.fetchClientsCursor(record2.guid, profileConst);
+ Assert.assertTrue(cur.moveToFirst());
+ Assert.assertEquals(1, cur.getCount());
+
+ // Test after wipe neither record exists.
+ db.wipeClientsTable();
+ cur = db.fetchClientsCursor(record2.guid, profileConst);
+ Assert.assertFalse(cur.moveToFirst());
+ Assert.assertEquals(0, cur.getCount());
+ cur = db.fetchClientsCursor(record1.guid, profileConst);
+ Assert.assertFalse(cur.moveToFirst());
+ Assert.assertEquals(0, cur.getCount());
+ } catch (NullCursorException e) {
+ Assert.fail("Should not have NullCursorException");
+ } finally {
+ if (cur != null) {
+ cur.close();
+ }
+ }
+ }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/background/db/TestClientsDatabaseAccessor.java
@@ -0,0 +1,166 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.background.db;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mozilla.gecko.background.testhelpers.CommandHelpers;
+import org.mozilla.gecko.background.testhelpers.TestRunner;
+import org.mozilla.gecko.sync.CommandProcessor.Command;
+import org.mozilla.gecko.sync.Utils;
+import org.mozilla.gecko.sync.repositories.NullCursorException;
+import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
+import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
+import org.robolectric.RuntimeEnvironment;
+
+import android.content.Context;
+
+@RunWith(TestRunner.class)
+public class TestClientsDatabaseAccessor {
+
+ ClientsDatabaseAccessor db;
+
+ @Before
+ public void setUp() {
+ final Context context = RuntimeEnvironment.application;
+ db = new ClientsDatabaseAccessor(context);
+ db.wipeDB();
+ }
+
+ @After
+ public void tearDown() {
+ db.close();
+ }
+
+ public void testStoreArrayListAndFetch() throws NullCursorException {
+ ArrayList<ClientRecord> list = new ArrayList<>();
+ ClientRecord record1 = new ClientRecord(Utils.generateGuid());
+ ClientRecord record2 = new ClientRecord(Utils.generateGuid());
+ ClientRecord record3 = new ClientRecord(Utils.generateGuid());
+
+ list.add(record1);
+ list.add(record2);
+ db.store(list);
+
+ ClientRecord r1 = db.fetchClient(record1.guid);
+ ClientRecord r2 = db.fetchClient(record2.guid);
+ ClientRecord r3 = db.fetchClient(record3.guid);
+
+ Assert.assertNotNull(r1);
+ Assert.assertNotNull(r2);
+ Assert.assertNull(r3);
+ Assert.assertTrue(record1.equals(r1));
+ Assert.assertTrue(record2.equals(r2));
+ }
+
+ @Test
+ public void testStoreAndFetchCommandsForClient() {
+ String accountGUID1 = Utils.generateGuid();
+ String accountGUID2 = Utils.generateGuid();
+
+ Command command1 = CommandHelpers.getCommand1();
+ Command command2 = CommandHelpers.getCommand2();
+ Command command3 = CommandHelpers.getCommand3();
+
+ try {
+ db.store(accountGUID1, command1);
+ db.store(accountGUID1, command2);
+ db.store(accountGUID2, command3);
+
+ List<Command> commands = db.fetchCommandsForClient(accountGUID1);
+ Assert.assertEquals(2, commands.size());
+ Assert.assertEquals(1, commands.get(0).args.size());
+ Assert.assertEquals(1, commands.get(1).args.size());
+ } catch (NullCursorException e) {
+ Assert.fail("Should not have NullCursorException");
+ }
+ }
+
+ @Test
+ public void testNumClients() {
+ final int COUNT = 5;
+ ArrayList<ClientRecord> list = new ArrayList<>();
+ for (int i = 0; i < 5; i++) {
+ list.add(new ClientRecord());
+ }
+ db.store(list);
+ Assert.assertEquals(COUNT, db.clientsCount());
+ }
+
+ @Test
+ public void testFetchAll() throws NullCursorException {
+ ArrayList<ClientRecord> list = new ArrayList<>();
+ ClientRecord record1 = new ClientRecord(Utils.generateGuid());
+ ClientRecord record2 = new ClientRecord(Utils.generateGuid());
+
+ list.add(record1);
+ list.add(record2);
+
+ boolean thrown = false;
+ try {
+ Map<String, ClientRecord> records = db.fetchAllClients();
+
+ Assert.assertNotNull(records);
+ Assert.assertEquals(0, records.size());
+
+ db.store(list);
+ records = db.fetchAllClients();
+ Assert.assertNotNull(records);
+ Assert.assertEquals(2, records.size());
+ Assert.assertTrue(record1.equals(records.get(record1.guid)));
+ Assert.assertTrue(record2.equals(records.get(record2.guid)));
+
+ // put() should throw an exception since records is immutable.
+ records.put(null, null);
+ } catch (UnsupportedOperationException e) {
+ thrown = true;
+ }
+ Assert.assertTrue(thrown);
+ }
+
+ @Test
+ public void testFetchNonStaleClients() throws NullCursorException {
+ String goodRecord1 = Utils.generateGuid();
+ ClientRecord record1 = new ClientRecord(goodRecord1);
+ record1.fxaDeviceId = "fxa1";
+ ClientRecord record2 = new ClientRecord(Utils.generateGuid());
+ record2.fxaDeviceId = "fxa2";
+ String goodRecord2 = Utils.generateGuid();
+ ClientRecord record3 = new ClientRecord(goodRecord2);
+ record3.fxaDeviceId = "fxa4";
+
+ ArrayList<ClientRecord> list = new ArrayList<>();
+ list.add(record1);
+ list.add(record2);
+ list.add(record3);
+ db.store(list);
+
+ Assert.assertTrue(db.hasNonStaleClients(new String[]{"fxa1", "fxa-unknown"}));
+ Assert.assertFalse(db.hasNonStaleClients(new String[]{}));
+
+ String noFxADeviceId = Utils.generateGuid();
+ ClientRecord record4 = new ClientRecord(noFxADeviceId);
+ record4.fxaDeviceId = null;
+ list.clear();
+ list.add(record4);
+ db.store(list);
+
+ Assert.assertTrue(db.hasNonStaleClients(new String[]{}));
+
+ Collection<ClientRecord> filtered = db.fetchNonStaleClients(new String[]{"fxa1", "fxa4", "fxa-unknown"});
+ ClientRecord[] filteredArr = filtered.toArray(new ClientRecord[0]);
+ Assert.assertEquals(3, filteredArr.length);
+ Assert.assertEquals(filteredArr[0].guid, goodRecord1);
+ Assert.assertEquals(filteredArr[1].guid, goodRecord2);
+ Assert.assertEquals(filteredArr[2].guid, noFxADeviceId);
+ }
+}
--- a/mobile/android/services/src/test/java/org/mozilla/gecko/background/testhelpers/CommandHelpers.java
+++ b/mobile/android/services/src/test/java/org/mozilla/gecko/background/testhelpers/CommandHelpers.java
@@ -7,34 +7,34 @@ import org.json.simple.JSONArray;
import org.mozilla.gecko.sync.CommandProcessor.Command;
public class CommandHelpers {
@SuppressWarnings("unchecked")
public static Command getCommand1() {
JSONArray args = new JSONArray();
args.add("argsA");
- return new Command("displayURI", args);
+ return new Command("displayURI", args, null);
}
@SuppressWarnings("unchecked")
public static Command getCommand2() {
JSONArray args = new JSONArray();
args.add("argsB");
- return new Command("displayURI", args);
+ return new Command("displayURI", args, null);
}
@SuppressWarnings("unchecked")
public static Command getCommand3() {
JSONArray args = new JSONArray();
args.add("argsC");
- return new Command("displayURI", args);
+ return new Command("displayURI", args, null);
}
@SuppressWarnings("unchecked")
public static Command getCommand4() {
JSONArray args = new JSONArray();
args.add("URI of Page");
args.add("Sender ID");
args.add("Title of Page");
- return new Command("displayURI", args);
+ return new Command("displayURI", args, null);
}
}