diff options
Diffstat (limited to 'devtools/client/debugger/test/mochitest/browser_dbg_variables-view-large-array-buffer.js')
-rw-r--r-- | devtools/client/debugger/test/mochitest/browser_dbg_variables-view-large-array-buffer.js | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_variables-view-large-array-buffer.js b/devtools/client/debugger/test/mochitest/browser_dbg_variables-view-large-array-buffer.js new file mode 100644 index 000000000..7ec1fe2f0 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg_variables-view-large-array-buffer.js @@ -0,0 +1,253 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Make sure that the variables view remains responsive when faced with + * huge ammounts of data. + */ + +"use strict"; + +const TAB_URL = EXAMPLE_URL + "doc_large-array-buffer.html"; +const {ELLIPSIS} = require("devtools/shared/l10n"); + + +var gTab, gPanel, gDebugger, gVariables; + +function test() { + // this test does a lot of work on large objects, default 45s is not enough + requestLongerTimeout(4); + + let options = { + source: TAB_URL, + line: 1 + }; + initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => { + gTab = aTab; + gPanel = aPanel; + gDebugger = gPanel.panelWin; + gVariables = gDebugger.DebuggerView.Variables; + + waitForCaretAndScopes(gPanel, 28, 1) + .then(() => performTests()) + .then(() => resumeDebuggerThenCloseAndFinish(gPanel)) + .then(null, error => { + ok(false, "Got an error: " + error.message + "\n" + error.stack); + }); + + generateMouseClickInTab(gTab, "content.document.querySelector('button')"); + }); +} + +const VARS_TO_TEST = [ + { + varName: "buffer", + stringified: "ArrayBuffer", + doNotExpand: true + }, + { + varName: "largeArray", + stringified: "Int8Array[10000]", + extraProps: [ + [ "buffer", "ArrayBuffer" ], + [ "byteLength", "10000" ], + [ "byteOffset", "0" ], + [ "length", "10000" ], + [ "__proto__", "Int8ArrayPrototype" ] + ] + }, + { + varName: "largeObject", + stringified: "Object[10000]", + extraProps: [ + [ "__proto__", "Object" ] + ] + }, + { + varName: "largeMap", + stringified: "Map[10000]", + hasEntries: true, + extraProps: [ + [ "size", "10000" ], + [ "__proto__", "Object" ] + ] + }, + { + varName: "largeSet", + stringified: "Set[10000]", + hasEntries: true, + extraProps: [ + [ "size", "10000" ], + [ "__proto__", "Object" ] + ] + } +]; + +const PAGE_RANGES = [ + [0, 2499], [2500, 4999], [5000, 7499], [7500, 9999] +]; + +function toPageNames(ranges) { + return ranges.map(([ from, to ]) => "[" + from + ELLIPSIS + to + "]"); +} + +function performTests() { + let localScope = gVariables.getScopeAtIndex(0); + + return promise.all(VARS_TO_TEST.map(spec => { + let { varName, stringified, doNotExpand } = spec; + + let variable = localScope.get(varName); + ok(variable, + `There should be a '${varName}' variable present in the scope.`); + + is(variable.target.querySelector(".name").getAttribute("value"), varName, + `Should have the right property name for '${varName}'.`); + is(variable.target.querySelector(".value").getAttribute("value"), stringified, + `Should have the right property value for '${varName}'.`); + ok(variable.target.querySelector(".value").className.includes("token-other"), + `Should have the right token class for '${varName}'.`); + + is(variable.expanded, false, + `The '${varName}' variable shouldn't be expanded.`); + + if (doNotExpand) { + return promise.resolve(); + } + + return variable.expand() + .then(() => verifyFirstLevel(variable, spec)); + })); +} + +// In objects and arrays, the sliced pages are at the top-level of +// the expanded object, but with Maps and Sets, we have to expand +// <entries> first and look there. +function getExpandedPages(variable, hasEntries) { + let expandedPages = promise.defer(); + if (hasEntries) { + let entries = variable.get("<entries>"); + ok(entries, "<entries> retrieved"); + entries.expand().then(() => expandedPages.resolve(entries)); + } else { + expandedPages.resolve(variable); + } + + return expandedPages.promise; +} + +function verifyFirstLevel(variable, spec) { + let { varName, hasEntries, extraProps } = spec; + + let enums = variable._enum.childNodes; + let nonEnums = variable._nonenum.childNodes; + + is(enums.length, hasEntries ? 1 : 4, + `The '${varName}' contains the right number of enumerable elements.`); + is(nonEnums.length, extraProps.length, + `The '${varName}' contains the right number of non-enumerable elements.`); + + // the sliced pages begin after <entries> row + let pagesOffset = hasEntries ? 1 : 0; + let expandedPages = getExpandedPages(variable, hasEntries); + + return expandedPages.then((pagesList) => { + toPageNames(PAGE_RANGES).forEach((pageName, i) => { + let index = i + pagesOffset; + + is(pagesList.target.querySelectorAll(".variables-view-property .name")[index].getAttribute("value"), + pageName, `The page #${i + 1} in the '${varName}' is named correctly.`); + is(pagesList.target.querySelectorAll(".variables-view-property .value")[index].getAttribute("value"), + "", `The page #${i + 1} in the '${varName}' should not have a corresponding value.`); + }); + }).then(() => { + extraProps.forEach(([ propName, propValue ], i) => { + // the extra props start after the 4 pages + let index = i + pagesOffset + 4; + + is(variable.target.querySelectorAll(".variables-view-property .name")[index].getAttribute("value"), + propName, `The other properties in '${varName}' are named correctly.`); + is(variable.target.querySelectorAll(".variables-view-property .value")[index].getAttribute("value"), + propValue, `The other properties in '${varName}' have the correct value.`); + }); + }).then(() => verifyNextLevels(variable, spec)); +} + +function verifyNextLevels(variable, spec) { + let { varName, hasEntries } = spec; + + // the entries are already expanded in verifyFirstLevel + let pagesList = hasEntries ? variable.get("<entries>") : variable; + + let lastPage = pagesList.get(toPageNames(PAGE_RANGES)[3]); + ok(lastPage, `The last page in the 1st level of '${varName}' was retrieved successfully.`); + + return lastPage.expand() + .then(() => verifyNextLevels2(lastPage, varName)); +} + +function verifyNextLevels2(lastPage1, varName) { + const PAGE_RANGES_IN_LAST_PAGE = [ + [7500, 8124], [8125, 8749], [8750, 9374], [9375, 9999] + ]; + + let pageEnums1 = lastPage1._enum.childNodes; + let pageNonEnums1 = lastPage1._nonenum.childNodes; + is(pageEnums1.length, 4, + `The last page in the 1st level of '${varName}' should contain all the created enumerable elements.`); + is(pageNonEnums1.length, 0, + `The last page in the 1st level of '${varName}' should not contain any non-enumerable elements.`); + + let pageNames = toPageNames(PAGE_RANGES_IN_LAST_PAGE); + pageNames.forEach((pageName, i) => { + is(lastPage1._enum.querySelectorAll(".variables-view-property .name")[i].getAttribute("value"), + pageName, `The page #${i + 1} in the 2nd level of '${varName}' is named correctly.`); + }); + + let lastPage2 = lastPage1.get(pageNames[3]); + ok(lastPage2, "The last page in the 2nd level was retrieved successfully."); + + return lastPage2.expand() + .then(() => verifyNextLevels3(lastPage2, varName)); +} + +function verifyNextLevels3(lastPage2, varName) { + let pageEnums2 = lastPage2._enum.childNodes; + let pageNonEnums2 = lastPage2._nonenum.childNodes; + is(pageEnums2.length, 625, + `The last page in the 3rd level of '${varName}' should contain all the created enumerable elements.`); + is(pageNonEnums2.length, 0, + `The last page in the 3rd level of '${varName}' shouldn't contain any non-enumerable elements.`); + + const LEAF_ITEMS = [ + [0, 9375, 624], + [1, 9376, 623], + [623, 9998, 1], + [624, 9999, 0] + ]; + + function expectedValue(name, value) { + switch (varName) { + case "largeArray": return 0; + case "largeObject": return value; + case "largeMap": return name + " \u2192 " + value; + case "largeSet": return value; + } + } + + LEAF_ITEMS.forEach(([index, name, value]) => { + is(lastPage2._enum.querySelectorAll(".variables-view-property .name")[index].getAttribute("value"), + name, `The properties in the leaf level of '${varName}' are named correctly.`); + is(lastPage2._enum.querySelectorAll(".variables-view-property .value")[index].getAttribute("value"), + expectedValue(name, value), `The properties in the leaf level of '${varName}' have the correct value.`); + }); +} + +registerCleanupFunction(function () { + gTab = null; + gPanel = null; + gDebugger = null; + gVariables = null; +}); |