This commit is contained in:
Wes Kocher 2015-07-07 16:39:01 -07:00
Родитель bdf0f17d12 0246d86e61
Коммит bd15899795
31 изменённых файлов: 1050 добавлений и 451 удалений

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="86af137c7f4c41c1185e609339002ab47fd6c640"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="938375d0abafd92d78b3913db1735e49f46a6598"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="86af137c7f4c41c1185e609339002ab47fd6c640"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="938375d0abafd92d78b3913db1735e49f46a6598"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="86af137c7f4c41c1185e609339002ab47fd6c640"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="938375d0abafd92d78b3913db1735e49f46a6598"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>

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

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="86af137c7f4c41c1185e609339002ab47fd6c640"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="938375d0abafd92d78b3913db1735e49f46a6598"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="47f48a42502b1c694db2b162011a67a2ff888c5d"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="86af137c7f4c41c1185e609339002ab47fd6c640"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="938375d0abafd92d78b3913db1735e49f46a6598"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="86af137c7f4c41c1185e609339002ab47fd6c640"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="938375d0abafd92d78b3913db1735e49f46a6598"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="86af137c7f4c41c1185e609339002ab47fd6c640"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="938375d0abafd92d78b3913db1735e49f46a6598"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="86af137c7f4c41c1185e609339002ab47fd6c640"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="938375d0abafd92d78b3913db1735e49f46a6598"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "86af137c7f4c41c1185e609339002ab47fd6c640",
"git_revision": "938375d0abafd92d78b3913db1735e49f46a6598",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "e700d1e96a1185d51c9f5782e442dc0610a10d95",
"revision": "2c98eea0fc57337386cb7c527503b0950c117167",
"repo_path": "integration/gaia-central"
}

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

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="86af137c7f4c41c1185e609339002ab47fd6c640"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="938375d0abafd92d78b3913db1735e49f46a6598"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="47f48a42502b1c694db2b162011a67a2ff888c5d"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="86af137c7f4c41c1185e609339002ab47fd6c640"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="938375d0abafd92d78b3913db1735e49f46a6598"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -8,6 +8,7 @@ const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource:///modules/MigrationUtils.jsm");
@ -17,6 +18,45 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
"resource://gre/modules/Sqlite.jsm");
function copyToTempUTF8File(file, charset) {
let inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
.createInstance(Ci.nsIFileInputStream);
inputStream.init(file, -1, -1, 0);
let inputStr = NetUtil.readInputStreamToString(
inputStream, inputStream.available(), { charset });
// Use random to reduce the likelihood of a name collision in createUnique.
let rand = Math.floor(Math.random() * Math.pow(2, 15));
let leafName = "mozilla-temp-" + rand;
let tempUTF8File = FileUtils.getFile(
"TmpD", ["mozilla-temp-files", leafName], true);
tempUTF8File.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
let out = FileUtils.openAtomicFileOutputStream(tempUTF8File);
try {
let bufferedOut = Cc["@mozilla.org/network/buffered-output-stream;1"]
.createInstance(Ci.nsIBufferedOutputStream);
bufferedOut.init(out, 4096);
try {
let converterOut = Cc["@mozilla.org/intl/converter-output-stream;1"]
.createInstance(Ci.nsIConverterOutputStream);
converterOut.init(bufferedOut, "utf-8", 0, 0x0000);
try {
converterOut.writeString(inputStr || "");
bufferedOut.QueryInterface(Ci.nsISafeOutputStream).finish();
} finally {
converterOut.close();
}
} finally {
bufferedOut.close();
}
} finally {
out.close();
}
return tempUTF8File;
}
function parseINIStrings(file) {
let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
getService(Ci.nsIINIParserFactory);
@ -185,7 +225,13 @@ Object.defineProperty(Qihoo360seProfileMigrator.prototype, "sourceProfiles", {
if (!loginIni.isReadable()) {
throw new Error("360 Secure Browser's 'login.ini' file could not be read.");
}
let loginIniObj = parseINIStrings(loginIni);
let loginIniInUtf8 = copyToTempUTF8File(loginIni, "gbk");
let loginIniObj = parseINIStrings(loginIniInUtf8);
try {
loginIniInUtf8.remove(false);
} catch(ex) {}
let nowLoginEmail = loginIniObj.NowLogin && loginIniObj.NowLogin.email;
/*

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

@ -1541,6 +1541,7 @@ Toolbox.prototype = {
}
if (!item) {
item = this.doc.createElement("menuitem");
item.setAttribute("type", "radio");
item.setAttribute("data-window-id", win.id);
if (win.parentID) {
item.setAttribute("data-parent-id", win.parentID);

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

@ -175,7 +175,8 @@ public class SearchEngineBar extends TwoWayView
view = LayoutInflater.from(getContext()).inflate(R.layout.search_engine_bar_label, parent, false);
}
Drawable icon = DrawableCompat.wrap(ContextCompat.getDrawable(parent.getContext(), R.drawable.search_icon_active));
final Drawable icon = DrawableCompat.wrap(
ContextCompat.getDrawable(parent.getContext(), R.drawable.search_icon_active).mutate());
DrawableCompat.setTint(icon, getResources().getColor(R.color.disabled_grey));
final ImageView iconView = (ImageView) view.findViewById(R.id.search_engine_label);

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

@ -127,9 +127,11 @@ OnSharedPreferenceChangeListener
public static final String PREFS_OPEN_URLS_IN_PRIVATE = NON_PREF_PREFIX + "openExternalURLsPrivately";
public static final String PREFS_VOICE_INPUT_ENABLED = NON_PREF_PREFIX + "voice_input_enabled";
public static final String PREFS_QRCODE_ENABLED = NON_PREF_PREFIX + "qrcode_enabled";
private static final String PREFS_DEVTOOLS = NON_PREF_PREFIX + "devtools.enabled";
private static final String ACTION_STUMBLER_UPLOAD_PREF = AppConstants.ANDROID_PACKAGE_NAME + ".STUMBLER_PREF";
// This isn't a Gecko pref, even if it looks like one.
private static final String PREFS_BROWSER_LOCALE = "locale";
@ -692,6 +694,13 @@ OnSharedPreferenceChangeListener
continue;
}
}
if (PREFS_DEVTOOLS.equals(key) &&
RestrictedProfiles.isUserRestricted()) {
preferences.removePreference(pref);
i--;
continue;
}
setupPreferences((PreferenceGroup) pref, prefs);
} else {
pref.setOnPreferenceChangeListener(this);

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

@ -55,7 +55,8 @@
<PreferenceScreen android:title="@string/pref_category_devtools"
android:summary="@string/pref_developer_remotedebugging"
android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment"
android:key="android.not_a_preference.devtools.enabled" >
<extra android:name="resource"
android:value="preferences_devtools"/>
</PreferenceScreen>

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

@ -72,7 +72,8 @@
</intent>
</PreferenceScreen>
<PreferenceScreen android:title="@string/pref_category_devtools"
android:summary="@string/pref_developer_remotedebugging" >
android:summary="@string/pref_developer_remotedebugging"
android:key="android.not_a_preference.devtools.enabled" >
<intent android:action="android.intent.action.VIEW"
android:targetPackage="@string/android_package_name"
android:targetClass="org.mozilla.gecko.preferences.GeckoPreferences" >

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

@ -14,6 +14,7 @@ task:
env:
VARIANT: userdebug
DOGFOOD: 1
HARDWARE_COMPOSER: 0
MOZHARNESS_CONFIG: b2g/taskcluster-spark-dogfood.py
extra:
treeherderEnv:

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

@ -5254,6 +5254,12 @@
"n_values": 700,
"description": "The Shumway feature is used during movie execution"
},
"SHUMWAY_LOAD_RESOURCE_RESULT": {
"expires_in_version": "default",
"kind": "enumerated",
"n_values": 10,
"description": "The Shumway external resource loading result"
},
"SHUMWAY_FALLBACK": {
"expires_in_version": "default",
"kind": "boolean",

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

@ -122,6 +122,7 @@ function configureLogging() {
*/
let Policy = {
now: () => new Date(),
generatePingId: () => Utils.generateUUID(),
}
this.EXPORTED_SYMBOLS = ["TelemetryController"];
@ -143,12 +144,7 @@ this.TelemetryController = Object.freeze({
* Used only for testing purposes.
*/
reset: function() {
Impl._clientID = null;
Impl._detachObservers();
TelemetryStorage.reset();
TelemetrySend.reset();
return this.setup();
return Impl.reset();
},
/**
* Used only for testing purposes.
@ -190,13 +186,14 @@ this.TelemetryController = Object.freeze({
* @param {Boolean} [aOptions.addEnvironment=false] true if the ping should contain the
* environment data.
* @param {Object} [aOptions.overrideEnvironment=null] set to override the environment data.
* @returns {Promise} A promise that resolves with the ping id once the ping is stored or sent.
* @returns {Promise} Test-only - a promise that resolves with the ping id once the ping is stored or sent.
*/
submitExternalPing: function(aType, aPayload, aOptions = {}) {
aOptions.addClientId = aOptions.addClientId || false;
aOptions.addEnvironment = aOptions.addEnvironment || false;
return Impl.submitExternalPing(aType, aPayload, aOptions);
const testOnly = Impl.submitExternalPing(aType, aPayload, aOptions);
return testOnly;
},
/**
@ -416,7 +413,7 @@ let Impl = {
// Fill the common ping fields.
let pingData = {
type: aType,
id: Utils.generateUUID(),
id: Policy.generatePingId(),
creationDate: (Policy.now()).toISOString(),
version: PING_FORMAT_VERSION,
application: this._getApplicationSection(),
@ -456,7 +453,7 @@ let Impl = {
* @param {Boolean} [aOptions.addEnvironment=false] true if the ping should contain the
* environment data.
* @param {Object} [aOptions.overrideEnvironment=null] set to override the environment data.
* @returns {Promise} A promise that is resolved with the ping id once the ping is stored or sent.
* @returns {Promise} Test-only - a promise that is resolved with the ping id once the ping is stored or sent.
*/
submitExternalPing: function send(aType, aPayload, aOptions) {
this._log.trace("submitExternalPing - type: " + aType + ", aOptions: " + JSON.stringify(aOptions));
@ -860,4 +857,18 @@ let Impl = {
return ping;
},
reset: Task.async(function*() {
this._clientID = null;
this._detachObservers();
// We need to kick of the controller setup first for tests that check the
// cached client id.
let controllerSetup = this.setupTelemetry(true);
yield TelemetrySend.reset();
yield TelemetryStorage.reset();
yield controllerSetup;
}),
};

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

@ -54,23 +54,38 @@ const IS_UNIFIED_TELEMETRY = Preferences.get(PREF_UNIFIED, false);
const PING_FORMAT_VERSION = 4;
const MS_IN_A_MINUTE = 60 * 1000;
const PING_TYPE_DELETION = "deletion";
// We try to spread "midnight" pings out over this interval.
const MIDNIGHT_FUZZING_INTERVAL_MS = 60 * 60 * 1000;
const MIDNIGHT_FUZZING_INTERVAL_MS = 60 * MS_IN_A_MINUTE;
// We delay sending "midnight" pings on this client by this interval.
const MIDNIGHT_FUZZING_DELAY_MS = Math.random() * MIDNIGHT_FUZZING_INTERVAL_MS;
// Timeout after which we consider a ping submission failed.
const PING_SUBMIT_TIMEOUT_MS = 2 * 60 * 1000;
const PING_SUBMIT_TIMEOUT_MS = 1.5 * MS_IN_A_MINUTE;
// To keep resource usage in check, we limit ping sending to a maximum number
// of pings per minute.
const MAX_PING_SENDS_PER_MINUTE = 10;
// If we have more pending pings then we can send right now, we schedule the next
// send for after SEND_TICK_DELAY.
const SEND_TICK_DELAY = 1 * MS_IN_A_MINUTE;
// If we had any ping send failures since the last ping, we use a backoff timeout
// for the next ping sends. We increase the delay exponentially up to a limit of
// SEND_MAXIMUM_BACKOFF_DELAY_MS.
// This exponential backoff will be reset by external ping submissions & idle-daily.
const SEND_MAXIMUM_BACKOFF_DELAY_MS = 120 * MS_IN_A_MINUTE;
// Files that have been lying around for longer than MAX_PING_FILE_AGE are
// deleted without being loaded.
const MAX_PING_FILE_AGE = 14 * 24 * 60 * 60 * 1000; // 2 weeks
const MAX_PING_FILE_AGE = 14 * 24 * 60 * MS_IN_A_MINUTE; // 2 weeks
// Files that are older than OVERDUE_PING_FILE_AGE, but younger than
// MAX_PING_FILE_AGE indicate that we need to send all of our pings ASAP.
const OVERDUE_PING_FILE_AGE = 7 * 24 * 60 * 60 * 1000; // 1 week
const OVERDUE_PING_FILE_AGE = 7 * 24 * 60 * MS_IN_A_MINUTE; // 1 week
// Maximum number of pings to save.
const MAX_LRU_PINGS = 50;
@ -83,8 +98,8 @@ const MAX_LRU_PINGS = 50;
let Policy = {
now: () => new Date(),
midnightPingFuzzingDelay: () => MIDNIGHT_FUZZING_DELAY_MS,
setPingSendTimeout: (callback, delayMs) => setTimeout(callback, delayMs),
clearPingSendTimeout: (id) => clearTimeout(id),
setSchedulerTickTimeout: (callback, delayMs) => setTimeout(callback, delayMs),
clearSchedulerTickTimeout: (id) => clearTimeout(id),
};
/**
@ -137,6 +152,7 @@ function gzipCompressString(string) {
return observer.buffer;
}
this.TelemetrySend = {
/**
* Maximum age in ms of a pending ping file before it gets evicted.
@ -159,6 +175,10 @@ this.TelemetrySend = {
return MAX_LRU_PINGS;
},
get pendingPingCount() {
return TelemetrySendImpl.pendingPingCount;
},
/**
* Initializes this module.
*
@ -186,7 +206,7 @@ this.TelemetrySend = {
* - save the ping to disk and send it at the next opportunity
*
* @param {Object} ping The ping data to send, must be serializable to JSON.
* @return {Promise} A promise that is resolved when the ping is sent or saved.
* @return {Promise} Test-only - a promise that is resolved when the ping is sent or saved.
*/
submitPing: function(ping) {
return TelemetrySendImpl.submitPing(ping);
@ -226,8 +246,253 @@ this.TelemetrySend = {
testWaitOnOutgoingPings: function() {
return TelemetrySendImpl.promisePendingPingActivity();
},
/**
* Test-only - this allows overriding behavior to enable ping sending in debug builds.
*/
setTestModeEnabled: function(testing) {
TelemetrySendImpl.setTestModeEnabled(testing);
},
};
let CancellableTimeout = {
_deferred: null,
_timer: null,
/**
* This waits until either the given timeout passed or the timeout was cancelled.
*
* @param {Number} timeoutMs The timeout in ms.
* @return {Promise<bool>} Promise that is resolved with false if the timeout was cancelled,
* false otherwise.
*/
promiseWaitOnTimeout: function(timeoutMs) {
if (!this._deferred) {
this._deferred = PromiseUtils.defer();
this._timer = Policy.setSchedulerTickTimeout(() => this._onTimeout(), timeoutMs);
}
return this._deferred.promise;
},
_onTimeout: function() {
if (this._deferred) {
this._deferred.resolve(false);
this._timer = null;
this._deferred = null;
}
},
cancelTimeout: function() {
if (this._deferred) {
Policy.clearSchedulerTickTimeout(this._timer);
this._deferred.resolve(true);
this._timer = null;
this._deferred = null;
}
},
};
/**
* SendScheduler implements the timer & scheduling behavior for ping sends.
*/
let SendScheduler = {
// Whether any ping sends failed since the last tick. If yes, we start with our exponential
// backoff timeout.
_sendsFailed: false,
// The current retry delay after ping send failures. We use this for the exponential backoff,
// increasing this value everytime we had send failures since the last tick.
_backoffDelay: SEND_TICK_DELAY,
_shutdown: false,
_sendTask: null,
_logger: null,
get _log() {
if (!this._logger) {
this._logger = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX + "Scheduler::");
}
return this._logger;
},
shutdown: function() {
this._log.trace("shutdown");
this._shutdown = true;
CancellableTimeout.cancelTimeout();
return Promise.resolve(this._sendTask);
},
/**
* Only used for testing, resets the state to emulate a restart.
*/
reset: function() {
this._log.trace("reset");
return this.shutdown().then(() => {
this._sendsFailed = false;
this._backoffDelay = SEND_TICK_DELAY;
this._shutdown = false;
});
},
/**
* Notify the scheduler of a failure in sending out pings that warrants retrying.
* This will trigger the exponential backoff timer behavior on the next tick.
*/
notifySendsFailed: function() {
this._log.trace("notifySendsFailed");
if (this._sendsFailed) {
return;
}
this._sendsFailed = true;
this._log.trace("notifySendsFailed - had send failures");
},
/**
* Returns whether ping submissions are currently throttled.
*/
isThrottled: function() {
const now = Policy.now();
const nextPingSendTime = this._getNextPingSendTime(now);
return (nextPingSendTime > now.getTime());
},
waitOnSendTask: function() {
return Promise.resolve(this._sendTask);
},
triggerSendingPings: function(immediately) {
this._log.trace("triggerSendingPings - active send task: " + !!this._sendTask + ", immediately: " + immediately);
if (!this._sendTask) {
this._sendTask = this._doSendTask();
let clear = () => this._sendTask = null;
this._sendTask.then(clear, clear);
} else if (immediately) {
CancellableTimeout.cancelTimeout();
}
return this._sendTask;
},
_doSendTask: Task.async(function*() {
this._backoffDelay = SEND_TICK_DELAY;
this._sendsFailed = false;
const resetBackoffTimer = () => {
this._backoffDelay = SEND_TICK_DELAY;
};
for (;;) {
this._log.trace("_doSendTask iteration");
if (this._shutdown) {
this._log.trace("_doSendTask - shutting down, bailing out");
return;
}
// Get a list of pending pings, sorted by last modified, descending.
// Filter out all the pings we can't send now. This addresses scenarios like "deletion" pings
// which can be send even when upload is disabled.
let pending = TelemetryStorage.getPendingPingList();
let current = TelemetrySendImpl.getUnpersistedPings();
this._log.trace("_doSendTask - pending: " + pending.length + ", current: " + current.length);
pending = pending.filter(p => TelemetrySendImpl.canSend(p));
current = current.filter(p => TelemetrySendImpl.canSend(p));
this._log.trace("_doSendTask - can send - pending: " + pending.length + ", current: " + current.length);
// Bail out if there is nothing to send.
if ((pending.length == 0) && (current.length == 0)) {
this._log.trace("_doSendTask - no pending pings, bailing out");
return;
}
// If we are currently throttled (e.g. fuzzing to avoid midnight spikes), wait for the next send window.
const now = Policy.now();
if (this.isThrottled()) {
const nextPingSendTime = this._getNextPingSendTime(now);
this._log.trace("_doSendTask - throttled, delaying ping send to " + new Date(nextPingSendTime));
const delay = nextPingSendTime - now.getTime();
const cancelled = yield CancellableTimeout.promiseWaitOnTimeout(delay);
if (cancelled) {
this._log.trace("_doSendTask - throttling wait was cancelled, resetting backoff timer");
resetBackoffTimer();
}
continue;
}
let sending = pending.slice(0, MAX_PING_SENDS_PER_MINUTE);
pending = pending.slice(MAX_PING_SENDS_PER_MINUTE);
this._log.trace("_doSendTask - triggering sending of " + sending.length + " pings now" +
", " + pending.length + " pings waiting");
this._sendsFailed = false;
const sendStartTime = Policy.now();
yield TelemetrySendImpl.sendPings(current, [for (p of sending) p.id]);
if (this._shutdown || (TelemetrySend.pendingPingCount == 0)) {
this._log.trace("_doSendTask - bailing out after sending, shutdown: " + this._shutdown +
", pendingPingCount: " + TelemetrySend.pendingPingCount);
return;
}
// Calculate the delay before sending the next batch of pings.
// We start with a delay that makes us send max. 1 batch per minute.
// If we had send failures in the last batch, we will override this with
// a backoff delay.
const timeSinceLastSend = Policy.now() - sendStartTime;
let nextSendDelay = Math.max(0, SEND_TICK_DELAY - timeSinceLastSend);
if (!this._sendsFailed) {
this._log.trace("_doSendTask - had no send failures, resetting backoff timer");
resetBackoffTimer();
} else {
const newDelay = Math.min(SEND_MAXIMUM_BACKOFF_DELAY_MS,
this._backoffDelay * 2);
this._log.trace("_doSendTask - had send failures, backing off -" +
" old timeout: " + this._backoffDelay +
", new timeout: " + newDelay);
this._backoffDelay = newDelay;
nextSendDelay = this._backoffDelay;
}
this._log.trace("_doSendTask - waiting for next send opportunity, timeout is " + nextSendDelay)
const cancelled = yield CancellableTimeout.promiseWaitOnTimeout(nextSendDelay);
if (cancelled) {
this._log.trace("_doSendTask - batch send wait was cancelled, resetting backoff timer");
resetBackoffTimer();
}
}
}),
/**
* This helper calculates the next time that we can send pings at.
* Currently this mostly redistributes ping sends from midnight until one hour after
* to avoid submission spikes around local midnight for daily pings.
*
* @param now Date The current time.
* @return Number The next time (ms from UNIX epoch) when we can send pings.
*/
_getNextPingSendTime: function(now) {
// 1. First we check if the time is between 0am and 1am. If it's not, we send
// immediately.
// 2. If we confirmed the time is indeed between 0am and 1am in step 1, we disallow
// sending before (midnight + fuzzing delay), which is a random time between 0am-1am
// (decided at startup).
const midnight = Utils.truncateToDays(now);
// Don't delay pings if we are not within the fuzzing interval.
if ((now.getTime() - midnight.getTime()) > MIDNIGHT_FUZZING_INTERVAL_MS) {
return now.getTime();
}
// Delay ping send if we are within the midnight fuzzing range.
// We spread those ping sends out between |midnight| and |midnight + midnightPingFuzzingDelay|.
return midnight.getTime() + Policy.midnightPingFuzzingDelay();
},
};
let TelemetrySendImpl = {
_sendingEnabled: false,
_logger: null,
@ -239,11 +504,11 @@ let TelemetrySendImpl = {
_pendingPingActivity: new Set(),
// This is true when running in the test infrastructure.
_testMode: false,
// This holds pings that we currently try and haven't persisted yet.
_currentPings: new Map(),
// Count of pending pings we discarded for age on startup.
_discardedPingsCount: 0,
// Count of pending pings we evicted for being over the limit on startup.
_evictedPingsCount: 0,
// Count of pending pings that were overdue.
_overduePingCount: 0,
@ -267,6 +532,18 @@ let TelemetrySendImpl = {
return this._overduePingCount;
},
get pendingPingRequests() {
return this._pendingPingRequests;
},
get pendingPingCount() {
return TelemetryStorage.getPendingPingList().length + this._currentPings.size;
},
setTestModeEnabled: function(testing) {
this._testMode = testing;
},
setup: Task.async(function*(testing) {
this._log.trace("setup");
@ -274,22 +551,20 @@ let TelemetrySendImpl = {
this._sendingEnabled = true;
this._discardedPingsCount = 0;
this._evictedPingsCount = 0;
Services.obs.addObserver(this, TOPIC_IDLE_DAILY, false);
this._server = Preferences.get(PREF_SERVER, undefined);
// If any pings were submitted before the delayed init finished
// we will send them now. We don't wait on sending as this could take some time.
this._sendPersistedPings();
// Check the pending pings on disk now.
let haveOverduePings = yield this._checkPendingPings();
if (haveOverduePings) {
// We don't wait on sending as this could take some time.
this._sendPersistedPings();
try {
yield this._checkPendingPings();
} catch (ex) {
this._log.error("setup - _checkPendingPings rejected", ex);
}
// Start sending pings, but don't block on this.
SendScheduler.triggerSendingPings(true);
}),
/**
@ -331,7 +606,7 @@ let TelemetrySendImpl = {
for (let info of shouldEvict) {
try {
yield TelemetryStorage.removePendingPing(info.id);
++this._evictedPingsCount;
++evictedCount;
} catch(ex) {
this._log.error("_checkPendingPings - failed to evict ping", ex);
}
@ -344,39 +619,40 @@ let TelemetrySendImpl = {
const overduePings = infos.filter((info) =>
(now.getTime() - info.lastModificationDate) > OVERDUE_PING_FILE_AGE);
this._overduePingCount = overduePings.length;
if (overduePings.length > 0) {
this._log.trace("_checkForOverduePings - Have " + overduePings.length +
" overdue pending pings, ready to send " + infos.length +
" pings now.");
return true;
}
return false;
}),
shutdown: Task.async(function*() {
for (let topic of this.OBSERVER_TOPICS) {
Services.obs.removeObserver(this, topic);
try {
Services.obs.removeObserver(this, topic);
} catch (ex) {
this._log.error("shutdown - failed to remove observer for " + topic, ex);
}
}
// We can't send anymore now.
this._sendingEnabled = false;
// Clear scheduled ping sends.
this._clearPingSendTimer();
// Cancel any outgoing requests.
yield this._cancelOutgoingRequests();
// ... and wait for any outstanding async ping activity.
// Stop any active send tasks.
yield SendScheduler.shutdown();
// Wait for any outstanding async ping activity.
yield this.promisePendingPingActivity();
// Save any outstanding pending pings to disk.
yield this._persistCurrentPings();
}),
reset: function() {
this._log.trace("reset");
this._currentPings = new Map();
this._overduePingCount = 0;
this._discardedPingsCount = 0;
this._evictedPingsCount = 0;
const histograms = [
"TELEMETRY_SUCCESS",
@ -386,53 +662,39 @@ let TelemetrySendImpl = {
];
histograms.forEach(h => Telemetry.getHistogramById(h).clear());
return SendScheduler.reset();
},
observe: function(subject, topic, data) {
switch(topic) {
case TOPIC_IDLE_DAILY:
this._sendPersistedPings();
SendScheduler.triggerSendingPings(true);
break;
}
},
submitPing: function(ping) {
if (!this._canSend(ping)) {
this._log.trace("submitPing - ping id: " + ping.id);
if (!this.canSend(ping)) {
this._log.trace("submitPing - Telemetry is not allowed to send pings.");
return Promise.resolve();
}
// Check if we can send pings now.
const now = Policy.now();
const nextPingSendTime = this._getNextPingSendTime(now);
const throttled = (nextPingSendTime > now.getTime());
// We can't send pings now, schedule a later send.
if (throttled) {
this._log.trace("submitPing - throttled, delaying ping send to " + new Date(nextPingSendTime));
this._reschedulePingSendTimer(nextPingSendTime);
}
if (!this._sendingEnabled || throttled) {
// Sending is disabled or throttled, add this to the pending pings.
this._log.trace("submitPing - ping is pending, sendingEnabled: " + this._sendingEnabled +
", throttled: " + throttled);
if (!this._sendingEnabled) {
// Sending is disabled or throttled, add this to the persisted pending pings.
this._log.trace("submitPing - can't send ping now, persisting to disk - " +
"sendingEnabled: " + this._sendingEnabled);
return TelemetryStorage.savePendingPing(ping);
}
// Try to send the ping, persist it if sending it fails.
this._log.trace("submitPing - already initialized, ping will be sent");
let ps = [];
ps.push(this._doPing(ping, ping.id, false)
.catch((ex) => {
this._log.info("submitPing - ping not sent, saving to disk", ex);
TelemetryStorage.savePendingPing(ping);
}));
ps.push(this._sendPersistedPings());
return Promise.all([for (p of ps) p.catch((ex) => {
this._log.error("submitPing - ping activity had an error", ex);
})]);
// Let the scheduler trigger sending pings if possible.
// As a safety mechanism, this resets any currently active throttling.
this._log.trace("submitPing - can send pings, trying to send now");
this._currentPings.set(ping.id, ping);
SendScheduler.triggerSendingPings(true);
return Promise.resolve();
},
/**
@ -445,80 +707,76 @@ let TelemetrySendImpl = {
_cancelOutgoingRequests: function() {
// Abort any pending ping XHRs.
for (let [url, request] of this._pendingPingRequests) {
this._log.trace("_cancelOutgoingRequests - aborting ping request for " + url);
for (let [id, request] of this._pendingPingRequests) {
this._log.trace("_cancelOutgoingRequests - aborting ping request for id " + id);
try {
request.abort();
} catch (e) {
this._log.error("_cancelOutgoingRequests - failed to abort request to " + url, e);
this._log.error("_cancelOutgoingRequests - failed to abort request for id " + id, e);
}
}
this._pendingPingRequests.clear();
},
/**
* This helper calculates the next time that we can send pings at.
* Currently this mostly redistributes ping sends from midnight until one hour after
* to avoid submission spikes around local midnight for daily pings.
*
* @param now Date The current time.
* @return Number The next time (ms from UNIX epoch) when we can send pings.
*/
_getNextPingSendTime: function(now) {
// 1. First we check if the time is between 0am and 1am. If it's not, we send
// immediately.
// 2. If we confirmed the time is indeed between 0am and 1am in step 1, we disallow
// sending before (midnight + fuzzing delay), which is a random time between 0am-1am
// (decided at startup).
sendPings: function(currentPings, persistedPingIds) {
let pingSends = [];
const midnight = Utils.truncateToDays(now);
// Don't delay pings if we are not within the fuzzing interval.
if ((now.getTime() - midnight.getTime()) > MIDNIGHT_FUZZING_INTERVAL_MS) {
return now.getTime();
for (let current of currentPings) {
let ping = current;
let p = Task.spawn(function*() {
try {
yield this._doPing(ping, ping.id, false);
} catch (ex) {
this._log.info("sendPings - ping " + ping.id + " not sent, saving to disk", ex);
yield TelemetryStorage.savePendingPing(ping);
} finally {
this._currentPings.delete(ping.id);
}
}.bind(this));
this._trackPendingPingTask(p);
pingSends.push(p);
}
// Delay ping send if we are within the midnight fuzzing range.
// We spread those ping sends out between |midnight| and |midnight + midnightPingFuzzingDelay|.
return midnight.getTime() + Policy.midnightPingFuzzingDelay();
if (persistedPingIds.length > 0) {
pingSends.push(this._sendPersistedPings(persistedPingIds).catch((ex) => {
this._log.info("sendPings - persisted pings not sent", ex);
}));
}
return Promise.all(pingSends);
},
/**
* Send the persisted pings to the server.
*
* @param {Array<string>} List of ping ids that should be sent.
*
* @return Promise A promise that is resolved when all pings finished sending or failed.
*/
_sendPersistedPings: Task.async(function*() {
this._log.trace("_sendPersistedPings - Can send: " + this._canSend());
_sendPersistedPings: Task.async(function*(pingIds) {
this._log.trace("sendPersistedPings");
if (TelemetryStorage.pendingPingCount < 1) {
this._log.trace("_sendPersistedPings - no pings to send");
return Promise.resolve();
return;
}
if (!this._canSend()) {
this._log.trace("_sendPersistedPings - Telemetry is not allowed to send pings.");
return Promise.resolve();
}
// Check if we can send pings now - otherwise schedule a later send.
const now = Policy.now();
const nextPingSendTime = this._getNextPingSendTime(now);
if (nextPingSendTime > now.getTime()) {
this._log.trace("_sendPersistedPings - delaying ping send to " + new Date(nextPingSendTime));
this._reschedulePingSendTimer(nextPingSendTime);
return Promise.resolve();
if (pingIds.length < 1) {
this._log.trace("sendPersistedPings - no pings to send");
return;
}
// We can send now.
const pendingPings = TelemetryStorage.getPendingPingList();
this._log.trace("_sendPersistedPings - sending " + pendingPings.length + " pings");
// If there are any send failures, _doPing() sets up handlers that e.g. trigger backoff timer behavior.
this._log.trace("sendPersistedPings - sending " + pingIds.length + " pings");
let pingSendPromises = [];
for (let ping of pendingPings) {
let p = ping;
for (let pingId of pingIds) {
const id = pingId;
pingSendPromises.push(
TelemetryStorage.loadPendingPing(p.id)
.then((data) => this._doPing(data, p.id, true)
.catch(e => this._log.error("_sendPersistedPings - _doPing rejected", e))));
TelemetryStorage.loadPendingPing(id)
.then((data) => this._doPing(data, id, true))
.catch(e => this._log.error("sendPersistedPings - failed to send ping " + id, e)));
}
let promise = Promise.all(pingSendPromises);
@ -536,6 +794,11 @@ let TelemetrySendImpl = {
hsuccess.add(success);
hping.add(new Date() - startTime);
if (!success) {
// Let the scheduler know about send failures for triggering backoff timeouts.
SendScheduler.notifySendsFailed();
}
if (success && isPersisted) {
return TelemetryStorage.removePendingPing(id);
} else {
@ -577,14 +840,15 @@ let TelemetrySendImpl = {
},
_doPing: function(ping, id, isPersisted) {
if (!this._canSend(ping)) {
if (!this.canSend(ping)) {
// We can't send the pings to the server, so don't try to.
this._log.trace("_doPing - Sending is disabled.");
this._log.trace("_doPing - Can't send ping " + ping.id);
return Promise.resolve();
}
this._log.trace("_doPing - server: " + this._server + ", persisted: " + isPersisted +
", id: " + id);
const isNewPing = isV4PingFormat(ping);
const version = isNewPing ? PING_FORMAT_VERSION : 1;
const url = this._server + this._getSubmissionPath(ping) + "?v=" + version;
@ -598,7 +862,7 @@ let TelemetrySendImpl = {
request.overrideMimeType("text/plain");
request.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
this._pendingPingRequests.set(url, request);
this._pendingPingRequests.set(id, request);
let startTime = new Date();
let deferred = PromiseUtils.defer();
@ -612,11 +876,11 @@ let TelemetrySendImpl = {
}
};
this._pendingPingRequests.delete(url);
this._pendingPingRequests.delete(id);
this._onPingRequestFinished(success, startTime, id, isPersisted)
.then(() => onCompletion(),
(error) => {
this._log.error("_doPing - request success: " + success + ", error" + error);
this._log.error("_doPing - request success: " + success + ", error: " + error);
onCompletion();
});
};
@ -650,7 +914,7 @@ let TelemetrySendImpl = {
this._log.error("_doPing - error submitting to " + url + ", status: " + status
+ " - server error, should retry later");
} else {
// We received an unexpected status codes.
// We received an unexpected status code.
this._log.error("_doPing - error submitting to " + url + ", status: " + status
+ ", type: " + event.type);
}
@ -688,7 +952,7 @@ let TelemetrySendImpl = {
* @param {Object} [ping=null] A ping to be checked.
* @return {Boolean} True if pings can be send to the servers, false otherwise.
*/
_canSend: function(ping = null) {
canSend: function(ping = null) {
// We only send pings from official builds, but allow overriding this for tests.
if (!Telemetry.isOfficialTelemetry && !this._testMode) {
return false;
@ -708,19 +972,6 @@ let TelemetrySendImpl = {
return Preferences.get(PREF_TELEMETRY_ENABLED, false);
},
_reschedulePingSendTimer: function(timestamp) {
this._clearPingSendTimer();
const interval = timestamp - Policy.now();
this._pingSendTimer = Policy.setPingSendTimeout(() => this._sendPersistedPings(), interval);
},
_clearPingSendTimer: function() {
if (this._pingSendTimer) {
Policy.clearPingSendTimeout(this._pingSendTimer);
this._pingSendTimer = null;
}
},
/**
* Track any pending ping send and save tasks through the promise passed here.
* This is needed to block shutdown on any outstanding ping activity.
@ -738,8 +989,32 @@ let TelemetrySendImpl = {
*/
promisePendingPingActivity: function () {
this._log.trace("promisePendingPingActivity - Waiting for ping task");
return Promise.all([for (p of this._pendingPingActivity) p.catch(ex => {
let p = [for (p of this._pendingPingActivity) p.catch(ex => {
this._log.error("promisePendingPingActivity - ping activity had an error", ex);
})]);
})];
p.push(SendScheduler.waitOnSendTask());
return Promise.all(p);
},
_persistCurrentPings: Task.async(function*() {
for (let [id, ping] of this._currentPings) {
try {
yield TelemetryStorage.savePendingPing(ping);
this._log.trace("_persistCurrentPings - saved ping " + id);
} catch (ex) {
this._log.error("_persistCurrentPings - failed to save ping " + id, ex);
} finally {
this._currentPings.delete(id);
}
}
}),
/**
* Returns the current pending, not yet persisted, pings, newest first.
*/
getUnpersistedPings: function() {
let current = [...this._currentPings.values()];
current.reverse();
return current;
},
};

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

@ -55,8 +55,6 @@ const ARCHIVE_SIZE_PROBE_SPECIAL_VALUE = 300;
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
let isPingDirectoryCreated = false;
/**
* This is a policy object used to override behavior for testing.
*/
@ -909,19 +907,18 @@ let TelemetryStorageImpl = {
* compression will be used.
* @returns {promise}
*/
savePingToFile: function(ping, filePath, overwrite, compress = false) {
return Task.spawn(function*() {
try {
let pingString = JSON.stringify(ping);
let options = { tmpPath: filePath + ".tmp", noOverwrite: !overwrite };
if (compress) {
options.compression = "lz4";
}
yield OS.File.writeAtomic(filePath, pingString, options);
} catch(e if e.becauseExists) {
savePingToFile: Task.async(function*(ping, filePath, overwrite, compress = false) {
try {
this._log.trace("savePingToFile - path: " + filePath);
let pingString = JSON.stringify(ping);
let options = { tmpPath: filePath + ".tmp", noOverwrite: !overwrite };
if (compress) {
options.compression = "lz4";
}
})
},
yield OS.File.writeAtomic(filePath, pingString, options);
} catch(e if e.becauseExists) {
}
}),
/**
* Save a ping to its file.
@ -986,6 +983,7 @@ let TelemetryStorageImpl = {
path: path,
lastModificationDate: Policy.now().getTime(),
});
this._log.trace("savePendingPing - saved ping with id " + ping.id);
});
},
@ -1024,14 +1022,15 @@ let TelemetryStorageImpl = {
return Promise.resolve(this._buildPingList());
}
// Make sure to clear the task once done.
let clear = pings => {
// Since there's no pending pings scan task running, start it.
// Also make sure to clear the task once done.
this._scanPendingPingsTask = this._scanPendingPings().then(pings => {
this._scanPendingPingsTask = null;
return pings;
};
// Since there's no pending pings scan task running, start it.
this._scanPendingPingsTask = this._scanPendingPings().then(clear, clear);
}, ex => {
this._scanPendingPingsTask = null;
throw ex;
});
return this._scanPendingPingsTask;
},
@ -1059,16 +1058,23 @@ let TelemetryStorageImpl = {
return [];
}
let info = yield OS.File.stat(file.path);
let info;
try {
info = yield OS.File.stat(file.path);
} catch (ex) {
this._log.error("_scanPendingPings - failed to stat file " + file.path, ex);
continue;
}
let id = OS.Path.basename(file.path);
if (!UUID_REGEX.test(id)) {
this._log.trace("_scanPendingPings - unknown filename is not a UUID: " + id);
this._log.trace("_scanPendingPings - filename is not a UUID: " + id);
id = Utils.generateUUID();
}
this._pendingPings.set(id, {
path: file.path,
lastModificationDate: info.lastModificationDate,
lastModificationDate: info.lastModificationDate.getTime(),
});
}
@ -1241,9 +1247,8 @@ function getPingDirectory() {
return Task.spawn(function*() {
let directory = TelemetryStorage.pingDirectoryPath;
if (!isPingDirectoryCreated) {
if (!(yield OS.File.exists(directory))) {
yield OS.File.makeDir(directory, { unixMode: OS.Constants.S_IRWXU });
isPingDirectoryCreated = true;
}
return directory;

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

@ -14,11 +14,23 @@ It contains some basic information shared between different ping types, the :doc
Submission
==========
Pings are submitted via a common API on ``TelemetryController``. It allows callers to choose a custom retention period that determines how long pings are kept on disk if submission wasn't successful.
If a ping failed to submit (e.g. because of missing internet connection), Telemetry will retry to submit it until its retention period is up.
Pings are submitted via a common API on ``TelemetryController``.
If a ping fails to successfully submit to the server immediately (e.g. because
of missing internet connection), Telemetry will store it on disk and retry to
send it until the maximum ping age is exceeded (14 days).
*Note:* the :doc:`main pings <main-ping>` are kept locally even after successful submission to enable the HealthReport and SelfSupport features. They will be deleted after their retention period of 180 days.
Sending of pending pings starts as soon as the delayed startup is finished. They are sent in batches, newest-first, with up
to 10 persisted pings per batch plus all unpersisted pings.
The send logic then waits for each batch to complete.
If it succeeds we trigger the next send of a ping batch. This is delayed as needed to only trigger one batch send per minute.
If ping sending encounters an error that means retrying later, a backoff timeout behavior is
triggered, exponentially increasing the timeout for the next try from 1 minute up to a limit of 120 minutes.
Any new ping submissions and "idle-daily" events reset this behavior as a safety mechanism and trigger immediate ping sending.
The telemetry server team is working towards `the common services status codes <https://wiki.mozilla.org/CloudServices/DataPipeline/HTTPEdgeServerSpecification#Server_Responses>`_, but for now the following logic is sufficient for Telemetry:
* `2XX` - success, don't resubmit

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

@ -5,6 +5,9 @@ const { classes: Cc, utils: Cu, interfaces: Ci, results: Cr } = Components;
Cu.import("resource://gre/modules/TelemetryController.jsm", this);
Cu.import("resource://gre/modules/Services.jsm", this);
Cu.import("resource://gre/modules/PromiseUtils.jsm", this);
Cu.import("resource://gre/modules/Task.jsm", this);
Cu.import("resource://testing-common/httpd.js", this);
const gIsWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
const gIsMac = ("@mozilla.org/xpcom/mac-utils;1" in Cc);
@ -22,6 +25,78 @@ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12
let gOldAppInfo = null;
let gGlobalScope = this;
const PingServer = {
_httpServer: null,
_started: false,
_defers: [ PromiseUtils.defer() ],
_currentDeferred: 0,
get port() {
return this._httpServer.identity.primaryPort;
},
get started() {
return this._started;
},
registerPingHandler: function(handler) {
const wrapped = wrapWithExceptionHandler(handler);
this._httpServer.registerPrefixHandler("/submit/telemetry/", wrapped);
},
resetPingHandler: function() {
this.registerPingHandler((request, response) => {
let deferred = this._defers[this._defers.length - 1];
this._defers.push(PromiseUtils.defer());
deferred.resolve(request);
});
},
start: function() {
this._httpServer = new HttpServer();
this._httpServer.start(-1);
this._started = true;
this.clearRequests();
this.resetPingHandler();
},
stop: function() {
return new Promise(resolve => {
this._httpServer.stop(resolve);
this._started = false;
});
},
clearRequests: function() {
this._defers = [ PromiseUtils.defer() ];
this._currentDeferred = 0;
},
promiseNextRequest: function() {
const deferred = this._defers[this._currentDeferred++];
return deferred.promise;
},
promiseNextPing: function() {
return this.promiseNextRequest().then(request => decodeRequestPayload(request));
},
promiseNextRequests: Task.async(function*(count) {
let results = [];
for (let i=0; i<count; ++i) {
results.push(yield this.promiseNextRequest());
}
return results;
}),
promiseNextPings: function(count) {
return this.promiseNextRequests(count).then(requests => {
return [for (req of requests) decodeRequestPayload(req)];
});
},
};
/**
* Decode the payload of an HTTP request into a ping.
* @param {Object} request The data representing an HTTP request (nsIHttpRequest).
@ -63,6 +138,19 @@ function decodeRequestPayload(request) {
return payload;
}
function wrapWithExceptionHandler(f) {
function wrapper(...args) {
try {
f(...args);
} catch (ex if typeof(ex) == 'object') {
dump("Caught exception: " + ex.message + "\n");
dump(ex.stack);
do_test_finished();
}
}
return wrapper;
}
function loadAddonManager(id, name, version, platformVersion) {
let ns = {};
Cu.import("resource://gre/modules/Services.jsm", ns);
@ -162,8 +250,9 @@ function fakeNow(...args) {
// Fake the timeout functions for TelemetryController sending.
function fakePingSendTimer(set, clear) {
let module = Cu.import("resource://gre/modules/TelemetrySend.jsm");
module.Policy.setPingSendTimeout = set;
module.Policy.clearPingSendTimeout = clear;
let obj = Cu.cloneInto({set, clear}, module, {cloneFunctions:true});
module.Policy.setSchedulerTickTimeout = obj.set;
module.Policy.clearSchedulerTickTimeout = obj.clear;
}
function fakeMidnightPingFuzzingDelay(delayMs) {
@ -171,6 +260,11 @@ function fakeMidnightPingFuzzingDelay(delayMs) {
module.Policy.midnightPingFuzzingDelay = () => delayMs;
}
function fakeGeneratePingId(func) {
let module = Cu.import("resource://gre/modules/TelemetryController.jsm");
module.Policy.generatePingId = func;
}
// Return a date that is |offset| ms in the future from |date|.
function futureDate(date, offset) {
return new Date(date.getTime() + offset);
@ -191,12 +285,18 @@ if (runningInParent) {
Services.prefs.setCharPref("toolkit.telemetry.log.level", "Trace");
// Telemetry archiving should be on.
Services.prefs.setBoolPref("toolkit.telemetry.archive.enabled", true);
fakePingSendTimer((callback, timeout) => {
Services.tm.mainThread.dispatch(() => callback(), Ci.nsIThread.DISPATCH_NORMAL);
},
() => {});
do_register_cleanup(() => TelemetrySend.shutdown());
}
TelemetryController.initLogging();
// Avoid timers interrupting test behavior.
fakeSchedulerTimer(() => {}, () => {});
fakePingSendTimer(() => {}, () => {});
// Make pind sending predictable.
fakeMidnightPingFuzzingDelay(0);

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

@ -9,6 +9,7 @@
Cu.import("resource://gre/modules/TelemetryController.jsm", this);
Cu.import("resource://gre/modules/TelemetrySession.jsm", this);
Cu.import("resource://gre/modules/TelemetryArchive.jsm", this);
Cu.import("resource://gre/modules/TelemetrySend.jsm", this);
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
Cu.import("resource://gre/modules/osfile.jsm", this);
Cu.import("resource://gre/modules/Task.jsm", this);
@ -122,7 +123,7 @@ add_task(function* test_archivedPings() {
yield checkLoadingPings();
// Check that we find the archived pings again by scanning after a restart.
TelemetryController.reset();
yield TelemetryController.reset();
let pingList = yield TelemetryArchive.promiseArchivedPingList();
Assert.deepEqual(expectedPingList, pingList,
@ -367,14 +368,14 @@ add_task(function* test_archiveCleanup() {
add_task(function* test_clientId() {
// Check that a ping submitted after the delayed telemetry initialization completed
// should get a valid client id.
yield TelemetryController.setup();
yield TelemetryController.reset();
const clientId = TelemetryController.clientID;
let id = yield TelemetryController.submitExternalPing("test-type", {}, {addClientId: true});
let ping = yield TelemetryArchive.promiseArchivedPingById(id);
Assert.ok(!!ping, "Should have loaded the ping.");
Assert.ok("clientId" in ping, "Ping should have a client id.")
Assert.ok("clientId" in ping, "Ping should have a client id.");
Assert.ok(UUID_REGEX.test(ping.clientId), "Client id is in UUID format.");
Assert.equal(ping.clientId, clientId, "Ping client id should match the global client id.");
@ -441,3 +442,7 @@ add_task(function* test_currentPingData() {
Assert.equal(ping.payload.keyedHistograms[id]["a"].sum, 1, "Keyed test value should match.");
}
});
add_task(function* test_shutdown() {
yield TelemetrySend.shutdown();
});

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

@ -233,5 +233,6 @@ add_task(function* test_subsessionsChaining() {
});
add_task(function* () {
yield TelemetrySend.shutdown();
do_test_finished();
});

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

@ -8,7 +8,6 @@
* checked in the second request.
*/
Cu.import("resource://testing-common/httpd.js", this);
Cu.import("resource://gre/modules/ClientID.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
@ -37,14 +36,11 @@ const PREF_UNIFIED = PREF_BRANCH + "unified";
const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
let gHttpServer = new HttpServer();
let gServerStarted = false;
let gRequestIterator = null;
let gClientID = null;
function sendPing(aSendClientId, aSendEnvironment) {
if (gServerStarted) {
TelemetrySend.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
if (PingServer.started) {
TelemetrySend.setServer("http://localhost:" + PingServer.port);
} else {
TelemetrySend.setServer("http://doesnotexist");
}
@ -56,24 +52,6 @@ function sendPing(aSendClientId, aSendEnvironment) {
return TelemetryController.submitExternalPing(TEST_PING_TYPE, {}, options);
}
function wrapWithExceptionHandler(f) {
function wrapper(...args) {
try {
f(...args);
} catch (ex if typeof(ex) == 'object') {
dump("Caught exception: " + ex.message + "\n");
dump(ex.stack);
do_test_finished();
}
}
return wrapper;
}
function registerPingHandler(handler) {
gHttpServer.registerPrefixHandler("/submit/telemetry/",
wrapWithExceptionHandler(handler));
}
function checkPingFormat(aPing, aType, aHasClientId, aHasEnvironment) {
const MANDATORY_PING_FIELDS = [
"type", "id", "creationDate", "version", "application", "payload"
@ -114,15 +92,6 @@ function checkPingFormat(aPing, aType, aHasClientId, aHasEnvironment) {
Assert.equal("environment" in aPing, aHasEnvironment);
}
/**
* Start the webserver used in the tests.
*/
function startWebserver() {
gHttpServer.start(-1);
gServerStarted = true;
gRequestIterator = Iterator(new Request());
}
function run_test() {
do_test_pending();
@ -159,10 +128,10 @@ add_task(function* test_overwritePing() {
// Checks that a sent ping is correctly received by a dummy http server.
add_task(function* test_simplePing() {
startWebserver();
PingServer.start();
yield sendPing(false, false);
let request = yield gRequestIterator.next();
let request = yield PingServer.promiseNextRequest();
// Check that we have a version query parameter in the URL.
Assert.notEqual(request.queryString, "");
@ -186,8 +155,7 @@ add_task(function* test_deletionPing() {
// Disable FHR upload: this should trigger a deletion ping.
Preferences.set(PREF_FHR_UPLOAD_ENABLED, false);
let request = yield gRequestIterator.next();
let ping = decodeRequestPayload(request);
let ping = yield PingServer.promiseNextPing();
checkPingFormat(ping, DELETION_PING_TYPE, true, false);
// Restore FHR Upload.
@ -198,8 +166,7 @@ add_task(function* test_pingHasClientId() {
// Send a ping with a clientId.
yield sendPing(true, false);
let request = yield gRequestIterator.next();
let ping = decodeRequestPayload(request);
let ping = yield PingServer.promiseNextPing();
checkPingFormat(ping, TEST_PING_TYPE, true, false);
if (HAS_DATAREPORTINGSERVICE &&
@ -212,8 +179,7 @@ add_task(function* test_pingHasClientId() {
add_task(function* test_pingHasEnvironment() {
// Send a ping with the environment data.
yield sendPing(false, true);
let request = yield gRequestIterator.next();
let ping = decodeRequestPayload(request);
let ping = yield PingServer.promiseNextPing();
checkPingFormat(ping, TEST_PING_TYPE, false, true);
// Test a field in the environment build section.
@ -223,8 +189,7 @@ add_task(function* test_pingHasEnvironment() {
add_task(function* test_pingHasEnvironmentAndClientId() {
// Send a ping with the environment data and client id.
yield sendPing(true, true);
let request = yield gRequestIterator.next();
let ping = decodeRequestPayload(request);
let ping = yield PingServer.promiseNextPing();
checkPingFormat(ping, TEST_PING_TYPE, true, true);
// Test a field in the environment build section.
@ -254,13 +219,12 @@ add_task(function* test_archivePings() {
// If we're using unified telemetry, disabling ping upload will generate a "deletion"
// ping. Catch it.
if (isUnified) {
let request = yield gRequestIterator.next();
let ping = decodeRequestPayload(request);
let ping = yield PingServer.promiseNextPing();
checkPingFormat(ping, DELETION_PING_TYPE, true, false);
}
// Register a new Ping Handler that asserts if a ping is received, then send a ping.
registerPingHandler(() => Assert.ok(false, "Telemetry must not send pings if not allowed to."));
PingServer.registerPingHandler(() => Assert.ok(false, "Telemetry must not send pings if not allowed to."));
let pingId = yield sendPing(true, true);
// Check that the ping was archived, even with upload disabled.
@ -282,12 +246,12 @@ add_task(function* test_archivePings() {
now = new Date(2014, 06, 18, 22, 0, 0);
fakeNow(now);
// Restore the non asserting ping handler. This is done by the Request() constructor.
gRequestIterator = Iterator(new Request());
// Restore the non asserting ping handler.
PingServer.resetPingHandler();
pingId = yield sendPing(true, true);
// Check that we archive pings when successfully sending them.
yield gRequestIterator.next();
yield PingServer.promiseNextPing();
ping = yield TelemetryArchive.promiseArchivedPingById(pingId);
Assert.equal(ping.id, pingId,
"TelemetryController should still archive pings if ping upload is enabled.");
@ -301,62 +265,62 @@ add_task(function* test_midnightPingSendFuzzing() {
let now = new Date(2030, 5, 1, 11, 00, 0);
fakeNow(now);
let pingSendTimerCallback = null;
let pingSendTimeout = null;
fakePingSendTimer((callback, timeout) => {
pingSendTimerCallback = callback;
pingSendTimeout = timeout;
}, () => {});
let waitForTimer = () => new Promise(resolve => {
fakePingSendTimer((callback, timeout) => {
resolve([callback, timeout]);
}, () => {});
});
gRequestIterator = Iterator(new Request());
PingServer.clearRequests();
yield TelemetryController.reset();
// A ping after midnight within the fuzzing delay should not get sent.
now = new Date(2030, 5, 2, 0, 40, 0);
fakeNow(now);
registerPingHandler((req, res) => {
PingServer.registerPingHandler((req, res) => {
Assert.ok(false, "No ping should be received yet.");
});
let timerPromise = waitForTimer();
yield sendPing(true, true);
Assert.ok(!!pingSendTimerCallback);
Assert.deepEqual(futureDate(now, pingSendTimeout), new Date(2030, 5, 2, 1, 0, 0));
let [timerCallback, timerTimeout] = yield timerPromise;
Assert.ok(!!timerCallback);
Assert.deepEqual(futureDate(now, timerTimeout), new Date(2030, 5, 2, 1, 0, 0));
// A ping just before the end of the fuzzing delay should not get sent.
now = new Date(2030, 5, 2, 0, 59, 59);
fakeNow(now);
pingSendTimeout = null;
timerPromise = waitForTimer();
yield sendPing(true, true);
Assert.deepEqual(futureDate(now, pingSendTimeout), new Date(2030, 5, 2, 1, 0, 0));
[timerCallback, timerTimeout] = yield timerPromise;
Assert.deepEqual(timerTimeout, 1 * 1000);
// The Request constructor restores the previous ping handler.
gRequestIterator = Iterator(new Request());
// Restore the previous ping handler.
PingServer.resetPingHandler();
// Setting the clock to after the fuzzing delay, we should trigger the two ping sends
// with the timer callback.
now = futureDate(now, pingSendTimeout);
now = futureDate(now, timerTimeout);
fakeNow(now);
yield pingSendTimerCallback();
let requests = [];
requests.push(yield gRequestIterator.next());
requests.push(yield gRequestIterator.next());
for (let req of requests) {
let ping = decodeRequestPayload(req);
yield timerCallback();
const pings = yield PingServer.promiseNextPings(2);
for (let ping of pings) {
checkPingFormat(ping, TEST_PING_TYPE, true, true);
}
yield TelemetrySend.testWaitOnOutgoingPings();
// Moving the clock further we should still send pings immediately.
now = futureDate(now, 5 * 60 * 1000);
yield sendPing(true, true);
let request = yield gRequestIterator.next();
let ping = decodeRequestPayload(request);
let ping = yield PingServer.promiseNextPing();
checkPingFormat(ping, TEST_PING_TYPE, true, true);
yield TelemetrySend.testWaitOnOutgoingPings();
// Check that pings shortly before midnight are immediately sent.
now = fakeNow(2030, 5, 3, 23, 59, 0);
yield sendPing(true, true);
request = yield gRequestIterator.next();
ping = decodeRequestPayload(request);
ping = yield PingServer.promiseNextPing();
checkPingFormat(ping, TEST_PING_TYPE, true, true);
yield TelemetrySend.testWaitOnOutgoingPings();
// Clean-up.
fakeMidnightPingFuzzingDelay(0);
@ -381,31 +345,6 @@ add_task(function* test_changePingAfterSubmission() {
});
add_task(function* stopServer(){
gHttpServer.stop(do_test_finished);
yield PingServer.stop();
do_test_finished();
});
// An iterable sequence of http requests
function Request() {
let defers = [];
let current = 0;
function RequestIterator() {}
// Returns a promise that resolves to the next http request
RequestIterator.prototype.next = function() {
let deferred = defers[current++];
return deferred.promise;
}
this.__iterator__ = function(){
return new RequestIterator();
}
registerPingHandler((request, response) => {
let deferred = defers[defers.length - 1];
defers.push(Promise.defer());
deferred.resolve(request);
});
defers.push(Promise.defer());
}

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

@ -0,0 +1,263 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
*/
// This tests the public Telemetry API for submitting pings.
"use strict";
Cu.import("resource://gre/modules/TelemetryController.jsm", this);
Cu.import("resource://gre/modules/TelemetrySession.jsm", this);
Cu.import("resource://gre/modules/TelemetrySend.jsm", this);
Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
Cu.import("resource://gre/modules/Services.jsm", this);
Cu.import("resource://gre/modules/Preferences.jsm", this);
Cu.import("resource://gre/modules/osfile.jsm", this);
const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
const PREF_TELEMETRY_SERVER = "toolkit.telemetry.server";
const MS_IN_A_MINUTE = 60 * 1000;
function countPingTypes(pings) {
let countByType = new Map();
for (let p of pings) {
countByType.set(p.type, 1 + (countByType.get(p.type) || 0));
}
return countByType;
}
function setPingLastModified(id, timestamp) {
const path = OS.Path.join(TelemetryStorage.pingDirectoryPath, id);
return OS.File.setDates(path, null, timestamp);
}
// Mock out the send timer activity.
function waitForTimer() {
return new Promise(resolve => {
fakePingSendTimer((callback, timeout) => {
resolve([callback, timeout]);
}, () => {});
});
}
// Allow easy faking of readable ping ids.
// This helps with debugging issues with e.g. ordering in the send logic.
function fakePingId(type, number) {
const HEAD = "93bd0011-2c8f-4e1c-bee0-";
const TAIL = "000000000000";
const N = String(number);
const id = HEAD + type + TAIL.slice(type.length, - N.length) + N;
fakeGeneratePingId(() => id);
return id;
}
let checkPingsSaved = Task.async(function* (pingIds) {
let allFound = true;
for (let id of pingIds) {
const path = OS.Path.join(TelemetryStorage.pingDirectoryPath, id);
let exists = false;
try {
exists = yield OS.File.exists(path);
} catch (ex) {}
if (!exists) {
dump("checkPingsSaved - failed to find ping: " + path + "\n");
allFound = false;
}
}
return allFound;
});
function run_test() {
// Trigger a proper telemetry init.
do_get_profile(true);
Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
run_next_test();
}
// Test the ping sending logic.
add_task(function* test_sendPendingPings() {
const TYPE_PREFIX = "test-sendPendingPings-";
const TEST_TYPE_A = TYPE_PREFIX + "A";
const TEST_TYPE_B = TYPE_PREFIX + "B";
const TYPE_A_COUNT = 20;
const TYPE_B_COUNT = 5;
// Fake a current date.
let now = TelemetryUtils.truncateToDays(new Date());
now = fakeNow(futureDate(now, 10 * 60 * MS_IN_A_MINUTE));
// Enable test-mode for TelemetrySend, otherwise we won't store pending pings
// before the module is fully initialized later.
TelemetrySend.setTestModeEnabled(true);
// Submit some pings without the server and telemetry started yet.
for (let i = 0; i < TYPE_A_COUNT; ++i) {
fakePingId("a", i);
const id = yield TelemetryController.submitExternalPing(TEST_TYPE_A, {});
yield setPingLastModified(id, now.getTime() + (i * 1000));
}
Assert.equal(TelemetrySend.pendingPingCount, TYPE_A_COUNT,
"Should have correct pending ping count");
// Submit some more pings of a different type.
now = fakeNow(futureDate(now, 5 * MS_IN_A_MINUTE));
for (let i = 0; i < TYPE_B_COUNT; ++i) {
fakePingId("b", i);
const id = yield TelemetryController.submitExternalPing(TEST_TYPE_B, {});
yield setPingLastModified(id, now.getTime() + (i * 1000));
}
Assert.equal(TelemetrySend.pendingPingCount, TYPE_A_COUNT + TYPE_B_COUNT,
"Should have correct pending ping count");
// Now enable sending to the ping server.
now = fakeNow(futureDate(now, MS_IN_A_MINUTE));
PingServer.start();
Preferences.set(PREF_TELEMETRY_SERVER, "http://localhost:" + PingServer.port);
let timerPromise = waitForTimer();
yield TelemetryController.reset();
let [pingSendTimerCallback, pingSendTimeout] = yield timerPromise;
Assert.ok(!!pingSendTimerCallback, "Should have a timer callback");
// We should have received 10 pings from the first send batch:
// 5 of type B and 5 of type A, as sending is newest-first.
// The other pings should be delayed by the 10-pings-per-minute limit.
let pings = yield PingServer.promiseNextPings(10);
Assert.equal(TelemetrySend.pendingPingCount, TYPE_A_COUNT - 5,
"Should have correct pending ping count");
PingServer.registerPingHandler(() => Assert.ok(false, "Should not have received any pings now"));
let countByType = countPingTypes(pings);
Assert.equal(countByType.get(TEST_TYPE_B), TYPE_B_COUNT,
"Should have received the correct amount of type B pings");
Assert.equal(countByType.get(TEST_TYPE_A), 10 - TYPE_B_COUNT,
"Should have received the correct amount of type A pings");
// As we hit the ping send limit and still have pending pings, a send tick should
// be scheduled in a minute.
Assert.ok(!!pingSendTimerCallback, "Timer callback should be set");
Assert.equal(pingSendTimeout, MS_IN_A_MINUTE, "Send tick timeout should be correct");
// Trigger the next tick - we should receive the next 10 type A pings.
PingServer.resetPingHandler();
now = fakeNow(futureDate(now, pingSendTimeout));
timerPromise = waitForTimer();
pingSendTimerCallback();
[pingSendTimerCallback, pingSendTimeout] = yield timerPromise;
pings = yield PingServer.promiseNextPings(10);
PingServer.registerPingHandler(() => Assert.ok(false, "Should not have received any pings now"));
countByType = countPingTypes(pings);
Assert.equal(countByType.get(TEST_TYPE_A), 10, "Should have received the correct amount of type A pings");
// We hit the ping send limit again and still have pending pings, a send tick should
// be scheduled in a minute.
Assert.equal(pingSendTimeout, MS_IN_A_MINUTE, "Send tick timeout should be correct");
// Trigger the next tick - we should receive the remaining type A pings.
PingServer.resetPingHandler();
now = fakeNow(futureDate(now, pingSendTimeout));
yield pingSendTimerCallback();
pings = yield PingServer.promiseNextPings(5);
PingServer.registerPingHandler(() => Assert.ok(false, "Should not have received any pings now"));
countByType = countPingTypes(pings);
Assert.equal(countByType.get(TEST_TYPE_A), 5, "Should have received the correct amount of type A pings");
});
// Test the backoff timeout behavior after send failures.
add_task(function* test_backoffTimeout() {
const TYPE_PREFIX = "test-backoffTimeout-";
const TEST_TYPE_C = TYPE_PREFIX + "C";
const TEST_TYPE_D = TYPE_PREFIX + "D";
const TEST_TYPE_E = TYPE_PREFIX + "E";
// Failing a ping send now should trigger backoff behavior.
let now = fakeNow(2010, 1, 1, 11, 0, 0);
yield TelemetrySend.reset();
PingServer.stop();
fakePingId("c", 0);
now = fakeNow(futureDate(now, MS_IN_A_MINUTE));
let timerPromise = waitForTimer();
yield TelemetryController.submitExternalPing(TEST_TYPE_C, {});
let [pingSendTimerCallback, pingSendTimeout] = yield timerPromise;
Assert.equal(TelemetrySend.pendingPingCount, 1, "Should have one pending ping.");
const MAX_BACKOFF_TIMEOUT = 120 * MS_IN_A_MINUTE;
for (let timeout = 2 * MS_IN_A_MINUTE; timeout <= MAX_BACKOFF_TIMEOUT; timeout *= 2) {
Assert.ok(!!pingSendTimerCallback, "Should have received a timer callback");
Assert.equal(pingSendTimeout, timeout, "Send tick timeout should be correct");
let callback = pingSendTimerCallback;
now = fakeNow(futureDate(now, pingSendTimeout));
timerPromise = waitForTimer();
yield callback();
[pingSendTimerCallback, pingSendTimeout] = yield timerPromise;
}
timerPromise = waitForTimer();
yield pingSendTimerCallback();
[pingSendTimerCallback, pingSendTimeout] = yield timerPromise;
Assert.equal(pingSendTimeout, MAX_BACKOFF_TIMEOUT, "Tick timeout should be capped");
// Submitting a new ping should reset the backoff behavior.
fakePingId("d", 0);
now = fakeNow(futureDate(now, MS_IN_A_MINUTE));
timerPromise = waitForTimer();
yield TelemetryController.submitExternalPing(TEST_TYPE_D, {});
[pingSendTimerCallback, pingSendTimeout] = yield timerPromise;
Assert.equal(pingSendTimeout, 2 * MS_IN_A_MINUTE, "Send tick timeout should be correct");
// With the server running again, we should send out the pending pings immediately
// when a new ping is submitted.
PingServer.start();
TelemetrySend.setServer("http://localhost:" + PingServer.port);
fakePingId("e", 0);
now = fakeNow(futureDate(now, MS_IN_A_MINUTE));
timerPromise = waitForTimer();
yield TelemetryController.submitExternalPing(TEST_TYPE_E, {});
let pings = yield PingServer.promiseNextPings(3);
let countByType = countPingTypes(pings);
Assert.equal(countByType.get(TEST_TYPE_C), 1, "Should have received the correct amount of type C pings");
Assert.equal(countByType.get(TEST_TYPE_D), 1, "Should have received the correct amount of type D pings");
Assert.equal(countByType.get(TEST_TYPE_E), 1, "Should have received the correct amount of type E pings");
yield TelemetrySend.testWaitOnOutgoingPings();
Assert.equal(TelemetrySend.pendingPingCount, 0, "Should have no pending pings left");
});
// Test that the current, non-persisted pending pings are properly saved on shutdown.
add_task(function* test_persistCurrentPingsOnShutdown() {
const TEST_TYPE = "test-persistCurrentPingsOnShutdown";
const PING_COUNT = 5;
yield TelemetrySend.reset();
PingServer.stop();
Assert.equal(TelemetrySend.pendingPingCount, 0, "Should have no pending pings yet");
// Submit new pings that shouldn't be persisted yet.
let ids = [];
for (let i=0; i<5; ++i) {
ids.push(fakePingId("f", i));
TelemetryController.submitExternalPing(TEST_TYPE, {});
}
Assert.equal(TelemetrySend.pendingPingCount, PING_COUNT, "Should have the correct pending ping count");
// Triggering a shutdown should persist the pings.
yield TelemetrySend.shutdown();
Assert.ok((yield checkPingsSaved(ids)), "All pending pings should have been persisted");
// After a restart the pings should have been found when scanning.
yield TelemetrySend.reset();
Assert.equal(TelemetrySend.pendingPingCount, PING_COUNT, "Should have the correct pending ping count");
});

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

@ -14,7 +14,6 @@
Cu.import("resource://gre/modules/osfile.jsm", this);
Cu.import("resource://gre/modules/Services.jsm", this);
Cu.import("resource://testing-common/httpd.js", this);
Cu.import("resource://gre/modules/Promise.jsm", this);
Cu.import("resource://gre/modules/TelemetryStorage.jsm", this);
Cu.import("resource://gre/modules/TelemetryController.jsm", this);
@ -45,7 +44,6 @@ const LRU_PINGS = TelemetrySend.MAX_LRU_PINGS;
const TOTAL_EXPECTED_PINGS = OVERDUE_PINGS + RECENT_PINGS + OLD_FORMAT_PINGS;
let gHttpServer = new HttpServer();
let gCreatedPings = 0;
let gSeenPings = 0;
@ -146,20 +144,6 @@ function pingHandler(aRequest) {
gSeenPings++;
}
/**
* Returns a Promise that resolves when gHttpServer has been
* successfully shut down.
*
* @returns Promise
*/
function stopHttpServer() {
let deferred = Promise.defer();
gHttpServer.stop(function() {
deferred.resolve();
})
return deferred.promise;
}
/**
* Clear out all pending pings.
*/
@ -179,8 +163,8 @@ function startTelemetry() {
}
function run_test() {
gHttpServer.registerPrefixHandler("/submit/telemetry/", pingHandler);
gHttpServer.start(-1);
PingServer.start();
PingServer.registerPingHandler(pingHandler);
do_get_profile();
loadAddonManager("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
@ -191,7 +175,7 @@ function run_test() {
Services.prefs.setBoolPref(TelemetryController.Constants.PREF_ENABLED, true);
Services.prefs.setCharPref(TelemetryController.Constants.PREF_SERVER,
"http://localhost:" + gHttpServer.identity.primaryPort);
"http://localhost:" + PingServer.port);
run_next_test();
}
@ -224,14 +208,15 @@ add_task(function* test_expired_pings_are_deleted() {
});
/**
* Test that really recent pings are not sent on Telemetry initialization.
* Test that really recent pings are sent on Telemetry initialization.
*/
add_task(function* test_recent_pings_not_sent() {
add_task(function* test_recent_pings_sent() {
let pingTypes = [{ num: RECENT_PINGS }];
let recentPings = yield createSavedPings(pingTypes);
yield TelemetryController.reset();
assertReceivedPings(0);
yield TelemetrySend.testWaitOnOutgoingPings();
assertReceivedPings(RECENT_PINGS);
yield clearPendingPings();
});
@ -248,6 +233,9 @@ add_task(function* test_most_recent_pings_kept() {
let head = pings.slice(0, LRU_PINGS);
let tail = pings.slice(-3);
const evictedHistogram = Services.telemetry.getHistogramById('TELEMETRY_FILES_EVICTED');
evictedHistogram.clear();
yield TelemetryController.reset();
const pending = yield TelemetryStorage.loadPendingPingList();
@ -257,6 +245,9 @@ add_task(function* test_most_recent_pings_kept() {
}
assertNotSaved(tail);
Assert.equal(evictedHistogram.snapshot().sum, tail.length,
"Should have tracked the evicted ping count");
yield TelemetrySend.shutdown();
yield clearPendingPings();
});
@ -318,6 +309,7 @@ add_task(function* test_overdue_old_format() {
yield File.setDates(PING_FILES_PATHS[f], null, Date.now() - OVERDUE_PING_FILE_AGE);
}
gSeenPings = 0;
yield TelemetryController.reset();
yield TelemetrySend.testWaitOnOutgoingPings();
assertReceivedPings(OLD_FORMAT_PINGS);
@ -353,6 +345,11 @@ add_task(function* test_overdue_pings_trigger_send() {
yield assertNotSaved(expiredPings);
yield assertNotSaved(overduePings);
Assert.equal(TelemetrySend.overduePingsCount, overduePings.length,
"Should have tracked the correct amount of overdue pings");
Assert.equal(TelemetrySend.discardedPingsCount, expiredPings.length,
"Should have tracked the correct amount of expired pings");
yield clearPendingPings();
});
@ -388,7 +385,7 @@ add_task(function* test_overdue_old_format() {
let receivedPings = 0;
// Register a new prefix handler to validate the URL.
gHttpServer.registerPrefixHandler("/submit/telemetry/", request => {
PingServer.registerPingHandler(request => {
// Check that we have a version query parameter in the URL.
Assert.notEqual(request.queryString, "");
@ -407,5 +404,5 @@ add_task(function* test_overdue_old_format() {
});
add_task(function* teardown() {
yield stopHttpServer();
yield PingServer.stop();
});

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

@ -8,7 +8,6 @@
* checked in the second request.
*/
Cu.import("resource://testing-common/httpd.js", this);
Cu.import("resource://services-common/utils.js");
Cu.import("resource://gre/modules/ClientID.jsm");
Cu.import("resource://gre/modules/Services.jsm");
@ -77,9 +76,6 @@ XPCOMUtils.defineLazyGetter(this, "DATAREPORTING_PATH", function() {
return OS.Path.join(OS.Constants.Path.profileDir, DATAREPORTING_DIR);
});
let gHttpServer = new HttpServer();
let gServerStarted = false;
let gRequestIterator = null;
let gClientID = null;
function generateUUID() {
@ -97,8 +93,8 @@ function truncateDateToDays(date) {
function sendPing() {
TelemetrySession.gatherStartup();
if (gServerStarted) {
TelemetrySend.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
if (PingServer.started) {
TelemetrySend.setServer("http://localhost:" + PingServer.port);
return TelemetrySession.testPing();
} else {
TelemetrySend.setServer("http://doesnotexist");
@ -113,19 +109,6 @@ let clearPendingPings = Task.async(function*() {
}
});
function wrapWithExceptionHandler(f) {
function wrapper(...args) {
try {
f(...args);
} catch (ex if typeof(ex) == 'object') {
dump("Caught exception: " + ex.message + "\n");
dump(ex.stack);
do_test_finished();
}
}
return wrapper;
}
function fakeGenerateUUID(sessionFunc, subsessionFunc) {
let session = Cu.import("resource://gre/modules/TelemetrySession.jsm");
session.Policy.generateSessionUUID = sessionFunc;
@ -137,11 +120,6 @@ function fakeIdleNotification(topic) {
return session.TelemetryScheduler.observe(null, topic, null);
}
function registerPingHandler(handler) {
gHttpServer.registerPrefixHandler("/submit/telemetry/",
wrapWithExceptionHandler(handler));
}
function setupTestData() {
Telemetry.newHistogram(IGNORE_HISTOGRAM, "never", Telemetry.HISTOGRAM_BOOLEAN);
Telemetry.histogramFrom(IGNORE_CLONED_HISTOGRAM, IGNORE_HISTOGRAM_TO_CLONE);
@ -533,10 +511,10 @@ add_task(function* test_noServerPing() {
// Checks that a sent ping is correctly received by a dummy http server.
add_task(function* test_simplePing() {
gHttpServer.start(-1);
gServerStarted = true;
gRequestIterator = Iterator(new Request());
Preferences.set(PREF_SERVER, "http://localhost:" + gHttpServer.identity.primaryPort);
yield clearPendingPings();
yield TelemetrySend.reset();
PingServer.start();
Preferences.set(PREF_SERVER, "http://localhost:" + PingServer.port);
let now = new Date(2020, 1, 1, 12, 0, 0);
let expectedDate = new Date(2020, 1, 1, 0, 0, 0);
@ -553,8 +531,7 @@ add_task(function* test_simplePing() {
fakeNow(new Date(2020, 1, 1, 12, SESSION_DURATION_IN_MINUTES, 0));
yield sendPing();
let request = yield gRequestIterator.next();
let ping = decodeRequestPayload(request);
let ping = yield PingServer.promiseNextPing();
checkPingFormat(ping, PING_TYPE_MAIN, true, true);
@ -579,7 +556,7 @@ add_task(function* test_saveLoadPing() {
// Let's start out with a defined state.
yield clearPendingPings();
yield TelemetryController.reset();
gRequestIterator = Iterator(new Request());
PingServer.clearRequests();
// Setup test data and trigger pings.
setupTestData();
@ -587,10 +564,7 @@ add_task(function* test_saveLoadPing() {
yield sendPing();
// Get requests received by dummy server.
let requests = [
yield gRequestIterator.next(),
yield gRequestIterator.next(),
];
const requests = yield PingServer.promiseNextRequests(2);
for (let req of requests) {
Assert.equal(req.getHeader("content-type"), "application/json; charset=UTF-8",
@ -858,7 +832,7 @@ add_task(function* test_dailyCollection() {
let nowDay = new Date(2030, 1, 1, 0, 0, 0);
let schedulerTickCallback = null;
gRequestIterator = Iterator(new Request());
PingServer.clearRequests();
fakeNow(now);
@ -866,8 +840,9 @@ add_task(function* test_dailyCollection() {
fakeSchedulerTimer(callback => schedulerTickCallback = callback, () => {});
// Init and check timer.
yield clearPendingPings();
yield TelemetrySession.setup();
TelemetrySend.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
TelemetrySend.setServer("http://localhost:" + PingServer.port);
// Set histograms to expected state.
const COUNT_ID = "TELEMETRY_TEST_COUNT";
@ -892,9 +867,8 @@ add_task(function* test_dailyCollection() {
yield schedulerTickCallback();
// Collect the daily ping.
let request = yield gRequestIterator.next();
Assert.ok(!!request);
let ping = decodeRequestPayload(request);
let ping = yield PingServer.promiseNextPing();
Assert.ok(!!ping);
Assert.equal(ping.type, PING_TYPE_MAIN);
Assert.equal(ping.payload.info.reason, REASON_DAILY);
@ -913,9 +887,8 @@ add_task(function* test_dailyCollection() {
// Run a scheduler tick. Trigger and collect another ping. The histograms should be reset.
yield schedulerTickCallback();
request = yield gRequestIterator.next();
Assert.ok(!!request);
ping = decodeRequestPayload(request);
ping = yield PingServer.promiseNextPing();
Assert.ok(!!ping);
Assert.equal(ping.type, PING_TYPE_MAIN);
Assert.equal(ping.payload.info.reason, REASON_DAILY);
@ -936,9 +909,8 @@ add_task(function* test_dailyCollection() {
fakeNow(now);
yield schedulerTickCallback();
request = yield gRequestIterator.next();
Assert.ok(!!request);
ping = decodeRequestPayload(request);
ping = yield PingServer.promiseNextPing();
Assert.ok(!!ping);
Assert.equal(ping.type, PING_TYPE_MAIN);
Assert.equal(ping.payload.info.reason, REASON_DAILY);
@ -959,7 +931,7 @@ add_task(function* test_dailyDuplication() {
return;
}
gRequestIterator = Iterator(new Request());
PingServer.clearRequests();
let schedulerTickCallback = null;
let now = new Date(2030, 1, 1, 0, 0, 0);
@ -979,15 +951,14 @@ add_task(function* test_dailyDuplication() {
yield schedulerTickCallback();
// Get the first daily ping.
let request = yield gRequestIterator.next();
Assert.ok(!!request);
let ping = decodeRequestPayload(request);
let ping = yield PingServer.promiseNextPing();
Assert.ok(!!ping);
Assert.equal(ping.type, PING_TYPE_MAIN);
Assert.equal(ping.payload.info.reason, REASON_DAILY);
// We don't expect to receive any other daily ping in this test, so assert if we do.
registerPingHandler((req, res) => {
PingServer.registerPingHandler((req, res) => {
Assert.ok(false, "No more daily pings should be sent/received in this test.");
});
@ -1003,6 +974,7 @@ add_task(function* test_dailyDuplication() {
// Shutdown to cleanup the aborted-session if it gets created.
yield TelemetrySession.shutdown();
PingServer.resetPingHandler();
});
add_task(function* test_dailyOverdue() {
@ -1023,7 +995,7 @@ add_task(function* test_dailyOverdue() {
fakeNow(now);
// Assert if we receive something!
registerPingHandler((req, res) => {
PingServer.registerPingHandler((req, res) => {
Assert.ok(false, "No daily ping should be received if not overdue!.");
});
@ -1031,8 +1003,9 @@ add_task(function* test_dailyOverdue() {
Assert.ok(!!schedulerTickCallback);
yield schedulerTickCallback();
// Restore the non asserting ping handler. This is done by the Request() constructor.
gRequestIterator = Iterator(new Request());
// Restore the non asserting ping handler.
PingServer.resetPingHandler();
PingServer.clearRequests();
// Simulate an overdue ping: we're not close to midnight, but the last daily ping
// time is too long ago.
@ -1044,9 +1017,8 @@ add_task(function* test_dailyOverdue() {
yield schedulerTickCallback();
// Get the first daily ping.
let request = yield gRequestIterator.next();
Assert.ok(!!request);
let ping = decodeRequestPayload(request);
let ping = yield PingServer.promiseNextPing();
Assert.ok(!!ping);
Assert.equal(ping.type, PING_TYPE_MAIN);
Assert.equal(ping.payload.info.reason, REASON_DAILY);
@ -1065,7 +1037,8 @@ add_task(function* test_environmentChange() {
let timerCallback = null;
let timerDelay = null;
gRequestIterator = Iterator(new Request());
clearPendingPings();
PingServer.clearRequests();
fakeNow(now);
@ -1078,7 +1051,7 @@ add_task(function* test_environmentChange() {
// Setup.
yield TelemetrySession.setup();
TelemetrySend.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
TelemetrySend.setServer("http://localhost:" + PingServer.port);
TelemetryEnvironment._watchPreferences(PREFS_TO_WATCH);
// Set histograms to expected state.
@ -1099,9 +1072,8 @@ add_task(function* test_environmentChange() {
fakeNow(now);
Preferences.set(PREF_TEST, 1);
let request = yield gRequestIterator.next();
Assert.ok(!!request);
let ping = decodeRequestPayload(request);
let ping = yield PingServer.promiseNextPing();
Assert.ok(!!ping);
Assert.equal(ping.type, PING_TYPE_MAIN);
Assert.equal(ping.environment.settings.userPrefs[PREF_TEST], undefined);
@ -1118,9 +1090,8 @@ add_task(function* test_environmentChange() {
fakeNow(now);
Preferences.set(PREF_TEST, 2);
request = yield gRequestIterator.next();
Assert.ok(!!request);
ping = decodeRequestPayload(request);
ping = yield PingServer.promiseNextPing();
Assert.ok(!!ping);
Assert.equal(ping.type, PING_TYPE_MAIN);
Assert.equal(ping.environment.settings.userPrefs[PREF_TEST], 1);
@ -1150,7 +1121,7 @@ add_task(function* test_pruneOldPingFile() {
add_task(function* test_savedPingsOnShutdown() {
// On desktop, we expect both "saved-session" and "shutdown" pings. We only expect
// the former on Android.
const expectedPings = (gIsAndroid) ? 1 : 2;
const expectedPingCount = (gIsAndroid) ? 1 : 2;
// Assure that we store the ping properly when saving sessions on shutdown.
// We make the TelemetrySession shutdown to trigger a session save.
const dir = TelemetryStorage.pingDirectoryPath;
@ -1158,12 +1129,11 @@ add_task(function* test_savedPingsOnShutdown() {
yield OS.File.makeDir(dir);
yield TelemetrySession.shutdown();
PingServer.clearRequests();
yield TelemetryController.reset();
const pending = yield TelemetryStorage.loadPendingPingList();
Assert.equal(pending.length, expectedPings,
"Should have the correct number of pending pings.");
const pings = [for (p of pending) yield TelemetryStorage.loadPendingPing(p.id)];
const pings = yield PingServer.promiseNextPings(expectedPingCount);
for (let ping of pings) {
Assert.ok("type" in ping);
@ -1366,35 +1336,17 @@ add_task(function* test_abortedSession() {
yield TelemetryStorage.savePingToFile(abortedSessionPing, ABORTED_FILE, false);
yield clearPendingPings();
gRequestIterator = Iterator(new Request());
PingServer.clearRequests();
yield TelemetrySession.reset();
yield TelemetryController.reset();
Assert.ok(!(yield OS.File.exists(ABORTED_FILE)),
"The aborted session ping must be removed from the aborted session ping directory.");
// TelemetryStorage requires all the pings to have their ID as filename. When appending
// the aborted-session ping to the pending pings, we must verify that it exists.
const PENDING_PING_FILE =
OS.Path.join(TelemetryStorage.pingDirectoryPath, abortedSessionPing.id);
Assert.ok((yield OS.File.exists(PENDING_PING_FILE)),
"The aborted session ping must exist in the saved pings directory.");
// Trigger sending the pending pings.
yield sendPing();
// We should receive two pings, one of them an aborted-session ping.
let requests = [
yield gRequestIterator.next(),
yield gRequestIterator.next(),
];
let pings = [for (req of requests) decodeRequestPayload(req)].filter((p) => {
return p.type == PING_TYPE_MAIN && p.payload.info.reason == REASON_ABORTED_SESSION;
});
Assert.equal(pings.length, 1, "Should have received one aborted-session ping.");
let receivedPing = pings[0];
Assert.equal(receivedPing.id, abortedSessionPing.id);
// We should have received an aborted-session ping.
const receivedPing = yield PingServer.promiseNextPing();
Assert.equal(receivedPing.type, PING_TYPE_MAIN, "Should have the correct type");
Assert.equal(receivedPing.payload.info.reason, REASON_ABORTED_SESSION, "Ping should have the correct reason");
yield TelemetrySession.shutdown();
});
@ -1444,7 +1396,7 @@ add_task(function* test_abortedDailyCoalescing() {
yield OS.File.removeDir(DATAREPORTING_PATH, { ignoreAbsent: true });
let schedulerTickCallback = null;
gRequestIterator = Iterator(new Request());
PingServer.clearRequests();
let nowDate = new Date(2009, 10, 18, 0, 0, 0);
fakeNow(nowDate);
@ -1466,8 +1418,7 @@ add_task(function* test_abortedDailyCoalescing() {
yield schedulerTickCallback();
// Wait for the daily ping.
let request = yield gRequestIterator.next();
let dailyPing = decodeRequestPayload(request);
let dailyPing = yield PingServer.promiseNextPing();
Assert.equal(dailyPing.payload.info.reason, REASON_DAILY);
// Check that an aborted session ping was also written to disk.
@ -1492,7 +1443,7 @@ add_task(function* test_schedulerComputerSleep() {
const ABORTED_FILE = OS.Path.join(DATAREPORTING_PATH, ABORTED_PING_FILE_NAME);
gRequestIterator = Iterator(new Request());
PingServer.clearRequests();
// Remove any aborted-session ping from the previous tests.
yield OS.File.removeDir(DATAREPORTING_PATH, { ignoreAbsent: true });
@ -1511,8 +1462,7 @@ add_task(function* test_schedulerComputerSleep() {
// Execute one scheduler tick.
yield schedulerTickCallback();
let request = yield gRequestIterator.next();
let dailyPing = decodeRequestPayload(request);
let dailyPing = yield PingServer.promiseNextPing();
Assert.equal(dailyPing.payload.info.reason, REASON_DAILY);
Assert.ok((yield OS.File.exists(ABORTED_FILE)),
@ -1535,7 +1485,7 @@ add_task(function* test_schedulerEnvironmentReschedules() {
]);
yield clearPendingPings();
gRequestIterator = Iterator(new Request());
PingServer.clearRequests();
// Set a fake current date and start Telemetry.
let nowDate = new Date(2060, 10, 18, 0, 0, 0);
@ -1553,10 +1503,10 @@ add_task(function* test_schedulerEnvironmentReschedules() {
Preferences.set(PREF_TEST, 1);
// Wait for the environment-changed ping.
yield gRequestIterator.next();
yield PingServer.promiseNextPing();
// We don't expect to receive any daily ping in this test, so assert if we do.
registerPingHandler((req, res) => {
PingServer.registerPingHandler((req, res) => {
Assert.ok(false, "No ping should be sent/received in this test.");
});
@ -1580,7 +1530,7 @@ add_task(function* test_schedulerNothingDue() {
yield clearPendingPings();
// We don't expect to receive any ping in this test, so assert if we do.
registerPingHandler((req, res) => {
PingServer.registerPingHandler((req, res) => {
Assert.ok(false, "No ping should be sent/received in this test.");
});
@ -1603,6 +1553,7 @@ add_task(function* test_schedulerNothingDue() {
Assert.ok(!(yield OS.File.exists(ABORTED_FILE)));
yield TelemetrySession.shutdown();
PingServer.resetPingHandler();
});
add_task(function* test_pingExtendedStats() {
@ -1615,12 +1566,11 @@ add_task(function* test_pingExtendedStats() {
Telemetry.canRecordExtended = false;
yield clearPendingPings();
gRequestIterator = Iterator(new Request());
PingServer.clearRequests();
yield TelemetrySession.reset();
yield sendPing();
let request = yield gRequestIterator.next();
let ping = decodeRequestPayload(request);
let ping = yield PingServer.promiseNextPing();
checkPingFormat(ping, PING_TYPE_MAIN, true, true);
// Check that the payload does not contain extended statistics fields.
@ -1645,8 +1595,7 @@ add_task(function* test_pingExtendedStats() {
// Send a new ping that should contain the extended data.
yield TelemetrySession.reset();
yield sendPing();
request = yield gRequestIterator.next();
ping = decodeRequestPayload(request);
ping = yield PingServer.promiseNextPing();
checkPingFormat(ping, PING_TYPE_MAIN, true, true);
// Check that the payload now contains extended statistics fields.
@ -1679,7 +1628,7 @@ add_task(function* test_schedulerUserIdle() {
}, () => {});
yield TelemetrySession.reset();
yield clearPendingPings();
gRequestIterator = Iterator(new Request());
PingServer.clearRequests();
// When not idle, the scheduler should have a 5 minutes tick interval.
Assert.equal(schedulerTimeout, SCHEDULER_TICK_INTERVAL_MS);
@ -1707,31 +1656,6 @@ add_task(function* test_schedulerUserIdle() {
});
add_task(function* stopServer(){
gHttpServer.stop(do_test_finished);
yield PingServer.stop();
do_test_finished();
});
// An iterable sequence of http requests
function Request() {
let defers = [];
let current = 0;
function RequestIterator() {}
// Returns a promise that resolves to the next http request
RequestIterator.prototype.next = function() {
let deferred = defers[current++];
return deferred.promise;
}
this.__iterator__ = function(){
return new RequestIterator();
}
registerPingHandler((request, response) => {
let deferred = defers[defers.length - 1];
defers.push(Promise.defer());
deferred.resolve(request);
});
defers.push(Promise.defer());
}

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

@ -50,5 +50,6 @@ skip-if = os == "android" # Disabled due to intermittent orange on Android
skip-if = android_version == "18"
[test_ThreadHangStats.js]
run-sequentially = Bug 1046307, test can fail intermittently when CPU load is high
[test_TelemetrySend.js]
[test_ChildHistograms.js]
skip-if = os == "android"