Backed out 3 changesets (bug 1363924) for android-test failures. CLOSED TREE

Backed out changeset 08747734c0fd (bug 1363924)
Backed out changeset fbba0f584237 (bug 1363924)
Backed out changeset d30f27293117 (bug 1363924)
This commit is contained in:
Csoregi Natalia 2018-02-26 20:46:49 +02:00
Родитель 7c03ced862
Коммит 8b633f366a
24 изменённых файлов: 479 добавлений и 893 удалений

Просмотреть файл

@ -1,76 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.telemetry.pingbuilders;
import android.os.Bundle;
import org.json.simple.JSONArray;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.telemetry.TelemetryContract;
@RunWith(TestRunner.class)
public class TelemetrySyncEventPingBuilderTest {
@Test
public void testGeneralShape() throws Exception {
JSONArray payload = buildPayloadArray(123456L, "sync", "object", "method", null, null);
Assert.assertArrayEquals(new Object[] {123456L, "sync", "method", "object"}, payload.toArray());
payload = buildPayloadArray(123456L, "sync", "object", "method", "value", null);
Assert.assertArrayEquals(new Object[] {123456L, "sync", "method", "object", "value"}, payload.toArray());
Bundle extra = new Bundle();
extra.putString("extra-key", "extra-value");
payload = buildPayloadArray(123456L, "sync", "object", "method", null, extra);
Assert.assertEquals("[123456,\"sync\",\"method\",\"object\",null,{\"extra\":\"extra\"}]",
payload.toJSONString());
payload = buildPayloadArray(123456L, "sync", "object", "method", "value", extra);
Assert.assertEquals("[123456,\"sync\",\"method\",\"object\",\"value\",{\"extra\":\"extra\"}]",
payload.toJSONString());
}
@Test(expected = IllegalStateException.class)
public void testNullTimestamp() throws Exception {
buildPayloadArray(null, "category", "object", "method", null, null);
}
@Test(expected = IllegalStateException.class)
public void testNullCategory() throws Exception {
buildPayloadArray(123456L, null, "object", "method", null, null);
}
@Test(expected = IllegalStateException.class)
public void testNullObject() throws Exception {
buildPayloadArray(123456L, "category", null, "method", null, null);
}
@Test(expected = IllegalStateException.class)
public void testNullMethod() throws Exception {
buildPayloadArray(123456L, "category", "object", null, null, null);
}
private JSONArray buildPayloadArray(Long ts, String category, String object, String method,
String value, Bundle extra) throws Exception {
Bundle bundle = new Bundle();
if (ts != null) {
bundle.putLong(TelemetryContract.KEY_EVENT_TIMESTAMP, ts);
}
bundle.putString(TelemetryContract.KEY_EVENT_CATEGORY, category);
bundle.putString(TelemetryContract.KEY_EVENT_OBJECT, object);
bundle.putString(TelemetryContract.KEY_EVENT_METHOD, method);
if (value != null) {
bundle.putString(TelemetryContract.KEY_EVENT_VALUE, value);
}
if (extra != null) {
bundle.putBundle(TelemetryContract.KEY_EVENT_EXTRA, extra);
}
return new TelemetrySyncEventPingBuilder().fromEventTelemetry(bundle)
.build().getPayload().getArray("event");
}
}

Просмотреть файл

