Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Tests `SuggestionsMap`.
"use strict";
ChromeUtils.defineESModuleGetters(this, {
ObjectUtils: "resource://gre/modules/ObjectUtils.sys.mjs",
SuggestionsMap: "resource:///modules/urlbar/private/SuggestBackendJs.sys.mjs",
});
// This overrides `SuggestionsMap.chunkSize`. Testing the actual value can make
// the test run too long. This is OK because the correctness of the chunking
// behavior doesn't depend on the chunk size.
const TEST_CHUNK_SIZE = 100;
add_setup(async () => {
// Sanity check the actual `chunkSize` value.
Assert.equal(
typeof SuggestionsMap.chunkSize,
"number",
"Sanity check: chunkSize is a number"
);
Assert.greater(SuggestionsMap.chunkSize, 0, "Sanity check: chunkSize > 0");
// Set our test value.
SuggestionsMap.chunkSize = TEST_CHUNK_SIZE;
});
// Tests many suggestions with one keyword each.
add_task(async function chunking_singleKeyword() {
let suggestionCounts = [
1 * SuggestionsMap.chunkSize - 1,
1 * SuggestionsMap.chunkSize,
1 * SuggestionsMap.chunkSize + 1,
2 * SuggestionsMap.chunkSize - 1,
2 * SuggestionsMap.chunkSize,
2 * SuggestionsMap.chunkSize + 1,
3 * SuggestionsMap.chunkSize - 1,
3 * SuggestionsMap.chunkSize,
3 * SuggestionsMap.chunkSize + 1,
];
for (let count of suggestionCounts) {
await doChunkingTest(count, 1);
}
});
// Tests a small number of suggestions with many keywords each.
add_task(async function chunking_manyKeywords() {
let keywordCounts = [
1 * SuggestionsMap.chunkSize - 1,
1 * SuggestionsMap.chunkSize,
1 * SuggestionsMap.chunkSize + 1,
2 * SuggestionsMap.chunkSize - 1,
2 * SuggestionsMap.chunkSize,
2 * SuggestionsMap.chunkSize + 1,
3 * SuggestionsMap.chunkSize - 1,
3 * SuggestionsMap.chunkSize,
3 * SuggestionsMap.chunkSize + 1,
];
for (let suggestionCount = 1; suggestionCount <= 3; suggestionCount++) {
for (let keywordCount of keywordCounts) {
await doChunkingTest(suggestionCount, keywordCount);
}
}
});
async function doChunkingTest(suggestionCount, keywordCountPerSuggestion) {
info(
"Running chunking test: " +
JSON.stringify({ suggestionCount, keywordCountPerSuggestion })
);
// Create `suggestionCount` suggestions, each with `keywordCountPerSuggestion`
// keywords.
let suggestions = [];
for (let i = 0; i < suggestionCount; i++) {
let keywords = [];
for (let k = 0; k < keywordCountPerSuggestion; k++) {
keywords.push(`keyword-${i}-${k}`);
}
suggestions.push({
keywords,
id: i,
url: "http://example.com/" + i,
title: "Suggestion " + i,
click_url: "http://example.com/click",
impression_url: "http://example.com/impression",
advertiser: "TestAdvertiser",
iab_category: "22 - Shopping",
});
}
// Add the suggestions.
let map = new SuggestionsMap();
await map.add(suggestions);
// Make sure all keyword-suggestion pairs have been added.
for (let i = 0; i < suggestionCount; i++) {
for (let k = 0; k < keywordCountPerSuggestion; k++) {
let keyword = `keyword-${i}-${k}`;
// Check the map. Logging all assertions takes a ton of time and makes the
// test run much longer than it otherwise would, especially if `chunkSize`
// is large, so only log failing assertions.
let actualSuggestions = map.get(keyword);
if (!ObjectUtils.deepEqual(actualSuggestions, [suggestions[i]])) {
Assert.deepEqual(
actualSuggestions,
[suggestions[i]],
`Suggestion ${i} is present for keyword ${keyword}`
);
}
}
}
}
add_task(async function duplicateKeywords() {
let suggestions = [
{
title: "suggestion 0",
keywords: ["a", "a", "a", "b", "b", "c"],
},
{
title: "suggestion 1",
keywords: ["b", "c", "d"],
},
{
title: "suggestion 2",
keywords: ["c", "d", "e"],
},
{
title: "suggestion 3",
keywords: ["f", "f"],
},
];
let expectedIndexesByKeyword = {
a: [0],
b: [0, 1],
c: [0, 1, 2],
d: [1, 2],
e: [2],
f: [3],
};
let map = new SuggestionsMap();
await map.add(suggestions);
for (let [keyword, indexes] of Object.entries(expectedIndexesByKeyword)) {
Assert.deepEqual(
map.get(keyword),
indexes.map(i => suggestions[i]),
"get() with keyword: " + keyword
);
}
});
add_task(async function mapKeywords() {
let suggestions = [
{
title: "suggestion 0",
keywords: ["a", "a", "a", "b", "b", "c"],
},
{
title: "suggestion 1",
keywords: ["b", "c", "d"],
},
{
title: "suggestion 2",
keywords: ["c", "d", "e"],
},
{
title: "suggestion 3",
keywords: ["f", "f"],
},
];
let expectedIndexesByKeyword = {
a: [],
b: [],
c: [],
d: [],
e: [],
f: [],
ax: [0],
bx: [0, 1],
cx: [0, 1, 2],
dx: [1, 2],
ex: [2],
fx: [3],
fy: [3],
fz: [3],
};
let map = new SuggestionsMap();
await map.add(suggestions, {
mapKeyword: keyword => {
if (keyword == "f") {
return [keyword + "x", keyword + "y", keyword + "z"];
}
return [keyword + "x"];
},
});
for (let [keyword, indexes] of Object.entries(expectedIndexesByKeyword)) {
Assert.deepEqual(
map.get(keyword),
indexes.map(i => suggestions[i]),
"get() with keyword: " + keyword
);
}
});
// Tests `keywordsProperty`.
add_task(async function keywordsProperty() {
let suggestion = {
title: "suggestion",
keywords: ["should be ignored"],
foo: ["hello"],
};
let map = new SuggestionsMap();
await map.add([suggestion], {
keywordsProperty: "foo",
});
Assert.deepEqual(
map.get("hello"),
[suggestion],
"Keyword in `foo` should match"
);
Assert.deepEqual(
map.get("should be ignored"),
[],
"Keyword in `keywords` should not match"
);
});
// Tests `MAP_KEYWORD_PREFIXES_STARTING_AT_FIRST_WORD`.
add_task(async function prefixesStartingAtFirstWord() {
let suggestion = {
title: "suggestion",
keywords: ["one two three", "four five six"],
};
// keyword passed to `get()` -> should match
let tests = {
o: false,
on: false,
one: true,
"one ": true,
"one t": true,
"one tw": true,
"one two": true,
"one two ": true,
"one two t": true,
"one two th": true,
"one two thr": true,
"one two thre": true,
"one two three": true,
"one two three ": false,
f: false,
fo: false,
fou: false,
four: true,
"four ": true,
"four f": true,
"four fi": true,
"four fiv": true,
"four five": true,
"four five ": true,
"four five s": true,
"four five si": true,
"four five six": true,
"four five six ": false,
};
let map = new SuggestionsMap();
await map.add([suggestion], {
mapKeyword: SuggestionsMap.MAP_KEYWORD_PREFIXES_STARTING_AT_FIRST_WORD,
});
for (let [keyword, shouldMatch] of Object.entries(tests)) {
Assert.deepEqual(
map.get(keyword),
shouldMatch ? [suggestion] : [],
"get() with keyword: " + keyword
);
}
});