Backed out changeset 396da163d87a (bug 1627541) for perma failures on test_geodefaults.js. CLOSED TREE

This commit is contained in:
Razvan Maries 2020-05-27 21:59:51 +03:00
Родитель 70ab539dc0
Коммит f2ef387aa5
20 изменённых файлов: 717 добавлений и 450 удалений

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

@ -116,10 +116,35 @@ const MULTI_LOCALE_ENGINES = [
"multilocale",
];
// A method that tries to determine if this user is in a US geography.
function isUSTimezone() {
// Timezone assumptions! We assume that if the system clock's timezone is
// between Newfoundland and Hawaii, that the user is in North America.
// This includes all of South America as well, but we have relatively few
// en-US users there, so that's OK.
// 150 minutes = 2.5 hours (UTC-2.5), which is
// Newfoundland Daylight Time (http://www.timeanddate.com/time/zones/ndt)
// 600 minutes = 10 hours (UTC-10), which is
// Hawaii-Aleutian Standard Time (http://www.timeanddate.com/time/zones/hast)
let UTCOffset = new Date().getTimezoneOffset();
return UTCOffset >= 150 && UTCOffset <= 600;
}
// A method that tries to determine our region via an XHR geoip lookup.
var ensureKnownRegion = async function(ss) {
var ensureKnownRegion = async function(ss, awaitRegionCheck) {
// If we have a region already stored in our prefs we trust it.
let region = Services.prefs.getCharPref("browser.search.region", "");
try {
if (gGeoSpecificDefaultsEnabled && !gModernConfig) {
if (gGeoSpecificDefaultsEnabled && !region) {
// We don't have it cached, so fetch it. fetchRegion() will call
// storeRegion if it gets a result (even if that happens after the
// promise resolves) and fetchRegionDefault.
await fetchRegion(ss, awaitRegionCheck);
} else if (gGeoSpecificDefaultsEnabled && !gModernConfig) {
// The territory default we have already fetched may have expired.
let expired = (ss.getGlobalAttr("searchDefaultExpir") || 0) <= Date.now();
// If we have a default engine or a list of visible default engines
@ -146,7 +171,7 @@ var ensureKnownRegion = async function(ss) {
clearTimeout(timerId);
resolve();
};
fetchRegionDefault(ss)
fetchRegionDefault(ss, awaitRegionCheck)
.then(callback)
.catch(err => {
Cu.reportError(err);
@ -169,6 +194,117 @@ var ensureKnownRegion = async function(ss) {
}
};
// Store the result of the geoip request as well as any other values and
// telemetry which depend on it.
async function storeRegion(region) {
let isTimezoneUS = isUSTimezone();
// If it's a US region, but not a US timezone, we don't store the value.
// This works because no region defaults to ZZ (unknown) in nsURLFormatter
if (region != "US" || isTimezoneUS) {
Services.prefs.setCharPref("browser.search.region", region);
}
// and telemetry...
if (region == "US" && !isTimezoneUS) {
SearchUtils.log("storeRegion mismatch - US Region, non-US timezone");
Services.telemetry
.getHistogramById("SEARCH_SERVICE_US_COUNTRY_MISMATCHED_TIMEZONE")
.add(1);
}
if (region != "US" && isTimezoneUS) {
SearchUtils.log("storeRegion mismatch - non-US Region, US timezone");
Services.telemetry
.getHistogramById("SEARCH_SERVICE_US_TIMEZONE_MISMATCHED_COUNTRY")
.add(1);
}
// telemetry to compare our geoip response with platform-specific country data.
// On Mac and Windows, we can get a country code via sysinfo
let platformCC = await Services.sysinfo.countryCode;
if (platformCC) {
let probeUSMismatched, probeNonUSMismatched;
switch (AppConstants.platform) {
case "macosx":
probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_OSX";
probeNonUSMismatched =
"SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_OSX";
break;
case "win":
probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_WIN";
probeNonUSMismatched =
"SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_WIN";
break;
default:
Cu.reportError(
"Platform " +
Services.appinfo.OS +
" has system country code but no search service telemetry probes"
);
break;
}
if (probeUSMismatched && probeNonUSMismatched) {
if (region == "US" || platformCC == "US") {
// one of the 2 said US, so record if they are the same.
Services.telemetry
.getHistogramById(probeUSMismatched)
.add(region != platformCC);
} else {
// non-US - record if they are the same
Services.telemetry
.getHistogramById(probeNonUSMismatched)
.add(region != platformCC);
}
}
}
}
// Get the region we are in via a XHR geoip request.
async function fetchRegion(ss, awaitRegionCheck) {
// values for the SEARCH_SERVICE_COUNTRY_FETCH_RESULT 'enum' telemetry probe.
const TELEMETRY_RESULT_ENUM = {
success: 0,
"region-fetch-no-result": 1,
"region-fetch-timeout": 2,
error: 3,
};
let startTime = Date.now();
let result;
let telemetryResult = TELEMETRY_RESULT_ENUM.success;
try {
result = await Region.getHomeRegion();
} catch (err) {
telemetryResult =
TELEMETRY_RESULT_ENUM[err.message] || TELEMETRY_RESULT_ENUM.error;
Cu.reportError(err);
}
let took = Date.now() - startTime;
if (result?.country_code) {
await storeRegion(result.country_code).catch(Cu.reportError);
}
SearchUtils.log(
"_fetchRegion got success response in " + took + "ms: " + result
);
Services.telemetry
.getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_TIME_MS")
.add(took);
// Now that we know the current region, it's possible to fetch defaults,
// which we couldn't do before in `ensureKnownRegion`.
try {
if (result && gModernConfig) {
await ss._maybeReloadEngines(awaitRegionCheck);
} else if (result && !gModernConfig) {
await fetchRegionDefault(ss, awaitRegionCheck);
}
} catch (ex) {
Cu.reportError(ex);
}
Services.telemetry
.getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_RESULT")
.add(telemetryResult);
}
// This converts our legacy google engines to the
// new codes. We have to manually change them here
// because we can't change the default name in absearch.
@ -199,7 +335,7 @@ function convertGoogleEngines(engineNames) {
// responsibility to ensure with a timer that we are not going to
// block the async init for too long.
// @deprecated Unused in the modern config.
var fetchRegionDefault = ss =>
var fetchRegionDefault = (ss, awaitRegionCheck) =>
new Promise(resolve => {
let urlTemplate = Services.prefs
.getDefaultBranch(SearchUtils.BROWSER_SEARCH_PREF)
@ -276,7 +412,7 @@ var fetchRegionDefault = ss =>
);
// If we're doing this somewhere during the app's lifetime, reload the list
// of engines in order to pick up any geo-specific changes.
ss._maybeReloadEngines().finally(resolve);
ss._maybeReloadEngines(awaitRegionCheck).finally(resolve);
};
request.ontimeout = function(event) {
SearchUtils.log("fetchRegionDefault: XHR finally timed-out");
@ -527,10 +663,15 @@ SearchService.prototype = {
/**
* Asynchronous implementation of the initializer.
*
* @param {boolean} [awaitRegionCheck]
* Indicates whether we should explicitly await the the region check process to
* complete, which may be fetched remotely. Pass in `true` if the caller needs
* to be absolutely certain of the correct default engine and/ or ordering of
* visible engines.
* @returns {number}
* A Components.results success code on success, otherwise a failure code.
*/
async _init() {
async _init(awaitRegionCheck) {
SearchUtils.log("_init start");
XPCOMUtils.defineLazyPreferenceGetter(
@ -549,11 +690,6 @@ SearchService.prototype = {
this._onSeparateDefaultPrefChanged.bind(this)
);
// We need to catch the region being updated
// during initialisation so we start listening
// straight away.
Services.obs.addObserver(this, Region.REGION_TOPIC);
try {
if (gModernConfig) {
// Create the search engine selector.
@ -568,11 +704,14 @@ SearchService.prototype = {
// The init flow is not going to block on a fetch from an external service,
// but we're kicking it off as soon as possible to prevent UI flickering as
// much as possible.
this._ensureKnownRegionPromise = ensureKnownRegion(this)
this._ensureKnownRegionPromise = ensureKnownRegion(this, awaitRegionCheck)
.catch(ex =>
SearchUtils.log("_init: failure determining region: " + ex)
)
.finally(() => (this._ensureKnownRegionPromise = null));
if (awaitRegionCheck) {
await this._ensureKnownRegionPromise;
}
this._setupRemoteSettings().catch(Cu.reportError);
@ -1319,10 +1458,17 @@ SearchService.prototype = {
},
/**
* Reloads engines asynchronously, but only when
* the service has already been initialized.
* Reloads engines asynchronously, but only when the service has already been
* initialized.
* @param {boolean} awaitRegionCheck
* Whether the caller is waiting for the region check.
*/
async _maybeReloadEngines() {
async _maybeReloadEngines(awaitRegionCheck) {
// The caller is already awaiting on the region check
// completing, so we dont need to queue a reload.
if (awaitRegionCheck) {
return;
}
if (!gInitialized) {
if (this._maybeReloadDebounce) {
SearchUtils.log(
@ -1408,8 +1554,8 @@ SearchService.prototype = {
);
},
_reInit(origin) {
SearchUtils.log("_reInit");
_reInit(origin, awaitRegionCheck = false) {
SearchUtils.log("_reInit: " + awaitRegionCheck);
// Re-entrance guard, because we're using an async lambda below.
if (gReinitializing) {
SearchUtils.log("_reInit: already re-initializing, bailing out.");
@ -1466,12 +1612,19 @@ SearchService.prototype = {
// The init flow is not going to block on a fetch from an external service,
// but we're kicking it off as soon as possible to prevent UI flickering as
// much as possible.
this._ensureKnownRegionPromise = ensureKnownRegion(this)
this._ensureKnownRegionPromise = ensureKnownRegion(
this,
awaitRegionCheck
)
.catch(ex =>
SearchUtils.log("_reInit: failure determining region: " + ex)
)
.finally(() => (this._ensureKnownRegionPromise = null));
if (awaitRegionCheck) {
await this._ensureKnownRegionPromise;
}
await this._loadEngines(cache);
// If we've got this far, but the application is now shutting down,
@ -1765,7 +1918,7 @@ SearchService.prototype = {
SearchUtils.log("_findEngineSelectorEngines: init");
let locale = Services.locale.appLocaleAsBCP47;
let region = Region.home || "default";
let region = Services.prefs.getCharPref("browser.search.region", "default");
let channel = AppConstants.MOZ_APP_VERSION_DISPLAY.endsWith("esr")
? "esr"
@ -1851,7 +2004,7 @@ SearchService.prototype = {
* @returns {Array<string>}
* Returns an array of engine names.
*/
async _parseListJSON(list) {
_parseListJSON(list) {
let json;
try {
json = JSON.parse(list);
@ -1861,7 +2014,10 @@ SearchService.prototype = {
return [];
}
let searchRegion = Region.home;
let searchRegion = Services.prefs.getCharPref(
"browser.search.region",
null
);
let searchSettings;
let locale = Services.locale.appLocaleAsBCP47;
@ -2249,9 +2405,12 @@ SearchService.prototype = {
},
// nsISearchService
async init() {
SearchUtils.log("SearchService.init");
async init(awaitRegionCheck = false) {
SearchUtils.log("SearchService.init: " + awaitRegionCheck);
if (this._initStarted) {
if (awaitRegionCheck) {
await this._ensureKnownRegionPromise;
}
return this._initObservers.promise;
}
@ -2259,7 +2418,7 @@ SearchService.prototype = {
this._initStarted = true;
try {
// Complete initialization by calling asynchronous initializer.
await this._init();
await this._init(awaitRegionCheck);
TelemetryStopwatch.finish("SEARCH_SERVICE_INIT_MS");
} catch (ex) {
if (ex.result == Cr.NS_ERROR_ALREADY_INITIALIZED) {
@ -2286,8 +2445,8 @@ SearchService.prototype = {
},
// reInit is currently only exposed for testing purposes
async reInit() {
return this._reInit("test");
async reInit(awaitRegionCheck) {
return this._reInit("test", awaitRegionCheck);
},
async getEngines() {
@ -3513,14 +3672,6 @@ SearchService.prototype = {
}
});
break;
case Region.REGION_TOPIC:
if (verb == Region.REGION_UPDATED) {
SearchUtils.log("Region updated: " + Region.home);
ensureKnownRegion(this)
.then(this._maybeReloadEngines.bind(this))
.catch(Cu.reportError);
}
break;
}
},
@ -3653,7 +3804,6 @@ SearchService.prototype = {
Services.obs.removeObserver(this, SearchUtils.TOPIC_ENGINE_MODIFIED);
Services.obs.removeObserver(this, QUIT_APPLICATION_TOPIC);
Services.obs.removeObserver(this, TOPIC_LOCALES_CHANGE);
Services.obs.removeObserver(this, Region.REGION_TOPIC);
},
QueryInterface: ChromeUtils.generateQI([

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

@ -228,14 +228,20 @@ interface nsISearchService : nsISupports
* immediately, if initialization has already been completed by some previous
* call to this method.
* This method should only be called when you need or want to wait for the
* full initialization of the search service.
* full initialization of the search service, which may include waiting for
* outbound service requests.
*
* @param skipRegionCheck
* Flag to avoid SearchService from waiting on the results
* of the region lookups before completing initialisation.
* Exposed for testing only.
*/
Promise init();
Promise init([optional] in boolean skipRegionCheck);
/**
* Exposed for testing.
*/
void reInit();
void reInit([optional] in boolean skipRegionCheck);
void reset();
Promise makeEngineFromConfig(in jsval config);
Promise ensureBuiltinExtension(in AString id,

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

@ -9,7 +9,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
FileUtils: "resource://gre/modules/FileUtils.jsm",
NetUtil: "resource://gre/modules/NetUtil.jsm",
PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
Region: "resource://gre/modules/Region.jsm",
RemoteSettings: "resource://services-settings/remote-settings.js",
RemoteSettingsClient: "resource://services-settings/RemoteSettingsClient.jsm",
SearchEngineSelector: "resource://gre/modules/SearchEngineSelector.jsm",
@ -46,7 +45,6 @@ var XULRuntime = Cc["@mozilla.org/xre/runtime;1"].getService(Ci.nsIXULRuntime);
// Expand the amount of information available in error logs
Services.prefs.setBoolPref("browser.search.log", true);
Services.prefs.setBoolPref("browser.region.log", true);
XPCOMUtils.defineLazyPreferenceGetter(
this,
@ -534,12 +532,14 @@ function installTestEngine() {
}
async function asyncReInit({ awaitRegionFetch = false } = {}) {
let promises = [
SearchTestUtils.promiseSearchNotification("reinit-complete"),
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
];
let promises = [SearchTestUtils.promiseSearchNotification("reinit-complete")];
if (awaitRegionFetch) {
promises.push(
SearchTestUtils.promiseSearchNotification("ensure-known-region-done")
);
}
Services.search.reInit();
Services.search.reInit(awaitRegionFetch);
return Promise.all(promises);
}

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

@ -12,7 +12,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
AppConstants: "resource://gre/modules/AppConstants.jsm",
ObjectUtils: "resource://gre/modules/ObjectUtils.jsm",
OS: "resource://gre/modules/osfile.jsm",
Region: "resource://gre/modules/Region.jsm",
RemoteSettings: "resource://services-settings/remote-settings.js",
SearchEngine: "resource://gre/modules/SearchEngine.jsm",
SearchEngineSelector: "resource://gre/modules/SearchEngineSelector.jsm",
@ -215,11 +214,14 @@ class SearchConfigTest {
* The two-letter locale code.
*/
async _reinit(region, locale) {
Region._setRegion(region?.toUpperCase(), true);
if (region) {
await SearchTestUtils.promiseSearchNotification("engines-reloaded");
Services.prefs.setStringPref(
"browser.search.region",
region.toUpperCase()
);
} else {
Services.prefs.clearUserPref("browser.search.region");
}
const reinitCompletePromise = SearchTestUtils.promiseSearchNotification(
"reinit-complete"
);

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

@ -18,6 +18,8 @@ add_task(async function no_request_if_prefed_off() {
await Services.search.init(true);
await promiseAfterCache();
checkNoRequest(requests);
// Install kTestEngineName and wait for it to reach the disk.
await Promise.all([installTestEngine(), promiseAfterCache()]);
@ -42,8 +44,11 @@ add_task(async function should_get_geo_defaults_only_once() {
// (Re)initializing the search service should trigger a request,
// and set the default engine based on it.
// Due to the previous initialization, we expect the region to already be set.
Region._setRegion("FR", false);
await Promise.all([asyncReInit(), promiseAfterCache()]);
await Promise.all([
asyncReInit({ awaitRegionFetch: true }),
promiseAfterCache(),
]);
checkRequest(requests);
Assert.equal((await Services.search.getDefault()).name, kTestEngineName);
// Verify the metadata was written correctly.
@ -56,17 +61,33 @@ add_task(async function should_get_geo_defaults_only_once() {
Assert.equal(metadata.searchDefaultHash.length, 44);
// The next restart shouldn't trigger a request.
await asyncReInit();
await asyncReInit({ awaitRegionFetch: true });
checkNoRequest(requests);
Assert.equal((await Services.search.getDefault()).name, kTestEngineName);
});
});
add_task(async function should_request_when_region_not_set() {
await withGeoServer(async function cont(requests) {
Services.prefs.clearUserPref("browser.search.region");
await Promise.all([
asyncReInit({ awaitRegionFetch: true }),
promiseAfterCache(),
]);
checkRequest(requests);
});
});
add_task(async function should_recheck_if_interval_expired() {
await withGeoServer(async function cont(requests) {
await forceExpiration();
let date = Date.now();
await Promise.all([asyncReInit(), promiseAfterCache()]);
await Promise.all([
asyncReInit({ awaitRegionFetch: true }),
promiseAfterCache(),
]);
checkRequest(requests);
// Check that the expiration timestamp has been updated.
let metadata = await promiseGlobalMetadata();
@ -88,7 +109,10 @@ add_task(async function should_recheck_if_appversion_changed() {
data.appVersion = "1";
await promiseSaveCacheData(data);
await Promise.all([asyncReInit(), promiseAfterCache()]);
await Promise.all([
asyncReInit({ awaitRegionFetch: true }),
promiseAfterCache(),
]);
checkRequest(requests);
// Check that the appVersion has been updated
@ -114,7 +138,7 @@ add_task(async function should_recheck_when_broken_hash() {
let unInitPromise = SearchTestUtils.promiseSearchNotification(
"uninit-complete"
);
let reInitPromise = asyncReInit();
let reInitPromise = asyncReInit({ awaitRegionFetch: true });
await unInitPromise;
await reInitPromise;
@ -158,7 +182,7 @@ add_task(async function should_remember_cohort_id() {
// Trigger a new request.
await forceExpiration();
let commitPromise = promiseAfterCache();
await asyncReInit();
await asyncReInit({ awaitRegionFetch: true });
checkRequest(requests);
await commitPromise;
@ -178,7 +202,7 @@ add_task(async function should_remember_cohort_id() {
// will remove it from the prefs due to the server no longer sending it.
await forceExpiration();
let commitPromise = promiseAfterCache();
await asyncReInit();
await asyncReInit({ awaitRegionFetch: true });
checkRequest(requests, cohort);
await commitPromise;
Assert.equal(
@ -193,14 +217,41 @@ add_task(async function should_retry_after_failure() {
async function cont(requests) {
// Trigger a new request.
await forceExpiration();
await asyncReInit();
await asyncReInit({ awaitRegionFetch: true });
checkRequest(requests);
// After another restart, a new request should be triggered automatically without
// the test having to call forceExpiration again.
await asyncReInit();
await asyncReInit({ awaitRegionFetch: true });
checkRequest(requests);
},
{ path: "lookup_fail" }
);
});
add_task(async function should_honor_retry_after_header() {
await withGeoServer(
async function cont(requests) {
// Trigger a new request.
await forceExpiration();
let date = Date.now();
let commitPromise = promiseAfterCache();
await asyncReInit({ awaitRegionFetch: true });
await commitPromise;
checkRequest(requests);
// Check that the expiration timestamp has been updated.
let metadata = await promiseGlobalMetadata();
Assert.equal(typeof metadata.searchDefaultExpir, "number");
Assert.ok(metadata.searchDefaultExpir >= date + kDayInSeconds * 1000);
Assert.ok(
metadata.searchDefaultExpir < date + (kDayInSeconds + 3600) * 1000
);
// After another restart, a new request should not be triggered.
await asyncReInit({ awaitRegionFetch: true });
checkNoRequest(requests);
},
{ path: "lookup_unavailable" }
);
});

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

@ -14,31 +14,153 @@ add_task(async function setup() {
add_task(async function test_regular_init() {
await withGeoServer(
async function cont(requests) {
Region._setRegion("us", false);
await Services.search.init();
await promiseAfterCache();
let reloadObserved = false;
let obs = (subject, topic, data) => {
if (data == "engines-reloaded") {
reloadObserved = true;
}
};
Services.obs.addObserver(obs, SEARCH_SERVICE_TOPIC);
Assert.notEqual(
Services.search.defaultEngine.name,
kTestEngineName,
"Test engine shouldn't be the default"
);
await Promise.all([
Services.search.init(true),
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
promiseAfterCache(),
]);
// If the system is under load (e.g. xpcshell-tests running in parallel),
// then we can hit the state where init() is still running, but the cache
// save fires early. This generates a second `write-cache-to-disk-complete`
// which can then let us sometimes proceed early. Therefore we have this
// additional wait to ensure that any cache saves are complete before
// we move on.
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 2000));
checkRequest(requests);
// Install kTestEngineName and wait for it to reach the disk.
await Promise.all([installTestEngine(), promiseAfterCache()]);
let enginesReloaded = SearchTestUtils.promiseSearchNotification(
"engines-reloaded"
);
Region._setRegion("FR", true);
await enginesReloaded;
await promiseAfterCache();
Assert.equal(
kTestEngineName,
(await Services.search.getDefault()).name,
"Geo defined default should be set"
);
checkNoRequest(requests);
Assert.ok(
!reloadObserved,
"Engines should not be reloaded during test, because region fetch succeeded"
);
Services.obs.removeObserver(obs, SEARCH_SERVICE_TOPIC);
},
{ delay: 100 }
);
});
add_task(async function test_init_with_skip_regioncheck() {
await withGeoServer(
async function cont(requests) {
let reloadObserved = false;
let obs = (subject, topic, data) => {
if (data == "engines-reloaded") {
reloadObserved = true;
}
};
Services.obs.addObserver(obs, SEARCH_SERVICE_TOPIC);
// Break the hash.
let metadata = await promiseGlobalMetadata();
metadata.searchDefaultHash = "broken";
await promiseSaveGlobalMetadata(metadata);
// Kick off a re-init.
await asyncReInit();
let otherPromises = [
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
promiseAfterCache(),
SearchTestUtils.promiseSearchNotification(
"engine-default",
SEARCH_ENGINE_TOPIC
),
];
// Make sure that the original default engine is put back in place.
Services.search.resetToOriginalDefaultEngine();
Assert.notEqual(
Services.search.defaultEngine.name,
kTestEngineName,
"Test engine shouldn't be the default anymore"
);
await Promise.all(otherPromises);
checkRequest(requests);
// Ensure that correct engine is being reported as the default.
Assert.equal(
Services.search.defaultEngine.name,
kTestEngineName,
"Test engine should be the default, because region fetch succeeded"
);
Assert.ok(
reloadObserved,
"Engines should be reloaded during test, because region fetch succeeded"
);
Services.obs.removeObserver(obs, SEARCH_SERVICE_TOPIC);
},
{ delay: 100 }
);
});
add_task(async function test_init_with_skip_regioncheck_no_engine_change() {
await withGeoServer(
async function cont(requests) {
let reloadObserved = false;
let reloadObs = (subject, topic, data) => {
if (data == "engines-reloaded") {
reloadObserved = true;
}
};
Services.obs.addObserver(reloadObs, SEARCH_SERVICE_TOPIC);
let engineChanged = false;
let changeObs = (subject, topic, data) => {
if (data == "engine-default") {
engineChanged = true;
}
};
Services.obs.addObserver(changeObs, SEARCH_ENGINE_TOPIC);
// Break the hash.
let metadata = await promiseGlobalMetadata();
metadata.searchDefaultHash = "broken";
await promiseSaveGlobalMetadata(metadata);
// Kick off a re-init.
await Promise.all([
asyncReInit(),
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
promiseAfterCache(),
]);
checkRequest(requests);
// Ensure that correct engine is being reported as the default.
Assert.equal(
Services.search.defaultEngine.name,
kTestEngineName,
"Test engine should be the default, because region fetch succeeded"
);
Assert.ok(
reloadObserved,
"Engines should be reloaded during test, because region fetch succeeded"
);
Services.obs.removeObserver(reloadObs, SEARCH_SERVICE_TOPIC);
Assert.ok(
!engineChanged,
"Engine should not have changed when a region fetch didn't change it"
);
Services.obs.removeObserver(changeObs, SEARCH_ENGINE_TOPIC);
},
{ delay: 100 }
);

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

@ -16,7 +16,7 @@ add_task(async function setup() {
SearchUtils.BROWSER_SEARCH_PREF + "separatePrivateDefault",
true
);
Region._setRegion("US", false);
Services.prefs.setCharPref("browser.search.region", "US");
});
add_task(async function test_listJSONlocale() {
@ -82,7 +82,7 @@ add_task(async function test_listJSONlocaleSwitch() {
// Check that region overrides apply
add_task(async function test_listJSONRegionOverride() {
Region._setRegion("RU", false);
Services.prefs.setCharPref("browser.search.region", "RU");
await asyncReInit();

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

@ -0,0 +1,82 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
add_task(async function setup() {
await AddonTestUtils.promiseStartupManager();
Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true);
Services.prefs
.getDefaultBranch(SearchUtils.BROWSER_SEARCH_PREF)
.setCharPref("geoSpecificDefaults.url", "");
});
add_task(async function test_location() {
Services.prefs.setCharPref(
"browser.region.network.url",
'data:application/json,{"country_code": "AU"}'
);
await Services.search.init(true);
equal(
Services.prefs.getCharPref("browser.search.region"),
"AU",
"got the correct region."
);
// check we have "success" recorded in telemetry
checkCountryResultTelemetry(TELEMETRY_RESULT_ENUM.SUCCESS);
// simple checks for our platform-specific telemetry. We can't influence
// what they return (as we can't influence the countryCode the platform
// thinks we are in), but we can check the values are correct given reality.
let probeUSMismatched, probeNonUSMismatched;
switch (AppConstants.platform) {
case "macosx":
probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_OSX";
probeNonUSMismatched =
"SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_OSX";
break;
case "win":
probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_WIN";
probeNonUSMismatched =
"SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_WIN";
break;
default:
break;
}
if (probeUSMismatched && probeNonUSMismatched) {
let countryCode = await Services.sysinfo.countryCode;
print("Platform says the country-code is", countryCode);
if (!countryCode) {
// On treeherder for Mac the countryCode is null, so the probes won't be
// recorded.
// We still let the test run for Mac, as a developer would likely
// eventually pick up on the issue.
info("No country code set on this machine, skipping rest of test");
return;
}
let expectedResult;
let hid;
// We know geoip said AU - if the platform thinks US then we expect
// probeUSMismatched with true (ie, a mismatch)
if (countryCode == "US") {
hid = probeUSMismatched;
expectedResult = { 0: 0, 1: 1, 2: 0 }; // boolean probe so 3 buckets, expect 1 result for |1|.
} else {
// We are expecting probeNonUSMismatched with false if the platform
// says AU (not a mismatch) and true otherwise.
hid = probeNonUSMismatched;
expectedResult =
countryCode == "AU" ? { 0: 1, 1: 0 } : { 0: 0, 1: 1, 2: 0 };
}
const histogram = Services.telemetry.getHistogramById(hid);
let snapshot;
await TestUtils.waitForCondition(() => {
snapshot = histogram.snapshot();
return snapshot.sum == 1;
});
deepEqual(snapshot.values, expectedResult);
}
});

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

@ -0,0 +1,21 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
add_task(async function setup() {
await AddonTestUtils.promiseStartupManager();
Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true);
});
add_task(async function test_location_error() {
// We use an invalid port that parses but won't open
let url = "http://localhost:0";
Services.prefs.setCharPref("browser.region.network.url", url);
await Services.search.init();
try {
Services.prefs.getCharPref("browser.search.region");
ok(false, "not expecting region to be set");
} catch (ex) {}
// should have an error recorded.
checkCountryResultTelemetry(TELEMETRY_RESULT_ENUM.ERROR);
});

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

@ -0,0 +1,49 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// A console listener so we can listen for a log message from nsSearchService.
function promiseTimezoneMessage() {
return new Promise(resolve => {
let listener = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIConsoleListener]),
observe(msg) {
if (
msg.message.startsWith(
"getIsUS() fell back to a timezone check with the result="
)
) {
Services.console.unregisterListener(listener);
resolve(msg);
}
},
};
Services.console.registerListener(listener);
});
}
add_task(async function setup() {
Services.prefs.setBoolPref("browser.region.log", true);
await AddonTestUtils.promiseStartupManager();
Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true);
});
add_task(async function test_location_malformed_json() {
// Here we have malformed JSON
Services.prefs.setCharPref(
"browser.region.network.url",
'data:application/json,{"country_code"'
);
await Services.search.init();
ok(
!Services.prefs.prefHasUserValue("browser.search.region"),
"should be no region pref"
);
// fetch the engines - this should not persist any prefs.
await Services.search.getEngines();
ok(
!Services.prefs.prefHasUserValue("browser.search.region"),
"should be no region pref"
);
// should have recorded SUCCESS_WITHOUT_DATA
checkCountryResultTelemetry(TELEMETRY_RESULT_ENUM.SUCCESS_WITHOUT_DATA);
});

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

@ -29,10 +29,10 @@ add_task(async function basic_multilocale_test() {
let initPromise = new Promise(resolve => (resolver = resolve));
useCustomGeoServer("FR", initPromise);
await Services.search.init();
await Services.search.init(false);
await Services.search.getDefaultEngines();
resolver();
await SearchTestUtils.promiseSearchNotification("engines-reloaded");
await SearchTestUtils.promiseSearchNotification("ensure-known-region-done");
let engines = await Services.search.getDefaultEngines();

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

@ -8,27 +8,44 @@ const SEARCH_SERVICE_TOPIC = "browser-search-service";
add_task(async function setup() {
await AddonTestUtils.promiseStartupManager();
await useTestEngines("data", "geolookup-extensions");
Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true);
});
add_task(async function test_maybereloadengine_update() {
await Services.search.init();
let reloadObserved = false;
let obs = (subject, topic, data) => {
if (data == "engines-reloaded") {
reloadObserved = true;
}
};
Services.obs.addObserver(obs, SEARCH_SERVICE_TOPIC);
let defaultEngine = await Services.search.getDefault();
Assert.equal(
defaultEngine._shortName,
"multilocale-an",
"Should have update to the new engine with the same name"
);
let initPromise = Services.search.init(false);
let enginesReloaded = SearchTestUtils.promiseSearchNotification(
"engines-reloaded"
);
Region._setRegion("FR", true);
await enginesReloaded;
async function cont(requests) {
await Promise.all([
initPromise,
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
promiseAfterCache(),
]);
Assert.equal(
defaultEngine._shortName,
"multilocale-af",
"Should have update to the new engine with the same name"
);
Assert.ok(
reloadObserved,
"Engines should be reloaded during test, because region fetch succeeded"
);
let defaultEngine = await Services.search.getDefault();
Assert.equal(
defaultEngine._shortName,
"multilocale-af",
"Should have update to the new engine with the same name"
);
Services.obs.removeObserver(obs, SEARCH_SERVICE_TOPIC);
}
await withGeoServer(cont, {
geoLookupData: { country_code: "FR" },
preGeolookupPromise: initPromise,
});
});

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

@ -17,31 +17,57 @@ add_task(async function setup() {
installDistributionEngine();
Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true);
await AddonTestUtils.promiseStartupManager();
});
add_task(async function test_maybereloadengine_update_distro() {
await Services.search.init();
let reloadObserved = false;
let obs = (subject, topic, data) => {
if (data == "engines-reloaded") {
reloadObserved = true;
}
};
Services.obs.addObserver(obs, SEARCH_SERVICE_TOPIC);
Region._setRegion("FR", true);
await SearchTestUtils.promiseSearchNotification("engines-reloaded");
let initPromise = Services.search.init(false);
let defaultEngine = await Services.search.getDefault();
Assert.equal(
defaultEngine._shortName,
"basic",
"Should have kept the same name"
);
Assert.equal(
defaultEngine._loadPath,
"[distribution]/searchplugins/common/basic.xml",
"Should have kept the distribution engine"
);
Assert.equal(
defaultEngine
._getURLOfType("text/html")
.getSubmission("", defaultEngine, "searchbar").uri.spec,
"http://searchtest.local/?search=",
"Should have kept the same submission URL"
);
async function cont(requests) {
await Promise.all([
initPromise,
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
promiseAfterCache(),
]);
Assert.ok(
reloadObserved,
"Engines should be reloaded during test, because region fetch succeeded"
);
let defaultEngine = await Services.search.getDefault();
Assert.equal(
defaultEngine._shortName,
"basic",
"Should have kept the same name"
);
Assert.equal(
defaultEngine._loadPath,
"[distribution]/searchplugins/common/basic.xml",
"Should have kept the distribution engine"
);
Assert.equal(
defaultEngine
._getURLOfType("text/html")
.getSubmission("", defaultEngine, "searchbar").uri.spec,
"http://searchtest.local/?search=",
"Should have kept the same submission URL"
);
Services.obs.removeObserver(obs, SEARCH_SERVICE_TOPIC);
}
await withGeoServer(cont, {
geoLookupData: { country_code: "FR" },
preGeolookupPromise: initPromise,
});
});

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

@ -8,7 +8,10 @@
"use strict";
add_task(async function setup() {
Region._setRegion("an", false);
Services.prefs.setCharPref("browser.search.region", "an");
Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true);
await AddonTestUtils.promiseStartupManager();
await useTestEngines("test-extensions");
});
@ -48,7 +51,7 @@ add_task(async function test_changeRegion() {
let reInitPromise = SearchTestUtils.promiseSearchNotification(
"reinit-complete"
);
Region._setRegion("tr", false);
Services.prefs.setCharPref("browser.search.region", "tr");
Services.search.reInit();
await reInitPromise;

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

@ -156,12 +156,13 @@ add_task(async function setup() {
});
add_task(async function test_regular_init() {
let reloadObserved = listenFor(SEARCH_SERVICE_TOPIC, "engines-reloaded");
let geoUrl = `data:application/json,{"country_code": "FR"}`;
Services.prefs.setCharPref("browser.region.network.url", geoUrl);
await Promise.all([
Services.search.init(),
SearchTestUtils.promiseSearchNotification("engines-reloaded"),
Services.search.init(true),
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
promiseAfterCache(),
]);
@ -170,6 +171,11 @@ add_task(async function test_regular_init() {
FR_DEFAULT,
"Geo defined default should be set"
);
Assert.ok(
!reloadObserved(),
"Engines don't reload with immediate region fetch"
);
});
add_task(async function test_init_with_slow_region_lookup() {
@ -178,6 +184,7 @@ add_task(async function test_init_with_slow_region_lookup() {
// Ensure the region lookup completes after init so the
// engines are reloaded
Services.prefs.setCharPref("browser.search.region", "");
let srv = useHttpServer();
srv.registerPathHandler("/fetch_region", async (req, res) => {
res.processAsync();
@ -192,14 +199,12 @@ add_task(async function test_init_with_slow_region_lookup() {
`http://localhost:${srv.identity.primaryPort}/fetch_region`
);
Region._setRegion("", false);
Region.init();
// Kick off a re-init.
initPromise = asyncReInit();
await initPromise;
let otherPromises = [
SearchTestUtils.promiseSearchNotification("ensure-known-region-done"),
promiseAfterCache(),
SearchTestUtils.promiseSearchNotification(
"engine-default",

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

@ -15,7 +15,7 @@ SearchTestUtils.initXPCShellAddonManager(this);
async function restart() {
Services.search.reset();
await promiseRestartManager();
await Services.search.init();
await Services.search.init(false);
}
async function getEngineNames() {
@ -61,7 +61,7 @@ add_task(async function basic_install_test() {
add_task(async function basic_multilocale_test() {
await forceExpiration();
Region._setRegion("an", false);
Services.prefs.setCharPref("browser.search.region", "an");
await withGeoServer(
async function cont(requests) {
@ -78,7 +78,7 @@ add_task(async function basic_multilocale_test() {
add_task(async function complex_multilocale_test() {
await forceExpiration();
Region._setRegion("af", false);
Services.prefs.setCharPref("browser.search.region", "af");
await withGeoServer(
async function cont(requests) {
@ -95,7 +95,7 @@ add_task(async function complex_multilocale_test() {
});
add_task(async function test_manifest_selection() {
await forceExpiration();
Region._setRegion("an", false);
Services.prefs.setCharPref("browser.search.region", "an");
Services.locale.availableLocales = ["af"];
Services.locale.requestedLocales = ["af"];

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

@ -43,6 +43,9 @@ support-files =
data1/engines.json
[test_list_json_searchdefault.js]
[test_list_json_searchorder.js]
[test_location_error.js]
[test_location_malformed_json.js]
[test_location.js]
[test_migrateWebExtensionEngine.js]
[test_multipleIcons.js]
[test_nocache.js]

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

@ -70,6 +70,7 @@ support-files =
[test_engine_selector_application_distribution.js]
[test_engine_selector_application_name.js]
[test_reload_engines.js]
[test_location_timeout.js]
[test_missing_engine.js]
[test_sort_orders-no-hints.js]
[test_webextensions_builtin_upgrade.js]

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

@ -11,7 +11,6 @@ const { XPCOMUtils } = ChromeUtils.import(
);
XPCOMUtils.defineLazyModuleGetters(this, {
AppConstants: "resource://gre/modules/AppConstants.jsm",
LocationHelper: "resource://gre/modules/LocationHelper.jsm",
Services: "resource://gre/modules/Services.jsm",
setTimeout: "resource://gre/modules/Timer.jsm",
@ -40,19 +39,14 @@ XPCOMUtils.defineLazyPreferenceGetter(
false
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"geoSpecificDefaults",
"browser.search.geoSpecificDefaults",
false
);
const log = console.createInstance({
prefix: "Region.jsm",
maxLogLevel: loggingEnabled ? "All" : "Warn",
});
const REGION_PREF = "browser.search.region";
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* This module keeps track of the users current region (country).
@ -65,172 +59,22 @@ class RegionDetector {
// Store the AbortController for the geolocation requests so we
// can abort the request on timeout.
fetchController = null;
// Topic for Observer events fired by Region.jsm.
REGION_TOPIC = "browser-region";
// Verb for event fired when we update the region.
REGION_UPDATED = "region-updated";
// Values for telemetry.
TELEMETRY = {
SUCCESS: 0,
NO_RESULT: 1,
TIMEOUT: 2,
ERROR: 3,
};
/**
* Read currently stored region data and if needed trigger background
* region detection.
*/
init() {
let region = Services.prefs.getCharPref(REGION_PREF, null);
if (!region) {
Services.tm.idleDispatchToMainThread(this._fetchRegion.bind(this));
}
this._home = region;
}
/**
* Get the region we currently consider the users home.
* Fetch the region that we consider to be the users home region.
*
* @returns {string}
* The users current home region.
* @returns {object}
* JSON with country_code field defining users current region.
*/
get home() {
return this._home;
async getHomeRegion() {
return Promise.race([this._getRegion(), this._timeout()]);
}
/**
* Fetch the users current region.
*
* @returns {string}
* The country_code defining users current region.
*/
async _fetchRegion() {
if (!geoSpecificDefaults) {
return null;
}
let startTime = Date.now();
let telemetryResult = this.TELEMETRY.SUCCESS;
let result = null;
try {
result = await Promise.race([
this._getRegion(),
timeout(regionFetchTimeout),
]);
} catch (err) {
telemetryResult = this.TELEMETRY[err.message] || this.TELEMETRY.ERROR;
log.error("Failed to fetch region", err);
}
let took = Date.now() - startTime;
if (result) {
await this._storeRegion(result);
}
Services.telemetry
.getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_TIME_MS")
.add(took);
Services.telemetry
.getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_RESULT")
.add(telemetryResult);
return result;
}
/**
* Validate then store the region and report telemetry.
*
* @param region
* The region to store.
*/
async _storeRegion(region) {
let prefix = "SEARCH_SERVICE";
let isTimezoneUS = isUSTimezone();
// If it's a US region, but not a US timezone, we don't store
// the value. This works because no region defaults to
// ZZ (unknown) in nsURLFormatter
if (region != "US" || isTimezoneUS) {
log.info("Saving home region:", region);
this._setRegion(region, true);
}
// and telemetry...
if (region == "US" && !isTimezoneUS) {
log.info("storeRegion mismatch - US Region, non-US timezone");
Services.telemetry
.getHistogramById(`${prefix}_US_COUNTRY_MISMATCHED_TIMEZONE`)
.add(1);
}
if (region != "US" && isTimezoneUS) {
log.info("storeRegion mismatch - non-US Region, US timezone");
Services.telemetry
.getHistogramById(`${prefix}_US_TIMEZONE_MISMATCHED_COUNTRY`)
.add(1);
}
// telemetry to compare our geoip response with
// platform-specific country data.
// On Mac and Windows, we can get a country code via sysinfo
let platformCC = await Services.sysinfo.countryCode;
if (platformCC) {
let probeUSMismatched, probeNonUSMismatched;
switch (AppConstants.platform) {
case "macosx":
probeUSMismatched = `${prefix}_US_COUNTRY_MISMATCHED_PLATFORM_OSX`;
probeNonUSMismatched = `${prefix}_NONUS_COUNTRY_MISMATCHED_PLATFORM_OSX`;
break;
case "win":
probeUSMismatched = `${prefix}_US_COUNTRY_MISMATCHED_PLATFORM_WIN`;
probeNonUSMismatched = `${prefix}_NONUS_COUNTRY_MISMATCHED_PLATFORM_WIN`;
break;
default:
log.error(
"Platform " +
Services.appinfo.OS +
" has system country code but no search service telemetry probes"
);
break;
}
if (probeUSMismatched && probeNonUSMismatched) {
if (region == "US" || platformCC == "US") {
// one of the 2 said US, so record if they are the same.
Services.telemetry
.getHistogramById(probeUSMismatched)
.add(region != platformCC);
} else {
// non-US - record if they are the same
Services.telemetry
.getHistogramById(probeNonUSMismatched)
.add(region != platformCC);
}
}
}
}
/**
* Save the updated region and notify observers.
*
* @param region
* The region to store.
*/
_setRegion(region = "", notify = false) {
this._home = region;
Services.prefs.setCharPref("browser.search.region", region);
if (notify) {
Services.obs.notifyObservers(
null,
this.REGION_TOPIC,
this.REGION_UPDATED,
region
);
}
}
/**
* Make the request to fetch the region from the configured service.
* Implementation of the above region fetching.
*/
async _getRegion() {
log.info("_getRegion called");
log.info("getRegion called");
this.fetchController = new AbortController();
let fetchOpts = {
headers: { "Content-Type": "application/json" },
@ -252,11 +96,11 @@ class RegionDetector {
try {
let res = await req.json();
this.fetchController = null;
log.info("_getRegion returning ", res.country_code);
return res.country_code;
log.info("getRegion returning " + res.country_code);
return { country_code: res.country_code };
} catch (err) {
log.error("Error fetching region", err);
throw new Error("NO_RESULT");
throw new Error("region-fetch-no-result");
}
}
@ -270,7 +114,7 @@ class RegionDetector {
if (this.fetchController) {
this.fetchController.abort();
}
throw new Error("TIMEOUT");
throw new Error("region-fetch-timeout");
}
async _fetchWifiData() {
@ -305,26 +149,3 @@ class RegionDetector {
}
let Region = new RegionDetector();
Region.init();
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// A method that tries to determine if this user is in a US geography.
function isUSTimezone() {
// Timezone assumptions! We assume that if the system clock's timezone is
// between Newfoundland and Hawaii, that the user is in North America.
// This includes all of South America as well, but we have relatively few
// en-US users there, so that's OK.
// 150 minutes = 2.5 hours (UTC-2.5), which is
// Newfoundland Daylight Time (http://www.timeanddate.com/time/zones/ndt)
// 600 minutes = 10 hours (UTC-10), which is
// Hawaii-Aleutian Standard Time (http://www.timeanddate.com/time/zones/hast)
let UTCOffset = new Date().getTimezoneOffset();
return UTCOffset >= 150 && UTCOffset <= 600;
}

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

@ -1,90 +1,16 @@
"use strict";
const { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
const { Region } = ChromeUtils.import("resource://gre/modules/Region.jsm");
const { TestUtils } = ChromeUtils.import(
"resource://testing-common/TestUtils.jsm"
);
const REGION_PREF = "browser.region.network.url";
const RESPONSE_DELAY = 500;
const RESPONSE_TIMEOUT = 100;
const histogram = Services.telemetry.getHistogramById(
"SEARCH_SERVICE_COUNTRY_FETCH_RESULT"
);
Services.prefs.setBoolPref("browser.search.geoSpecificDefaults", true);
add_task(async function test_basic() {
let srv = useHttpServer();
srv.registerPathHandler("/geo", (req, res) => {
res.setStatusLine("1.1", 200, "OK");
send(res, { country_code: "UK" });
});
await Region._fetchRegion();
Assert.ok(true, "Region fetch should succeed");
Assert.equal(Region.home, "UK", "Region fetch should return correct result");
await new Promise(r => srv.stop(r));
});
add_task(async function test_invalid_url() {
histogram.clear();
Services.prefs.setCharPref(REGION_PREF, "http://localhost:0");
let result = await Region._fetchRegion();
Assert.ok(!result, "Should return no result");
checkTelemetry(Region.TELEMETRY.ERROR);
});
add_task(async function test_invalid_json() {
histogram.clear();
Services.prefs.setCharPref(
REGION_PREF,
'data:application/json,{"country_code"'
);
let result = await Region._fetchRegion();
Assert.ok(!result, "Should return no result");
checkTelemetry(Region.TELEMETRY.NO_RESULT);
});
add_task(async function test_mismatched_probe() {
let probeDetails = await getExpectedHistogramDetails();
let probeHistogram;
if (probeDetails) {
probeHistogram = Services.telemetry.getHistogramById(probeDetails.probeId);
probeHistogram.clear();
}
histogram.clear();
Services.prefs.setCharPref(
REGION_PREF,
'data:application/json,{"country_code": "AU"}'
);
await Region._fetchRegion();
Assert.equal(Region.home, "AU", "Should have correct region");
checkTelemetry(Region.TELEMETRY.SUCCESS);
// We dont store probes for linux and on treeherder +
// Mac there is no plaform countryCode so in these cases
// skip the rest of the checks.
if (!probeDetails) {
return;
}
let snapshot = probeHistogram.snapshot();
deepEqual(snapshot.values, probeDetails.expectedResult);
});
function useHttpServer() {
let server = new HttpServer();
server.start(-1);
Services.prefs.setCharPref(
REGION_PREF,
"browser.region.network.url",
`http://localhost:${server.identity.primaryPort}/geo`
);
return server;
@ -96,62 +22,44 @@ function send(res, json) {
res.write(JSON.stringify(json));
}
async function checkTelemetry(aExpectedValue) {
await TestUtils.waitForCondition(() => {
return histogram.snapshot().sum == 1;
add_task(async function test_basic() {
let srv = useHttpServer();
srv.registerPathHandler("/geo", (req, res) => {
res.setStatusLine("1.1", 200, "OK");
send(res, { country_code: "US" });
});
let snapshot = histogram.snapshot();
Assert.equal(snapshot.values[aExpectedValue], 1);
}
// Define some checks for our platform-specific telemetry.
// We can't influence what they return (as we can't
// influence the countryCode the platform thinks we
// are in), but we can check the values are
// correct given reality.
async function getExpectedHistogramDetails() {
let probeUSMismatched, probeNonUSMismatched;
switch (AppConstants.platform) {
case "macosx":
probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_OSX";
probeNonUSMismatched =
"SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_OSX";
break;
case "win":
probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_WIN";
probeNonUSMismatched =
"SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_WIN";
break;
default:
break;
let res = await Region.getHomeRegion();
Assert.ok(true, "Region fetch should succeed");
Assert.equal(
res.country_code,
"US",
"Region fetch should return correct result"
);
await new Promise(r => srv.stop(r));
});
add_task(async function test_timeout() {
let srv = useHttpServer();
srv.registerPathHandler("/geo", (req, res) => {
res.processAsync();
do_timeout(RESPONSE_DELAY, () => {
send(res, { country_code: "US" });
res.finish();
});
});
Services.prefs.setIntPref("browser.region.timeout", RESPONSE_TIMEOUT);
try {
await Region.getHomeRegion();
Assert.ok(false, "Region fetch should have timed out");
} catch (e) {
Assert.equal(e.message, "region-fetch-timeout", "Region fetch timed out");
}
if (probeUSMismatched && probeNonUSMismatched) {
let countryCode = await Services.sysinfo.countryCode;
print("Platform says the country-code is", countryCode);
if (!countryCode) {
// On treeherder for Mac the countryCode is null, so the probes won't be
// recorded.
// We still let the test run for Mac, as a developer would likely
// eventually pick up on the issue.
info("No country code set on this machine, skipping rest of test");
return false;
}
if (countryCode == "US") {
// boolean probe so 3 buckets, expect 1 result for |1|.
return {
probeId: probeUSMismatched,
expectedResult: { 0: 0, 1: 1, 2: 0 },
};
}
// We are expecting probeNonUSMismatched with false if the platform
// says AU (not a mismatch) and true otherwise.
return {
probeId: probeNonUSMismatched,
expectedResult:
countryCode == "AU" ? { 0: 1, 1: 0 } : { 0: 0, 1: 1, 2: 0 },
};
}
return false;
}
await new Promise(r => srv.stop(r));
});