summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/test/mochitest/browser_dbg_variables-view-large-array-buffer.js
diff options
context:
space:
mode:
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.js253
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;
+});