/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; /* exported assertDNRStoreData, getDNRRule, getSchemaNormalizedRule, getSchemaNormalizedRules */ ChromeUtils.defineESModuleGetters(this, { Schemas: "resource://gre/modules/Schemas.sys.mjs", }); function getDNRRule({ id = 1, priority = 1, action = {}, condition = {}, } = {}) { return { id, priority, action: { type: "block", ...action, }, condition: { ...condition, }, }; } const getSchemaNormalizedRule = (extensionTestWrapper, value) => { const { extension } = extensionTestWrapper; const validationContext = { url: extension.baseURI.spec, principal: extension.principal, logError: err => { // We don't expect this test helper function to be called on invalid rules, // and so we trigger an explicit test failure if we ever hit any. Assert.ok( false, `Unexpected logError on normalizing DNR rule ${JSON.stringify( value )} - ${err}` ); }, preprocessors: {}, manifestVersion: extension.manifestVersion, }; return Schemas.normalize( value, "declarativeNetRequest.Rule", validationContext ); }; const getSchemaNormalizedRules = (extensionTestWrapper, rules) => { return rules.map(rule => { const normalized = getSchemaNormalizedRule(extensionTestWrapper, rule); if (normalized.error) { throw new Error( `Unexpected DNR Rule normalization error: ${normalized.error}` ); } return normalized.value; }); }; const assertDNRStoreData = async ( dnrStore, extensionTestWrapper, expectedRulesets, { assertIndividualRules = true } = {} ) => { const extUUID = extensionTestWrapper.uuid; const rule_resources = extensionTestWrapper.extension.manifest.declarative_net_request ?.rule_resources; const expectedRulesetIds = Array.from(Object.keys(expectedRulesets)); const expectedRulesetIndexesMap = expectedRulesetIds.reduce((acc, rsId) => { acc.set( rsId, rule_resources.findIndex(rr => rr.id === rsId) ); return acc; }, new Map()); ok( dnrStore._dataPromises.has(extUUID), "Got promise for the test extension DNR data being loaded" ); await dnrStore._dataPromises.get(extUUID); ok(dnrStore._data.has(extUUID), "Got data for the test extension"); const dnrExtData = dnrStore._data.get(extUUID); Assert.deepEqual( { schemaVersion: dnrExtData.schemaVersion, extVersion: dnrExtData.extVersion, }, { schemaVersion: dnrExtData.constructor.VERSION, extVersion: extensionTestWrapper.extension.version, }, "Got the expected data schema version and extension version in the store data" ); Assert.deepEqual( Array.from(dnrExtData.staticRulesets.keys()), expectedRulesetIds, "Got the enabled rulesets in the stored data staticRulesets Map" ); for (const rulesetId of expectedRulesetIds) { const expectedRulesetIdx = expectedRulesetIndexesMap.get(rulesetId); const expectedRulesetRules = getSchemaNormalizedRules( extensionTestWrapper, expectedRulesets[rulesetId] ); const actualData = dnrExtData.staticRulesets.get(rulesetId); equal( actualData.idx, expectedRulesetIdx, `Got the expected ruleset index for ruleset id ${rulesetId}` ); // Asserting an entire array of rules all at once will produce // a big enough output to don't be immediately useful to investigate // failures, asserting each rule individually would produce more // readable assertion failure logs. const assertRuleAtIdx = ruleIdx => { const actualRule = actualData.rules[ruleIdx]; const expectedRule = expectedRulesetRules[ruleIdx]; Assert.deepEqual( actualRule, expectedRule, `Got the expected rule at index ${ruleIdx} for ruleset id "${rulesetId}"` ); Assert.equal( actualRule.constructor.name, "Rule", `Expect rule at index ${ruleIdx} to be an instance of the Rule class` ); if (expectedRule.condition.regexFilter) { const compiledRegexFilter = actualData.rules[ruleIdx].condition.getCompiledRegexFilter(); Assert.equal( compiledRegexFilter?.constructor.name, "RegExp", `Expect rule ${ruleIdx} condition.getCompiledRegexFilter() to return a compiled regexp filter` ); Assert.equal( compiledRegexFilter?.source, new RegExp(expectedRule.condition.regexFilter).source, `Expect rule ${ruleIdx} condition's compiled RegExp source to match the regexFilter string` ); Assert.equal( compiledRegexFilter?.ignoreCase, !expectedRule.condition.isUrlFilterCaseSensitive, `Expect rule ${ruleIdx} conditions's compiled RegExp ignoreCase to be set based on condition.isUrlFilterCaseSensitive` ); } }; // Some tests may be using a big enough number of rules that // the assertiongs would be producing a huge amount of log spam, // and so for those tests we only explicitly assert the first // and last rule and that the total amount of rules matches the // expected number of rules (there are still other tests explicitly // asserting all loaded rules). if (assertIndividualRules) { info(`Verify each individual rule loaded for ruleset id "${rulesetId}"`); for (let ruleIdx = 0; ruleIdx < expectedRulesetRules.length; ruleIdx++) { assertRuleAtIdx(ruleIdx); } } else if (expectedRulesetRules.length) { // NOTE: Only asserting the first and last rule also helps to speed up // the test is some slower builds when the number of expected rules is // big enough (e.g. the test task verifying the enforced rule count limits // was timing out in tsan build because asserting all indidual rules was // taking long enough and the event page was being suspended on the idle // timeout by the time we did run all these assertion and proceeding with // the rest of the test task assertions), we still confirm that the total // number of expected vs actual rules also matches right after these // assertions. info( `Verify the first and last rules loaded for ruleset id "${rulesetId}"` ); const lastExpectedRuleIdx = expectedRulesetRules.length - 1; for (const ruleIdx of [0, lastExpectedRuleIdx]) { assertRuleAtIdx(ruleIdx); } } equal( actualData.rules.length, expectedRulesetRules.length, `Got the expected number of rules loaded for ruleset id "${rulesetId}"` ); } };