browser_dbg_variables-view-large-array-buffer.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
  2. /* Any copyright is dedicated to the Public Domain.
  3. * http://creativecommons.org/publicdomain/zero/1.0/ */
  4. /**
  5. * Make sure that the variables view remains responsive when faced with
  6. * huge ammounts of data.
  7. */
  8. "use strict";
  9. const TAB_URL = EXAMPLE_URL + "doc_large-array-buffer.html";
  10. const {ELLIPSIS} = require("devtools/shared/l10n");
  11. var gTab, gPanel, gDebugger, gVariables;
  12. function test() {
  13. // this test does a lot of work on large objects, default 45s is not enough
  14. requestLongerTimeout(4);
  15. let options = {
  16. source: TAB_URL,
  17. line: 1
  18. };
  19. initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
  20. gTab = aTab;
  21. gPanel = aPanel;
  22. gDebugger = gPanel.panelWin;
  23. gVariables = gDebugger.DebuggerView.Variables;
  24. waitForCaretAndScopes(gPanel, 28, 1)
  25. .then(() => performTests())
  26. .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
  27. .then(null, error => {
  28. ok(false, "Got an error: " + error.message + "\n" + error.stack);
  29. });
  30. generateMouseClickInTab(gTab, "content.document.querySelector('button')");
  31. });
  32. }
  33. const VARS_TO_TEST = [
  34. {
  35. varName: "buffer",
  36. stringified: "ArrayBuffer",
  37. doNotExpand: true
  38. },
  39. {
  40. varName: "largeArray",
  41. stringified: "Int8Array[10000]",
  42. extraProps: [
  43. [ "buffer", "ArrayBuffer" ],
  44. [ "byteLength", "10000" ],
  45. [ "byteOffset", "0" ],
  46. [ "length", "10000" ],
  47. [ "__proto__", "Int8ArrayPrototype" ]
  48. ]
  49. },
  50. {
  51. varName: "largeObject",
  52. stringified: "Object[10000]",
  53. extraProps: [
  54. [ "__proto__", "Object" ]
  55. ]
  56. },
  57. {
  58. varName: "largeMap",
  59. stringified: "Map[10000]",
  60. hasEntries: true,
  61. extraProps: [
  62. [ "size", "10000" ],
  63. [ "__proto__", "Object" ]
  64. ]
  65. },
  66. {
  67. varName: "largeSet",
  68. stringified: "Set[10000]",
  69. hasEntries: true,
  70. extraProps: [
  71. [ "size", "10000" ],
  72. [ "__proto__", "Object" ]
  73. ]
  74. }
  75. ];
  76. const PAGE_RANGES = [
  77. [0, 2499], [2500, 4999], [5000, 7499], [7500, 9999]
  78. ];
  79. function toPageNames(ranges) {
  80. return ranges.map(([ from, to ]) => "[" + from + ELLIPSIS + to + "]");
  81. }
  82. function performTests() {
  83. let localScope = gVariables.getScopeAtIndex(0);
  84. return promise.all(VARS_TO_TEST.map(spec => {
  85. let { varName, stringified, doNotExpand } = spec;
  86. let variable = localScope.get(varName);
  87. ok(variable,
  88. `There should be a '${varName}' variable present in the scope.`);
  89. is(variable.target.querySelector(".name").getAttribute("value"), varName,
  90. `Should have the right property name for '${varName}'.`);
  91. is(variable.target.querySelector(".value").getAttribute("value"), stringified,
  92. `Should have the right property value for '${varName}'.`);
  93. ok(variable.target.querySelector(".value").className.includes("token-other"),
  94. `Should have the right token class for '${varName}'.`);
  95. is(variable.expanded, false,
  96. `The '${varName}' variable shouldn't be expanded.`);
  97. if (doNotExpand) {
  98. return promise.resolve();
  99. }
  100. return variable.expand()
  101. .then(() => verifyFirstLevel(variable, spec));
  102. }));
  103. }
  104. // In objects and arrays, the sliced pages are at the top-level of
  105. // the expanded object, but with Maps and Sets, we have to expand
  106. // <entries> first and look there.
  107. function getExpandedPages(variable, hasEntries) {
  108. let expandedPages = promise.defer();
  109. if (hasEntries) {
  110. let entries = variable.get("<entries>");
  111. ok(entries, "<entries> retrieved");
  112. entries.expand().then(() => expandedPages.resolve(entries));
  113. } else {
  114. expandedPages.resolve(variable);
  115. }
  116. return expandedPages.promise;
  117. }
  118. function verifyFirstLevel(variable, spec) {
  119. let { varName, hasEntries, extraProps } = spec;
  120. let enums = variable._enum.childNodes;
  121. let nonEnums = variable._nonenum.childNodes;
  122. is(enums.length, hasEntries ? 1 : 4,
  123. `The '${varName}' contains the right number of enumerable elements.`);
  124. is(nonEnums.length, extraProps.length,
  125. `The '${varName}' contains the right number of non-enumerable elements.`);
  126. // the sliced pages begin after <entries> row
  127. let pagesOffset = hasEntries ? 1 : 0;
  128. let expandedPages = getExpandedPages(variable, hasEntries);
  129. return expandedPages.then((pagesList) => {
  130. toPageNames(PAGE_RANGES).forEach((pageName, i) => {
  131. let index = i + pagesOffset;
  132. is(pagesList.target.querySelectorAll(".variables-view-property .name")[index].getAttribute("value"),
  133. pageName, `The page #${i + 1} in the '${varName}' is named correctly.`);
  134. is(pagesList.target.querySelectorAll(".variables-view-property .value")[index].getAttribute("value"),
  135. "", `The page #${i + 1} in the '${varName}' should not have a corresponding value.`);
  136. });
  137. }).then(() => {
  138. extraProps.forEach(([ propName, propValue ], i) => {
  139. // the extra props start after the 4 pages
  140. let index = i + pagesOffset + 4;
  141. is(variable.target.querySelectorAll(".variables-view-property .name")[index].getAttribute("value"),
  142. propName, `The other properties in '${varName}' are named correctly.`);
  143. is(variable.target.querySelectorAll(".variables-view-property .value")[index].getAttribute("value"),
  144. propValue, `The other properties in '${varName}' have the correct value.`);
  145. });
  146. }).then(() => verifyNextLevels(variable, spec));
  147. }
  148. function verifyNextLevels(variable, spec) {
  149. let { varName, hasEntries } = spec;
  150. // the entries are already expanded in verifyFirstLevel
  151. let pagesList = hasEntries ? variable.get("<entries>") : variable;
  152. let lastPage = pagesList.get(toPageNames(PAGE_RANGES)[3]);
  153. ok(lastPage, `The last page in the 1st level of '${varName}' was retrieved successfully.`);
  154. return lastPage.expand()
  155. .then(() => verifyNextLevels2(lastPage, varName));
  156. }
  157. function verifyNextLevels2(lastPage1, varName) {
  158. const PAGE_RANGES_IN_LAST_PAGE = [
  159. [7500, 8124], [8125, 8749], [8750, 9374], [9375, 9999]
  160. ];
  161. let pageEnums1 = lastPage1._enum.childNodes;
  162. let pageNonEnums1 = lastPage1._nonenum.childNodes;
  163. is(pageEnums1.length, 4,
  164. `The last page in the 1st level of '${varName}' should contain all the created enumerable elements.`);
  165. is(pageNonEnums1.length, 0,
  166. `The last page in the 1st level of '${varName}' should not contain any non-enumerable elements.`);
  167. let pageNames = toPageNames(PAGE_RANGES_IN_LAST_PAGE);
  168. pageNames.forEach((pageName, i) => {
  169. is(lastPage1._enum.querySelectorAll(".variables-view-property .name")[i].getAttribute("value"),
  170. pageName, `The page #${i + 1} in the 2nd level of '${varName}' is named correctly.`);
  171. });
  172. let lastPage2 = lastPage1.get(pageNames[3]);
  173. ok(lastPage2, "The last page in the 2nd level was retrieved successfully.");
  174. return lastPage2.expand()
  175. .then(() => verifyNextLevels3(lastPage2, varName));
  176. }
  177. function verifyNextLevels3(lastPage2, varName) {
  178. let pageEnums2 = lastPage2._enum.childNodes;
  179. let pageNonEnums2 = lastPage2._nonenum.childNodes;
  180. is(pageEnums2.length, 625,
  181. `The last page in the 3rd level of '${varName}' should contain all the created enumerable elements.`);
  182. is(pageNonEnums2.length, 0,
  183. `The last page in the 3rd level of '${varName}' shouldn't contain any non-enumerable elements.`);
  184. const LEAF_ITEMS = [
  185. [0, 9375, 624],
  186. [1, 9376, 623],
  187. [623, 9998, 1],
  188. [624, 9999, 0]
  189. ];
  190. function expectedValue(name, value) {
  191. switch (varName) {
  192. case "largeArray": return 0;
  193. case "largeObject": return value;
  194. case "largeMap": return name + " \u2192 " + value;
  195. case "largeSet": return value;
  196. }
  197. }
  198. LEAF_ITEMS.forEach(([index, name, value]) => {
  199. is(lastPage2._enum.querySelectorAll(".variables-view-property .name")[index].getAttribute("value"),
  200. name, `The properties in the leaf level of '${varName}' are named correctly.`);
  201. is(lastPage2._enum.querySelectorAll(".variables-view-property .value")[index].getAttribute("value"),
  202. expectedValue(name, value), `The properties in the leaf level of '${varName}' have the correct value.`);
  203. });
  204. }
  205. registerCleanupFunction(function () {
  206. gTab = null;
  207. gPanel = null;
  208. gDebugger = null;
  209. gVariables = null;
  210. });