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

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

@ -3,19 +3,16 @@
package org.mozilla.gecko.telemetry.pingbuilders; package org.mozilla.gecko.telemetry.pingbuilders;
import android.os.Bundle;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import org.json.JSONException; import org.json.JSONException;
import org.json.simple.JSONArray; import org.json.simple.JSONArray;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mozilla.gecko.background.testhelpers.TestRunner; import org.mozilla.gecko.background.testhelpers.TestRunner;
import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.telemetry.TelemetryContract;
import org.mozilla.gecko.telemetry.TelemetryOutgoingPing; import org.mozilla.gecko.telemetry.TelemetryOutgoingPing;
import org.mozilla.gecko.telemetry.TelemetryPing; import org.mozilla.gecko.telemetry.TelemetryPing;
import org.mozilla.gecko.telemetry.stores.TelemetryJSONFilePingStore; import org.mozilla.gecko.telemetry.stores.TelemetryJSONFilePingStore;
@ -108,8 +105,6 @@ public class TelemetrySyncPingBundleBuilderTest {
public void testGeneralShape() throws Exception { public void testGeneralShape() throws Exception {
builder.setSyncStore(syncPings); builder.setSyncStore(syncPings);
builder.setSyncEventStore(eventPings); builder.setSyncEventStore(eventPings);
builder.setDeviceID("device-id-1");
builder.setUID("uid-1");
TelemetryOutgoingPing outgoingPing = builder.build(); TelemetryOutgoingPing outgoingPing = builder.build();
@ -132,15 +127,15 @@ public class TelemetrySyncPingBundleBuilderTest {
assertTrue(application.containsKey("xpcomAbi")); assertTrue(application.containsKey("xpcomAbi"));
// Test general shape of payload. Expecting {"why":"schedule", "version": 1, // Test general shape of payload. Expecting {"syncs":[],"why":"schedule", "version": 1,
// "os": {"name": "Android", "version": "<version>", "locale": "<locale>"}, // "os": {"name": "Android", "version": "<version>", "locale": "<locale>"}}.
// "deviceID": <Hashed Device ID>, "uid": <Hashed UID>}. // 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"); ExtendedJSONObject payload = outgoingPing.getPayload().getObject("payload");
assertEquals(5, payload.keySet().size()); assertEquals(4, payload.keySet().size());
assertEquals("schedule", payload.getString("why")); assertEquals("schedule", payload.getString("why"));
assertEquals(Integer.valueOf(1), payload.getIntegerSafely("version")); assertEquals(Integer.valueOf(1), payload.getIntegerSafely("version"));
assertEquals(payload.getString("uid"), "uid-1"); assertEquals(0, payload.getArray("syncs").size());
assertEquals(payload.getString("deviceID"), "device-id-1");
// Test os key. // Test os key.
ExtendedJSONObject os = payload.getObject("os"); ExtendedJSONObject os = payload.getObject("os");
assertEquals(3, os.keySet().size()); assertEquals(3, os.keySet().size());
@ -156,8 +151,10 @@ public class TelemetrySyncPingBundleBuilderTest {
public void testBundlingOfMultiplePings() throws Exception { public void testBundlingOfMultiplePings() throws Exception {
// Try just one ping first. // Try just one ping first.
syncPings.storePing(new TelemetrySyncPingBuilder() syncPings.storePing(new TelemetrySyncPingBuilder()
.setDeviceID("test-device-id")
.setRestarted(true) .setRestarted(true)
.setTook(123L) .setTook(123L)
.setUID("test-uid")
.build() .build()
); );
builder.setSyncStore(syncPings); builder.setSyncStore(syncPings);
@ -169,12 +166,14 @@ public class TelemetrySyncPingBundleBuilderTest {
assertEquals("schedule", payload.getString("why")); assertEquals("schedule", payload.getString("why"));
JSONArray syncs = payload.getArray("syncs"); JSONArray syncs = payload.getArray("syncs");
assertEquals(1, syncs.size()); 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. // Add another ping.
syncPings.storePing(new TelemetrySyncPingBuilder() syncPings.storePing(new TelemetrySyncPingBuilder()
.setDeviceID("test-device-id")
.setRestarted(false) .setRestarted(false)
.setTook(321L) .setTook(321L)
.setUID("test-uid")
.build() .build()
); );
builder.setSyncStore(syncPings); builder.setSyncStore(syncPings);
@ -185,37 +184,14 @@ public class TelemetrySyncPingBundleBuilderTest {
.getObject("payload") .getObject("payload")
.getArray("syncs"); .getArray("syncs");
assertEquals(2, syncs.size()); assertEquals(2, syncs.size());
assertSync((ExtendedJSONObject) syncs.get(0), 123L, true); assertSync((ExtendedJSONObject) syncs.get(0), "test-uid", 123L, "test-device-id", true);
assertSync((ExtendedJSONObject) syncs.get(1), 321L, false); assertSync((ExtendedJSONObject) syncs.get(1), "test-uid", 321L, "test-device-id", 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());
} }
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(Long.valueOf(took), sync.getLong("took"));
assertEquals(deviceID, sync.getString("deviceID"));
// Test that 'when' timestamp looks generally sane. // Test that 'when' timestamp looks generally sane.
final long now = System.currentTimeMillis(); 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/SynchronizerConfiguration.java',
'sync/telemetry/TelemetryCollector.java', 'sync/telemetry/TelemetryCollector.java',
'sync/telemetry/TelemetryContract.java', 'sync/telemetry/TelemetryContract.java',
'sync/telemetry/TelemetryEventCollector.java',
'sync/telemetry/TelemetryStageCollector.java', 'sync/telemetry/TelemetryStageCollector.java',
'sync/ThreadPool.java', 'sync/ThreadPool.java',
'sync/UnexpectedJSONException.java', 'sync/UnexpectedJSONException.java',

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

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

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

@ -6,53 +6,19 @@ package org.mozilla.gecko.telemetry.pingbuilders;
import android.os.Bundle; 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; import org.mozilla.gecko.telemetry.TelemetryLocalPing;
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()} * Local ping builder which understands how to process event data.
* returns ExtendedJSONObject. Wrap our JSONArray into the payload JSON object. * This is a placeholder, to be implemented in Bug 1363924.
*/ */
payload.put("event", event); public class TelemetrySyncEventPingBuilder extends TelemetryLocalPingBuilder {
public TelemetrySyncEventPingBuilder fromEventTelemetry(Bundle data) {
return this; return this;
} }
@Override @Override
public TelemetryLocalPing build() { public TelemetryLocalPing build() {
return new TelemetryLocalPing(payload, docID); throw new UnsupportedOperationException();
} }
} }

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

@ -89,6 +89,16 @@ public class TelemetrySyncPingBuilder extends TelemetryLocalPingBuilder {
return this; 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 @Nullable
private static JSONArray buildOutgoing(List<StoreBatchTracker.Batch> batches) { private static JSONArray buildOutgoing(List<StoreBatchTracker.Batch> batches) {
if (batches == null || batches.size() == 0) { if (batches == null || batches.size() == 0) {

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

@ -8,13 +8,11 @@ package org.mozilla.gecko.telemetry.pingbuilders;
import android.os.Build; import android.os.Build;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.util.Log;
import org.json.simple.JSONArray; import org.json.simple.JSONArray;
import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.Locales; import org.mozilla.gecko.Locales;
import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.NonArrayJSONException;
import org.mozilla.gecko.telemetry.TelemetryOutgoingPing; import org.mozilla.gecko.telemetry.TelemetryOutgoingPing;
import org.mozilla.gecko.telemetry.TelemetryPing; import org.mozilla.gecko.telemetry.TelemetryPing;
import org.mozilla.gecko.telemetry.stores.TelemetryPingStore; 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". * 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: * Sample result will look something like:
* { * {
* "syncs": [list of syncs, as produced by the SyncBuilder], * "syncs": [list of syncs, as produced by the SyncBuilder],
@ -42,8 +42,6 @@ import java.util.TimeZone;
* } * }
*/ */
public class TelemetrySyncPingBundleBuilder extends TelemetryPingBuilder { public class TelemetrySyncPingBundleBuilder extends TelemetryPingBuilder {
public static final String LOG_TAG = "SyncPingBundleBuilder";
private static final String PING_TYPE = "sync"; private static final String PING_TYPE = "sync";
private static final int PING_BUNDLE_VERSION = 4; // Bug 1410145 private static final int PING_BUNDLE_VERSION = 4; // Bug 1410145
private static final int PING_SYNC_DATA_FORMAT_VERSION = 1; // Bug 1374758 private static final int PING_SYNC_DATA_FORMAT_VERSION = 1; // Bug 1374758
@ -71,16 +69,6 @@ public class TelemetrySyncPingBundleBuilder extends TelemetryPingBuilder {
return this; 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 @Override
public TelemetryOutgoingPing build() { public TelemetryOutgoingPing build() {
final DateFormat pingCreationDateFormat = new SimpleDateFormat( final DateFormat pingCreationDateFormat = new SimpleDateFormat(
@ -130,27 +118,12 @@ public class TelemetrySyncPingBundleBuilder extends TelemetryPingBuilder {
syncs.add(ping.getPayload()); syncs.add(ping.getPayload());
} }
if (syncs.size() > 0) {
pingData.put("syncs", syncs); pingData.put("syncs", syncs);
}
return this; return this;
} }
@SuppressWarnings("unchecked")
// Event telemetry will be implemented in Bug 1363924.
public TelemetrySyncPingBundleBuilder setSyncEventStore(TelemetryPingStore store) { 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; 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 = "devicePushRegistrationError";
private static final String ACCOUNT_KEY_DEVICE_PUSH_REGISTRATION_ERROR_TIME = "devicePushRegistrationErrorTime"; 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. // Account authentication token type for fetching account profile.
private static final String PROFILE_OAUTH_TOKEN_TYPE = "oauth::profile"; private static final String PROFILE_OAUTH_TOKEN_TYPE = "oauth::profile";
@ -1045,14 +1038,6 @@ public class AndroidFxAccount {
setDevicePushRegistrationError(0L, 0l); 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. @SuppressLint("ParcelCreator") // The CREATOR field is defined in the super class.
private class ProfileResultReceiver extends ResultReceiver { private class ProfileResultReceiver extends ResultReceiver {
/* package-private */ ProfileResultReceiver(Handler handler) { /* package-private */ ProfileResultReceiver(Handler handler) {

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

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

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

@ -8,24 +8,15 @@ import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; 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.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.db.BrowserContract; 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.NullCursorException;
import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor; import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
import org.mozilla.gecko.sync.repositories.domain.ClientRecord; 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.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -64,12 +55,10 @@ public class CommandProcessor {
public final String commandType; public final String commandType;
public final JSONArray args; public final JSONArray args;
private List<String> argsList; 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.commandType = commandType;
this.args = args; this.args = args;
this.flowID = flowID;
} }
/** /**
@ -99,9 +88,6 @@ public class CommandProcessor {
JSONObject out = new JSONObject(); JSONObject out = new JSONObject();
out.put("command", this.commandType); out.put("command", this.commandType);
out.put("args", this.args); out.put("args", this.args);
if (this.flowID != null) {
out.put("flowID", this.flowID);
}
return out; return out;
} }
} }
@ -141,20 +127,8 @@ public class CommandProcessor {
Logger.debug(LOG_TAG, "Command \"" + command.commandType + "\" not registered and will not be processed."); Logger.debug(LOG_TAG, "Command \"" + command.commandType + "\" not registered and will not be processed.");
return; 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) { executableCommand.executeCommand(session, command.getArgsList());
final HashMap<String, String> extra = new HashMap<>();
if (command.flowID != null) {
extra.put("flowID", command.flowID);
}
TelemetryEventCollector.recordEvent(context, "processcommand", command.commandType, null, extra);
} }
/** /**
@ -175,9 +149,8 @@ public class CommandProcessor {
if (unparsedArgs == null) { if (unparsedArgs == null) {
return null; return null;
} }
final String flowID = unparsedCommand.getString("flowID");
return new Command(type, unparsedArgs, flowID); return new Command(type, unparsedArgs);
} catch (NonArrayJSONException e) { } catch (NonArrayJSONException e) {
Logger.debug(LOG_TAG, "Unable to parse args array. Invalid command"); Logger.debug(LOG_TAG, "Unable to parse args array. Invalid command");
return null; return null;
@ -196,8 +169,7 @@ public class CommandProcessor {
args.add(sender); args.add(sender);
args.add(title); args.add(title);
final String flowID = Utils.generateGuid(); final Command displayURICommand = new Command("displayURI", args);
final Command displayURICommand = new Command("displayURI", args, flowID);
this.sendCommand(clientID, displayURICommand, context); 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) { protected void sendCommandToClient(String clientID, Command command, Context context) {
Logger.info(LOG_TAG, "Sending " + command.commandType + " to " + clientID); 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); ClientsDatabaseAccessor db = new ClientsDatabaseAccessor(context);
try { try {

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

@ -79,7 +79,7 @@ public class BaseResource implements Resource {
protected DefaultHttpClient client; protected DefaultHttpClient client;
public ResourceDelegate delegate; public ResourceDelegate delegate;
protected HttpRequestBase request; protected HttpRequestBase request;
public static final String charset = "utf-8"; public final String charset = "utf-8";
private boolean shouldGzipCompress = false; private boolean shouldGzipCompress = false;
// A hint whether uploaded payloads are chunked. Default true to use GzipCompressingEntity, which is built-in functionality. // 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. // Database Specifications.
protected static final String DB_NAME = "clients_database"; protected static final String DB_NAME = "clients_database";
protected static final int SCHEMA_VERSION = 5; protected static final int SCHEMA_VERSION = 4;
// Clients Table. // Clients Table.
public static final String TBL_CLIENTS = "clients"; 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 TBL_COMMANDS = "commands";
public static final String COL_COMMAND = "command"; public static final String COL_COMMAND = "command";
public static final String COL_ARGS = "args"; 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 " + public static final String TBL_COMMANDS_KEY = COL_ACCOUNT_GUID + " = ? AND " +
COL_COMMAND + " = ? AND " + COL_COMMAND + " = ? AND " +
COL_ARGS + " = ?"; COL_ARGS + " = ?";
@ -92,7 +91,6 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
+ COL_ACCOUNT_GUID + " TEXT, " + COL_ACCOUNT_GUID + " TEXT, "
+ COL_COMMAND + " TEXT, " + COL_COMMAND + " TEXT, "
+ COL_ARGS + " TEXT, " + COL_ARGS + " TEXT, "
+ COL_FLOW_ID + " TEXT, "
+ "PRIMARY KEY (" + COL_ACCOUNT_GUID + ", " + COL_COMMAND + ", " + COL_ARGS + "), " + "PRIMARY KEY (" + COL_ACCOUNT_GUID + ", " + COL_COMMAND + ", " + COL_ARGS + "), "
+ "FOREIGN KEY (" + COL_ACCOUNT_GUID + ") REFERENCES " + TBL_CLIENTS + " (" + COL_ACCOUNT_GUID + "))"; + "FOREIGN KEY (" + COL_ACCOUNT_GUID + ") REFERENCES " + TBL_CLIENTS + " (" + COL_ACCOUNT_GUID + "))";
db.execSQL(createCommandsTableSql); 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("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 + ")"); 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() { public void wipeDB() {
@ -195,10 +189,9 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
* @param accountGUID * @param accountGUID
* @param command - The command type * @param command - The command type
* @param args - A JSON string of args * @param args - A JSON string of args
* @param flowID - Optional - The flowID
* @throws NullCursorException * @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) { if (Logger.LOG_PERSONAL_INFORMATION) {
Logger.pii(LOG_TAG, "Storing command " + command + " with args " + args); Logger.pii(LOG_TAG, "Storing command " + command + " with args " + args);
} else { } else {
@ -214,9 +207,6 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
} else { } else {
cv.put(COL_ARGS, args); cv.put(COL_ARGS, args);
} }
if (flowID != null) {
cv.put(COL_FLOW_ID, flowID);
}
Cursor cur = this.fetchSpecificCommand(accountGUID, command, args); Cursor cur = this.fetchSpecificCommand(accountGUID, command, args);
try { try {
@ -239,8 +229,6 @@ public class ClientsDatabase extends CachedSQLiteOpenHelper {
return queryHelper.safeQuery(db, ".fetchClientsCursor", TBL_CLIENTS, TBL_CLIENTS_COLUMNS, TBL_CLIENTS_KEY, args); 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 { public Cursor fetchSpecificCommand(String accountGUID, String command, String commandArgs) throws NullCursorException {
String[] args = new String[] { accountGUID, command, commandArgs }; String[] args = new String[] { accountGUID, command, commandArgs };
SQLiteDatabase db = this.getCachedReadableDatabase(); SQLiteDatabase db = this.getCachedReadableDatabase();

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

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

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

@ -25,11 +25,4 @@ public class TelemetryContract {
public static final String KEY_DEVICE_OS = "os"; public static final String KEY_DEVICE_OS = "os";
public static final String KEY_DEVICE_VERSION = "version"; public static final String KEY_DEVICE_VERSION = "version";
public static final String KEY_DEVICE_ID = "id"; 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() { public static Command getCommand1() {
JSONArray args = new JSONArray(); JSONArray args = new JSONArray();
args.add("argsA"); args.add("argsA");
return new Command("displayURI", args, null); return new Command("displayURI", args);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static Command getCommand2() { public static Command getCommand2() {
JSONArray args = new JSONArray(); JSONArray args = new JSONArray();
args.add("argsB"); args.add("argsB");
return new Command("displayURI", args, null); return new Command("displayURI", args);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static Command getCommand3() { public static Command getCommand3() {
JSONArray args = new JSONArray(); JSONArray args = new JSONArray();
args.add("argsC"); args.add("argsC");
return new Command("displayURI", args, null); return new Command("displayURI", args);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -35,6 +35,6 @@ public class CommandHelpers {
args.add("URI of Page"); args.add("URI of Page");
args.add("Sender ID"); args.add("Sender ID");
args.add("Title of Page"); 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``) - value: Not used (ie, ``null``)
- extra: An object with the following attributes: - 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 - 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 for this GUID will be the same as the flowID sent to the client via
``sendcommand``. ``sendcommand``.