@ -16,6 +16,8 @@ import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.synchronizer.StoreBatchTracker;
import org.mozilla.gecko.sync.telemetry.TelemetryStageCollector;
import org.mozilla.gecko.sync.validation.BookmarkValidationResults;
import org.mozilla.gecko.sync.validation.ValidationResults;
import org.mozilla.gecko.telemetry.TelemetryLocalPing;
import java.util.ArrayList;
@ -37,19 +39,27 @@ public class TelemetrySyncPingBuilderTest {
@Test
public void testGeneralShape() throws Exception {
TelemetryLocalPing localPing = builder
.setDeviceID("device-id")
.setUID("uid")
.setTook(123L)
.setRestarted(false)
.build();
ExtendedJSONObject payload = localPing.getPayload();
assertEquals("uid", payload.getString("uid"));
assertEquals(Long.valueOf(123L), payload.getLong("took"));
assertEquals("device-id", payload.getString("deviceID"));
assertFalse(payload.containsKey("restarted"));
localPing = builder
.setDeviceID("device-id")
.setUID("uid")
.setTook(123L)
.setRestarted(true)
.build();
payload = localPing.getPayload();
assertEquals("uid", payload.getString("uid"));
assertEquals(Long.valueOf(123L), payload.getLong("took"));
assertEquals("device-id", payload.getString("deviceID"));
assertTrue(payload.getLong("when") != null);
assertEquals(true, payload.getBoolean("restarted"));

Просмотреть файл

@ -3,19 +3,16 @@
package org.mozilla.gecko.telemetry.pingbuilders;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import org.json.JSONException;
import org.json.simple.JSONArray;
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.ExtendedJSONObject;
import org.mozilla.gecko.sync.telemetry.TelemetryContract;
import org.mozilla.gecko.telemetry.TelemetryOutgoingPing;
import org.mozilla.gecko.telemetry.TelemetryPing;
import org.mozilla.gecko.telemetry.stores.TelemetryJSONFilePingStore;
@ -108,8 +105,6 @@ public class TelemetrySyncPingBundleBuilderTest {
public void testGeneralShape() throws Exception {
builder.setSyncStore(syncPings);
builder.setSyncEventStore(eventPings);
builder.setDeviceID("device-id-1");
builder.setUID("uid-1");
TelemetryOutgoingPing outgoingPing = builder.build();
@ -132,15 +127,15 @@ public class TelemetrySyncPingBundleBuilderTest {
assertTrue(application.containsKey("xpcomAbi"));
// Test general shape of payload. Expecting {"why":"schedule", "version": 1,
// "os": {"name": "Android", "version": "<version>", "locale": "<locale>"},
// "deviceID": <Hashed Device ID>, "uid": <Hashed UID>}.
// Test general shape of payload. Expecting {"syncs":[],"why":"schedule", "version": 1,
// "os": {"name": "Android", "version": "<version>", "locale": "<locale>"}}.
// NB that even though we set an empty sync event store, it's not in the json string.
// That's because sync events are not yet instrumented.
ExtendedJSONObject payload = outgoingPing.getPayload().getObject("payload");
assertEquals(5, payload.keySet().size());
assertEquals(4, payload.keySet().size());
assertEquals("schedule", payload.getString("why"));
assertEquals(Integer.valueOf(1), payload.getIntegerSafely("version"));
assertEquals(payload.getString("uid"), "uid-1");
assertEquals(payload.getString("deviceID"), "device-id-1");
assertEquals(0, payload.getArray("syncs").size());
// Test os key.
ExtendedJSONObject os = payload.getObject("os");
assertEquals(3, os.keySet().size());
@ -156,8 +151,10 @@ public class TelemetrySyncPingBundleBuilderTest {
public void testBundlingOfMultiplePings() throws Exception {
// Try just one ping first.
syncPings.storePing(new TelemetrySyncPingBuilder()
.setDeviceID("test-device-id")
.setRestarted(true)
.setTook(123L)
.setUID("test-uid")
.build()
);
builder.setSyncStore(syncPings);
@ -169,12 +166,14 @@ public class TelemetrySyncPingBundleBuilderTest {
assertEquals("schedule", payload.getString("why"));
JSONArray syncs = payload.getArray("syncs");
assertEquals(1, syncs.size());
assertSync((ExtendedJSONObject) syncs.get(0), 123L, true);
assertSync((ExtendedJSONObject) syncs.get(0), "test-uid", 123L, "test-device-id", true);
// Add another ping.
syncPings.storePing(new TelemetrySyncPingBuilder()
.setDeviceID("test-device-id")
.setRestarted(false)
.setTook(321L)
.setUID("test-uid")
.build()
);
builder.setSyncStore(syncPings);
@ -185,37 +184,14 @@ public class TelemetrySyncPingBundleBuilderTest {
.getObject("payload")
.getArray("syncs");
assertEquals(2, syncs.size());
assertSync((ExtendedJSONObject) syncs.get(0), 123L, true);
assertSync((ExtendedJSONObject) syncs.get(1), 321L, false);
// And add an event ping!
Bundle event = new Bundle();
event.putLong(TelemetryContract.KEY_EVENT_TIMESTAMP, 123456L);
event.putString(TelemetryContract.KEY_EVENT_CATEGORY, "sync");
event.putString(TelemetryContract.KEY_EVENT_OBJECT, "object");
event.putString(TelemetryContract.KEY_EVENT_METHOD, "method");
event.putString(TelemetryContract.KEY_EVENT_VALUE, "value");
Bundle extra = new Bundle();
extra.putString("extra-key", "extra-value");
event.putBundle(TelemetryContract.KEY_EVENT_EXTRA, extra);
eventPings.storePing(new TelemetrySyncEventPingBuilder()
.fromEventTelemetry(event)
.build()
);
builder.setSyncEventStore(eventPings);
// We should have three pings now.
outgoingPing = builder.build();
JSONArray events = outgoingPing.getPayload()
.getObject("payload")
.getArray("events");
assertEquals(1, events.size());
Assert.assertEquals("[[123456,\"sync\",\"method\",\"object\",\"value\",{\"extra\":\"extra\"}]]",
events.toJSONString());
assertSync((ExtendedJSONObject) syncs.get(0), "test-uid", 123L, "test-device-id", true);
assertSync((ExtendedJSONObject) syncs.get(1), "test-uid", 321L, "test-device-id", false);
}
private void assertSync(ExtendedJSONObject sync, long took, boolean restarted) throws JSONException {
private void assertSync(ExtendedJSONObject sync, String uid, long took, String deviceID, boolean restarted) throws JSONException {
assertEquals(uid, sync.getString("uid"));
assertEquals(Long.valueOf(took), sync.getLong("took"));
assertEquals(deviceID, sync.getString("deviceID"));
// Test that 'when' timestamp looks generally sane.
final long now = System.currentTimeMillis();

Просмотреть файл

@ -1087,7 +1087,6 @@ sync_java_files = [TOPSRCDIR + '/mobile/android/services/src/main/java/org/mozil
'sync/SynchronizerConfiguration.java',
'sync/telemetry/TelemetryCollector.java',
'sync/telemetry/TelemetryContract.java',
'sync/telemetry/TelemetryEventCollector.java',
'sync/telemetry/TelemetryStageCollector.java',
'sync/ThreadPool.java',
'sync/UnexpectedJSONException.java',

Просмотреть файл

@ -151,6 +151,14 @@ public class TelemetryBackgroundReceiver extends BroadcastReceiver {
telemetryStore = syncTelemetryStore;
TelemetrySyncPingBuilder localPingBuilder = new TelemetrySyncPingBuilder();
if (uid != null) {
localPingBuilder.setUID(uid);
}
if (deviceID != null) {
localPingBuilder.setDeviceID(deviceID);
}
if (devices != null) {
localPingBuilder.setDevices(devices);
}
@ -171,7 +179,9 @@ public class TelemetryBackgroundReceiver extends BroadcastReceiver {
case TelemetryContract.KEY_TYPE_EVENT:
telemetryStore = syncEventTelemetryStore;
localPing = new TelemetrySyncEventPingBuilder()
.fromEventTelemetry(telemetryBundle)
.fromEventTelemetry(
(Bundle) intent.getParcelableExtra(
TelemetryContract.KEY_TELEMETRY))
.build();
break;
default:
@ -223,8 +233,6 @@ public class TelemetryBackgroundReceiver extends BroadcastReceiver {
// Bundle up all that we have in our telemetry stores.
final TelemetryOutgoingPing syncPing = new TelemetrySyncPingBundleBuilder()
.setUID(uid)
.setDeviceID(deviceID)
.setSyncStore(syncTelemetryStore)
.setSyncEventStore(syncEventTelemetryStore)
.setReason(reasonToUpload)

Просмотреть файл

@ -6,53 +6,19 @@ package org.mozilla.gecko.telemetry.pingbuilders;
import android.os.Bundle;
import org.json.simple.JSONArray;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.telemetry.TelemetryContract;
import org.mozilla.gecko.telemetry.TelemetryLocalPing;
/**
* Local ping builder which understands how to process event data.
* This is a placeholder, to be implemented in Bug 1363924.
*/
public class TelemetrySyncEventPingBuilder extends TelemetryLocalPingBuilder {
@SuppressWarnings("unchecked")
public TelemetrySyncEventPingBuilder fromEventTelemetry(Bundle data) {
final long timestamp = data.getLong(TelemetryContract.KEY_EVENT_TIMESTAMP, -1L);
final String category = data.getString(TelemetryContract.KEY_EVENT_CATEGORY);
final String object = data.getString(TelemetryContract.KEY_EVENT_OBJECT);
final String method = data.getString(TelemetryContract.KEY_EVENT_METHOD);
final String value = data.getString(TelemetryContract.KEY_EVENT_VALUE);
final Bundle extra = data.getBundle(TelemetryContract.KEY_EVENT_EXTRA);
if (timestamp == -1L || category == null || object == null || method == null) {
throw new IllegalStateException("Bundle should be well formed.");
}
final JSONArray event = new JSONArray();
// Events are serialized as arrays when sending the sync ping. The order of the following
// statements SHOULD NOT be changed unless the telemetry server specification changes.
event.add(timestamp);
event.add(category);
event.add(method);
event.add(object);
if (value != null || extra != null) {
event.add(value);
if (extra != null) {
final ExtendedJSONObject extraJSON = new ExtendedJSONObject();
for (final String k : extra.keySet()) {
extraJSON.put(k, extra.getString(k));
}
event.add(extraJSON);
}
}
/**
* Note: {@link org.mozilla.gecko.telemetry.TelemetryOutgoingPing#getPayload()}
* returns ExtendedJSONObject. Wrap our JSONArray into the payload JSON object.
*/
payload.put("event", event);
return this;
}
@Override
public TelemetryLocalPing build() {
return new TelemetryLocalPing(payload, docID);
throw new UnsupportedOperationException();
}
}

Просмотреть файл

@ -89,6 +89,16 @@ public class TelemetrySyncPingBuilder extends TelemetryLocalPingBuilder {
return this;
}
public TelemetrySyncPingBuilder setUID(@NonNull String uid) {
payload.put("uid", uid);
return this;
}
public TelemetrySyncPingBuilder setDeviceID(@NonNull String deviceID) {
payload.put("deviceID", deviceID);
return this;
}
@Nullable
private static JSONArray buildOutgoing(List<StoreBatchTracker.Batch> batches) {
if (batches == null || batches.size() == 0) {

Просмотреть файл

@ -8,13 +8,11 @@ package org.mozilla.gecko.telemetry.pingbuilders;
import android.os.Build;
import android.support.annotation.NonNull;
import android.util.Log;
import org.json.simple.JSONArray;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.Locales;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.NonArrayJSONException;
import org.mozilla.gecko.telemetry.TelemetryOutgoingPing;
import org.mozilla.gecko.telemetry.TelemetryPing;
import org.mozilla.gecko.telemetry.stores.TelemetryPingStore;
@ -35,6 +33,8 @@ import java.util.TimeZone;
*
* This builder takes two stores ('sync' and 'event') and produces a single "sync ping".
*
* Note that until Bug 1363924, event telemetry will be ignored.
*
* Sample result will look something like:
* {
* "syncs": [list of syncs, as produced by the SyncBuilder],
@ -42,8 +42,6 @@ import java.util.TimeZone;
* }
*/
public class TelemetrySyncPingBundleBuilder extends TelemetryPingBuilder {
public static final String LOG_TAG = "SyncPingBundleBuilder";
private static final String PING_TYPE = "sync";
private static final int PING_BUNDLE_VERSION = 4; // Bug 1410145
private static final int PING_SYNC_DATA_FORMAT_VERSION = 1; // Bug 1374758
@ -71,16 +69,6 @@ public class TelemetrySyncPingBundleBuilder extends TelemetryPingBuilder {
return this;
}
public TelemetrySyncPingBundleBuilder setUID(@NonNull String uid) {
pingData.put("uid", uid);
return this;
}
public TelemetrySyncPingBundleBuilder setDeviceID(@NonNull String deviceID) {
pingData.put("deviceID", deviceID);
return this;
}
@Override
public TelemetryOutgoingPing build() {
final DateFormat pingCreationDateFormat = new SimpleDateFormat(
@ -130,27 +118,12 @@ public class TelemetrySyncPingBundleBuilder extends TelemetryPingBuilder {
syncs.add(ping.getPayload());
}
if (syncs.size() > 0) {
pingData.put("syncs", syncs);
}
pingData.put("syncs", syncs);
return this;
}
@SuppressWarnings("unchecked")
// Event telemetry will be implemented in Bug 1363924.
public TelemetrySyncPingBundleBuilder setSyncEventStore(TelemetryPingStore store) {
final JSONArray events = new JSONArray();
List<TelemetryPing> pings = store.getAllPings();
for (TelemetryPing ping : pings) {
try {
events.add(ping.getPayload().getArray("event"));
} catch (NonArrayJSONException ex) {
Log.e(LOG_TAG, "Invalid state: Non JSONArray for event payload.");
}
}
if (events.size() > 0) {
pingData.put("events", events);
}
return this;
}
}

Просмотреть файл

@ -0,0 +1,200 @@
/* 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();
}
}
}
}

Просмотреть файл

@ -0,0 +1,165 @@
/* 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);
}
}

Просмотреть файл

@ -0,0 +1,40 @@
/* 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);
}
}

Просмотреть файл

@ -110,13 +110,6 @@ public class AndroidFxAccount {
private static final String ACCOUNT_KEY_DEVICE_PUSH_REGISTRATION_ERROR = "devicePushRegistrationError";
private static final String ACCOUNT_KEY_DEVICE_PUSH_REGISTRATION_ERROR_TIME = "devicePushRegistrationErrorTime";
// We only see the hashed FxA UID once every sync.
// We might need it later for telemetry purposes outside of the context of a sync, which
// is why it is persisted.
// It is not expected to change during the lifetime of an account, but we set
// that value every time we see an FxA token nonetheless.
private static final String ACCOUNT_KEY_HASHED_FXA_UID = "hashedFxAUID";
// Account authentication token type for fetching account profile.
private static final String PROFILE_OAUTH_TOKEN_TYPE = "oauth::profile";
@ -1045,14 +1038,6 @@ public class AndroidFxAccount {
setDevicePushRegistrationError(0L, 0l);
}
public synchronized void setCachedHashedFxAUID(final String newHashedFxAUID) {
accountManager.setUserData(account, ACCOUNT_KEY_HASHED_FXA_UID, newHashedFxAUID);
}
public synchronized String getCachedHashedFxAUID() {
return accountManager.getUserData(account, ACCOUNT_KEY_HASHED_FXA_UID);
}
@SuppressLint("ParcelCreator") // The CREATOR field is defined in the super class.
private class ProfileResultReceiver extends ResultReceiver {
/* package-private */ ProfileResultReceiver(Handler handler) {

Просмотреть файл

@ -348,7 +348,6 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
@Override
public void handleSuccess(final TokenServerToken token) {
FxAccountUtils.pii(LOG_TAG, "Got token! uid is " + token.uid + " and endpoint is " + token.endpoint + ".");
fxAccount.setCachedHashedFxAUID(token.hashedFxaUid);
fxAccount.releaseSharedAccountStateLock();
if (!didReceiveBackoff) {

Просмотреть файл

@ -8,24 +8,15 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
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.fxa.authenticator.AndroidFxAccount;
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.mozilla.gecko.sync.telemetry.TelemetryEventCollector;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ -64,12 +55,10 @@ public class CommandProcessor {
public final String commandType;
public final JSONArray args;
private List<String> argsList;
@Nullable public String flowID;
public Command(String commandType, JSONArray args, @Nullable String flowID) {
public Command(String commandType, JSONArray args) {
this.commandType = commandType;
this.args = args;
this.flowID = flowID;
}
/**
@ -99,9 +88,6 @@ public class CommandProcessor {
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;
}
}
@ -141,20 +127,8 @@ public class CommandProcessor {
Logger.debug(LOG_TAG, "Command \"" + command.commandType + "\" not registered and will not be processed.");
return;
}
try {
recordProcessCommandTelemetryEvent(session.getContext(), command);
} catch (Exception e) {
Log.e(LOG_TAG, "Could not record telemetry event.");
}
executableCommand.executeCommand(session, command.getArgsList());
}
private static void recordProcessCommandTelemetryEvent(Context context, Command command) {
final HashMap<String, String> extra = new HashMap<>();
if (command.flowID != null) {
extra.put("flowID", command.flowID);
}
TelemetryEventCollector.recordEvent(context, "processcommand", command.commandType, null, extra);
executableCommand.executeCommand(session, command.getArgsList());
}
/**
@ -175,9 +149,8 @@ public class CommandProcessor {
if (unparsedArgs == null) {
return null;
}
final String flowID = unparsedCommand.getString("flowID");
return new Command(type, unparsedArgs, flowID);
return new Command(type, unparsedArgs);
} catch (NonArrayJSONException e) {
Logger.debug(LOG_TAG, "Unable to parse args array. Invalid command");
return null;
@ -196,8 +169,7 @@ public class CommandProcessor {
args.add(sender);
args.add(title);
final String flowID = Utils.generateGuid();
final Command displayURICommand = new Command("displayURI", args, flowID);
final Command displayURICommand = new Command("displayURI", args);
this.sendCommand(clientID, displayURICommand, context);
}
@ -250,39 +222,8 @@ public class CommandProcessor {
}
}
private static void recordSendCommandTelemetryEvent(Context context, Command command, String clientID) {
final AndroidFxAccount fxAccount = AndroidFxAccount.fromContext(context);
if (fxAccount == null) {
Log.e(LOG_TAG, "Can't record telemetry event: FxAccount doesn't exist.");
return;
}
final String hashedFxAUID = fxAccount.getCachedHashedFxAUID();
if (TextUtils.isEmpty(hashedFxAUID)) {
Log.e(LOG_TAG, "Can't record telemetry event: The hashed FxA UID is empty");
return;
}
HashMap<String, String> extra = new HashMap<>();
if (!TextUtils.isEmpty(command.flowID)) {
extra.put("flowID", command.flowID);
}
try {
extra.put("deviceID", Utils.byte2Hex(Utils.sha256(clientID.concat(hashedFxAUID).getBytes("UTF-8"))));
} catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
// Should not happen.
Log.e(LOG_TAG, "Either UTF-8 or SHA-256 are not supported", e);
}
TelemetryEventCollector.recordEvent(context, "sendcommand", command.commandType, null, extra);
}
protected void sendCommandToClient(String clientID, Command command, Context context) {
Logger.info(LOG_TAG, "Sending " + command.commandType + " to " + clientID);
try {
recordSendCommandTelemetryEvent(context, command, clientID);
} catch (Exception e) {
Log.e(LOG_TAG, "Could not record telemetry event.");
}
ClientsDatabaseAccessor db = new ClientsDatabaseAccessor(context);
try {

Просмотреть файл

@ -79,7 +79,7 @@ public class BaseResource implements Resource {
protected DefaultHttpClient client;
public ResourceDelegate delegate;
protected HttpRequestBase request;
public static final String charset = "utf-8";
public final String charset = "utf-8";
private boolean shouldGzipCompress = false;
// A hint whether uploaded payloads are chunked. Default true to use GzipCompressingEntity, which is built-in functionality.

Просмотреть файл

@ -19,7 +19,7 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
// Database Specifications.
protected static final String DB_NAME = "clients_database";
protected static final int SCHEMA_VERSION = 5;
protected static final int SCHEMA_VERSION = 4;
// Clients Table.
public static final String TBL_CLIENTS = "clients";
@ -46,9 +46,8 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
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, COL_FLOW_ID };
public static final String[] TBL_COMMANDS_COLUMNS = new String[] { COL_ACCOUNT_GUID, COL_COMMAND, COL_ARGS };
public static final String TBL_COMMANDS_KEY = COL_ACCOUNT_GUID + " = ? AND " +
COL_COMMAND + " = ? AND " +
COL_ARGS + " = ?";
@ -92,7 +91,6 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
+ 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);
@ -122,10 +120,6 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
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() {
@ -195,10 +189,9 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
* @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, String flowID) throws NullCursorException {
public void store(String accountGUID, String command, String args) throws NullCursorException {
if (Logger.LOG_PERSONAL_INFORMATION) {
Logger.pii(LOG_TAG, "Storing command " + command + " with args " + args);
} else {
@ -214,9 +207,6 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
} else {
cv.put(COL_ARGS, args);
}
if (flowID != null) {
cv.put(COL_FLOW_ID, flowID);
}
Cursor cur = this.fetchSpecificCommand(accountGUID, command, args);
try {
@ -239,8 +229,6 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
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();

Просмотреть файл

@ -50,7 +50,7 @@ public class ClientsDatabaseAccessor {
}
public void store(String accountGUID, Command command) throws NullCursorException {
db.store(accountGUID, command.commandType, command.args.toJSONString(), command.flowID);
db.store(accountGUID, command.commandType, command.args.toJSONString());
}
public ClientRecord fetchClient(String accountGUID) throws NullCursorException {
@ -195,10 +195,9 @@ public class ClientsDatabaseAccessor {
}
protected static Command commandFromCursor(Cursor cur) {
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);
String commandType = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_COMMAND);
JSONArray commandArgs = RepoUtils.getJSONArrayFromCursor(cur, ClientsDatabase.COL_ARGS);
return new Command(commandType, commandArgs);
}
public int clientsCount() {

Просмотреть файл

@ -25,11 +25,4 @@ public class TelemetryContract {
public static final String KEY_DEVICE_OS = "os";
public static final String KEY_DEVICE_VERSION = "version";
public static final String KEY_DEVICE_ID = "id";
public static final String KEY_EVENT_TIMESTAMP = "ts";
public static final String KEY_EVENT_CATEGORY = "category";
public static final String KEY_EVENT_METHOD = "method";
public static final String KEY_EVENT_OBJECT = "object";
public static final String KEY_EVENT_VALUE = "value";
public static final String KEY_EVENT_EXTRA = "extra";
}

Просмотреть файл

@ -1,141 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.telemetry;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
import org.mozilla.gecko.sync.net.BaseResource;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
public class TelemetryEventCollector {
private static final String LOG_TAG = "TelemetryEventCollector";
private static final String ACTION_BACKGROUND_TELEMETRY = "org.mozilla.gecko.telemetry.BACKGROUND";
private static final String EVENT_CATEGORY_SYNC = "sync"; // Max byte length: 30.
public static void recordEvent(final Context context, final String object, final String method,
@Nullable final String value,
@Nullable final HashMap<String, String> extra) {
if (!validateTelemetryEvent(object, method, value, extra)) {
throw new IllegalArgumentException("Could not validate telemetry event.");
}
Log.d(LOG_TAG, "Recording event {" + object + ", " + method + ", " + value + ", " + extra);
final Bundle event = new Bundle();
event.putLong(TelemetryContract.KEY_EVENT_TIMESTAMP, SystemClock.elapsedRealtime());
event.putString(TelemetryContract.KEY_EVENT_CATEGORY, EVENT_CATEGORY_SYNC);
event.putString(TelemetryContract.KEY_EVENT_OBJECT, object);
event.putString(TelemetryContract.KEY_EVENT_METHOD, method);
if (value != null) {
event.putString(TelemetryContract.KEY_EVENT_VALUE, value);
}
if (extra != null) {
final Bundle extraBundle = new Bundle();
for (Map.Entry<String, String> e : extra.entrySet()) {
extraBundle.putString(e.getKey(), e.getValue());
}
event.putBundle(TelemetryContract.KEY_EVENT_EXTRA, extraBundle);
}
if (!setIDs(context, event)) {
throw new IllegalStateException("UID and deviceID need to be set.");
}
final Intent telemetryIntent = new Intent();
telemetryIntent.setAction(ACTION_BACKGROUND_TELEMETRY);
telemetryIntent.putExtra(TelemetryContract.KEY_TYPE, TelemetryContract.KEY_TYPE_EVENT);
telemetryIntent.putExtra(TelemetryContract.KEY_TELEMETRY, event);
LocalBroadcastManager.getInstance(context).sendBroadcast(telemetryIntent);
}
// The Firefox Telemetry pipeline imposes size limits.
// See toolkit/components/telemetry/docs/collection/events.rst
@VisibleForTesting
static boolean validateTelemetryEvent(final String object, final String method,
@Nullable final String value, @Nullable final HashMap<String, String> extra) {
// The Telemetry Sender uses BaseResource under the hood.
final Charset charset = Charset.forName(BaseResource.charset);
// Length checks.
if (method.getBytes(charset).length > 20 ||
object.getBytes(charset).length > 20 ||
(value != null && value.getBytes(charset).length > 80)) {
Log.w(LOG_TAG, "Invalid event parameters - wrong lengths: " + method + " " +
object + " " + value);
return false;
}
if (extra != null) {
if (extra.size() > 10) {
Log.w(LOG_TAG, "Invalid event parameters - too many extra keys: " + extra);
return false;
}
for (Map.Entry<String, String> e : extra.entrySet()) {
if (e.getKey().getBytes(charset).length > 15 ||
e.getValue().getBytes(charset).length > 80) {
Log.w(LOG_TAG, "Invalid event parameters: extra item \"" + e.getKey() +
"\" is invalid: " + extra);
return false;
}
}
}
return true;
}
private static boolean setIDs(final Context context, final Bundle event) {
final SharedPreferences sharedPrefs;
final AndroidFxAccount fxAccount = AndroidFxAccount.fromContext(context);
if (fxAccount == null) {
Log.e(LOG_TAG, "Can't record telemetry event: FxAccount doesn't exist.");
return false;
}
try {
sharedPrefs = fxAccount.getSyncPrefs();
} catch (UnsupportedEncodingException | GeneralSecurityException e) {
Log.e(LOG_TAG, "Can't record telemetry event: Could not retrieve Sync Prefs", e);
return false;
}
final String hashedFxAUID = fxAccount.getCachedHashedFxAUID();
if (TextUtils.isEmpty(hashedFxAUID)) {
Log.e(LOG_TAG, "Can't record telemetry event: The hashed FxA UID is empty");
return false;
}
final ClientsDataDelegate clientsDataDelegate = new SharedPreferencesClientsDataDelegate(sharedPrefs, context);
try {
final String hashedDeviceID = Utils.byte2Hex(Utils.sha256(
clientsDataDelegate.getAccountGUID().concat(hashedFxAUID).getBytes("UTF-8")
));
event.putString(TelemetryContract.KEY_LOCAL_DEVICE_ID, hashedDeviceID);
} catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
// Should not happen.
Log.e(LOG_TAG, "Either UTF-8 or SHA-256 are not supported", e);
return false;
}
event.putString(TelemetryContract.KEY_LOCAL_UID, hashedFxAUID);
return true;
}
}

Просмотреть файл

@ -1,220 +0,0 @@
/* 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();
}
}
}
}

Просмотреть файл

@ -1,166 +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.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);
}
}

Просмотреть файл

@ -12,21 +12,21 @@ public class CommandHelpers {
public static Command getCommand1() {
JSONArray args = new JSONArray();
args.add("argsA");
return new Command("displayURI", args, null);
return new Command("displayURI", args);
}
@SuppressWarnings("unchecked")
public static Command getCommand2() {
JSONArray args = new JSONArray();
args.add("argsB");
return new Command("displayURI", args, null);
return new Command("displayURI", args);
}
@SuppressWarnings("unchecked")
public static Command getCommand3() {
JSONArray args = new JSONArray();
args.add("argsC");
return new Command("displayURI", args, null);
return new Command("displayURI", args);
}
@SuppressWarnings("unchecked")
@ -35,6 +35,6 @@ public class CommandHelpers {
args.add("URI of Page");
args.add("Sender ID");
args.add("Title of Page");
return new Command("displayURI", args, null);
return new Command("displayURI", args);
}
}

Просмотреть файл

@ -1,64 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.gecko.sync.telemetry;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner;
import java.util.HashMap;
@RunWith(TestRunner.class)
public class TelemetryEventCollectorTest {
@Test
public void testValidateTelemetryEvent() {
// object arg bytes len > 20.
Assert.assertFalse(TelemetryEventCollector.validateTelemetryEvent(repeat("c", 21), "met", null, null));
Assert.assertFalse(TelemetryEventCollector.validateTelemetryEvent(repeat("©", 11), "met", null, null));
// method arg bytes len > 20.
Assert.assertFalse(TelemetryEventCollector.validateTelemetryEvent("obj", repeat("c", 21), null, null));
Assert.assertFalse(TelemetryEventCollector.validateTelemetryEvent("obj", repeat("©", 11), null, null));
// val arg bytes len > 80.
Assert.assertFalse(TelemetryEventCollector.validateTelemetryEvent("obj", "met", repeat("c", 81), null));
Assert.assertFalse(TelemetryEventCollector.validateTelemetryEvent("obj", "met", repeat("©", 41), null));
// extra arg len > 10.
HashMap<String, String> extra = new HashMap<>();
for (int i = 0; i < 11; i++) {
extra.put("" + i, "" + i);
}
Assert.assertFalse(TelemetryEventCollector.validateTelemetryEvent("obj", "met", "val", extra));
// extra arg key len > 15.
extra.clear();
for (int i = 0; i < 9; i++) {
extra.put("" + i, "" + i);
}
extra.put("HashMapKeyInstanceFactory", "val");
Assert.assertFalse(TelemetryEventCollector.validateTelemetryEvent("obj", "met", "val", extra));
// extra arg val bytes len > 80.
extra.clear();
for (int i = 0; i < 8; i++) {
extra.put("" + i, "" + i);
}
extra.put("key", repeat("©", 41));
Assert.assertFalse(TelemetryEventCollector.validateTelemetryEvent("obj", "met", "val", extra));
// Happy case.
extra.clear();
for (int i = 0; i < 10; i++) {
extra.put(repeat(i + "", 15), repeat("" + i, 80));
}
Assert.assertTrue(TelemetryEventCollector.validateTelemetryEvent(repeat("c", 20), repeat("c", 20), repeat("c", 80), extra));
}
private static String repeat(String c, int times) {
return new String(new char[times]).replace("\0", c);
}
}

Просмотреть файл

@ -229,6 +229,7 @@ client. This is logically the "other end" of ``sendcommand``.
- value: Not used (ie, ``null``)
- extra: An object with the following attributes:
- deviceID: A GUID which identifies the device the command is being sent to.
- flowID: A GUID which uniquely identifies this command invocation. The value
for this GUID will be the same as the flowID sent to the client via
``sendcommand``.