diff options
Diffstat (limited to 'devtools/client/canvasdebugger/test')
54 files changed, 3527 insertions, 0 deletions
diff --git a/devtools/client/canvasdebugger/test/.eslintrc.js b/devtools/client/canvasdebugger/test/.eslintrc.js new file mode 100644 index 000000000..8d15a76d9 --- /dev/null +++ b/devtools/client/canvasdebugger/test/.eslintrc.js @@ -0,0 +1,6 @@ +"use strict"; + +module.exports = { + // Extend from the shared list of defined globals for mochitests. + "extends": "../../../.eslintrc.mochitests.js" +}; diff --git a/devtools/client/canvasdebugger/test/browser.ini b/devtools/client/canvasdebugger/test/browser.ini new file mode 100644 index 000000000..65c81c32f --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser.ini @@ -0,0 +1,61 @@ +[DEFAULT] +tags = devtools +subsuite = devtools +support-files = + doc_raf-begin.html + doc_settimeout.html + doc_no-canvas.html + doc_raf-no-canvas.html + doc_simple-canvas.html + doc_simple-canvas-bitmasks.html + doc_simple-canvas-deep-stack.html + doc_simple-canvas-transparent.html + doc_webgl-bindings.html + doc_webgl-enum.html + doc_webgl-drawArrays.html + doc_webgl-drawElements.html + head.js + +[browser_canvas-actor-test-01.js] +[browser_canvas-actor-test-02.js] +[browser_canvas-actor-test-03.js] +[browser_canvas-actor-test-04.js] +[browser_canvas-actor-test-05.js] +[browser_canvas-actor-test-06.js] +[browser_canvas-actor-test-07.js] +[browser_canvas-actor-test-08.js] +[browser_canvas-actor-test-09.js] +subsuite = gpu +[browser_canvas-actor-test-10.js] +subsuite = gpu +[browser_canvas-actor-test-11.js] +subsuite = gpu +[browser_canvas-actor-test-12.js] +[browser_canvas-frontend-call-highlight.js] +[browser_canvas-frontend-call-list.js] +[browser_canvas-frontend-call-search.js] +[browser_canvas-frontend-call-stack-01.js] +[browser_canvas-frontend-call-stack-02.js] +[browser_canvas-frontend-call-stack-03.js] +[browser_canvas-frontend-clear.js] +[browser_canvas-frontend-img-screenshots.js] +[browser_canvas-frontend-img-thumbnails-01.js] +[browser_canvas-frontend-img-thumbnails-02.js] +[browser_canvas-frontend-open.js] +[browser_canvas-frontend-record-01.js] +[browser_canvas-frontend-record-02.js] +[browser_canvas-frontend-record-03.js] +[browser_canvas-frontend-record-04.js] +[browser_canvas-frontend-reload-01.js] +[browser_canvas-frontend-reload-02.js] +[browser_canvas-frontend-slider-01.js] +[browser_canvas-frontend-slider-02.js] +[browser_canvas-frontend-snapshot-select-01.js] +[browser_canvas-frontend-snapshot-select-02.js] +[browser_canvas-frontend-stepping.js] +[browser_canvas-frontend-stop-01.js] +[browser_canvas-frontend-stop-02.js] +[browser_canvas-frontend-stop-03.js] +[browser_profiling-canvas.js] +[browser_profiling-webgl.js] +subsuite = gpu diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-01.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-01.js new file mode 100644 index 000000000..9b6ee4e4f --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-01.js @@ -0,0 +1,17 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the canvas debugger leaks on initialization and sudden destruction. + * You can also use this initialization format as a template for other tests. + */ + +function* ifTestingSupported() { + let { target, front } = yield initCallWatcherBackend(SIMPLE_CANVAS_URL); + + ok(target, "Should have a target available."); + ok(front, "Should have a protocol front available."); + + yield removeTab(target.tab); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-02.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-02.js new file mode 100644 index 000000000..eb8a8f5f7 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-02.js @@ -0,0 +1,78 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if functions calls are recorded and stored for a canvas context, + * and that their stack is successfully retrieved. + */ + +function* ifTestingSupported() { + let { target, front } = yield initCallWatcherBackend(SIMPLE_CANVAS_URL); + + let navigated = once(target, "navigate"); + + yield front.setup({ + tracedGlobals: ["CanvasRenderingContext2D", "WebGLRenderingContext"], + startRecording: true, + performReload: true, + storeCalls: true + }); + ok(true, "The front was setup up successfully."); + + yield navigated; + ok(true, "Target automatically navigated when the front was set up."); + + // Allow the content to execute some functions. + yield waitForTick(); + + let functionCalls = yield front.pauseRecording(); + ok(functionCalls, + "An array of function call actors was sent after reloading."); + ok(functionCalls.length > 0, + "There's at least one function call actor available."); + + is(functionCalls[0].type, CallWatcherFront.METHOD_FUNCTION, + "The called function is correctly identified as a method."); + is(functionCalls[0].name, "clearRect", + "The called function's name is correct."); + is(functionCalls[0].file, SIMPLE_CANVAS_URL, + "The called function's file is correct."); + is(functionCalls[0].line, 25, + "The called function's line is correct."); + + is(functionCalls[0].callerPreview, "Object", + "The called function's caller preview is correct."); + is(functionCalls[0].argsPreview, "0, 0, 128, 128", + "The called function's args preview is correct."); + + let details = yield functionCalls[1].getDetails(); + ok(details, + "The first called function has some details available."); + + is(details.stack.length, 3, + "The called function's stack depth is correct."); + + is(details.stack[0].name, "fillStyle", + "The called function's stack is correct (1.1)."); + is(details.stack[0].file, SIMPLE_CANVAS_URL, + "The called function's stack is correct (1.2)."); + is(details.stack[0].line, 20, + "The called function's stack is correct (1.3)."); + + is(details.stack[1].name, "drawRect", + "The called function's stack is correct (2.1)."); + is(details.stack[1].file, SIMPLE_CANVAS_URL, + "The called function's stack is correct (2.2)."); + is(details.stack[1].line, 26, + "The called function's stack is correct (2.3)."); + + is(details.stack[2].name, "drawScene", + "The called function's stack is correct (3.1)."); + is(details.stack[2].file, SIMPLE_CANVAS_URL, + "The called function's stack is correct (3.2)."); + is(details.stack[2].line, 33, + "The called function's stack is correct (3.3)."); + + yield removeTab(target.tab); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-03.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-03.js new file mode 100644 index 000000000..8a8a63780 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-03.js @@ -0,0 +1,75 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if functions inside a single animation frame are recorded and stored + * for a canvas context. + */ + +function* ifTestingSupported() { + let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL); + + let navigated = once(target, "navigate"); + + yield front.setup({ reload: true }); + ok(true, "The front was setup up successfully."); + + yield navigated; + ok(true, "Target automatically navigated when the front was set up."); + + let snapshotActor = yield front.recordAnimationFrame(); + ok(snapshotActor, + "A snapshot actor was sent after recording."); + + let animationOverview = yield snapshotActor.getOverview(); + ok(snapshotActor, + "An animation overview could be retrieved after recording."); + + let functionCalls = animationOverview.calls; + ok(functionCalls, + "An array of function call actors was sent after recording."); + is(functionCalls.length, 8, + "The number of function call actors is correct."); + + is(functionCalls[0].type, CallWatcherFront.METHOD_FUNCTION, + "The first called function is correctly identified as a method."); + is(functionCalls[0].name, "clearRect", + "The first called function's name is correct."); + is(functionCalls[0].file, SIMPLE_CANVAS_URL, + "The first called function's file is correct."); + is(functionCalls[0].line, 25, + "The first called function's line is correct."); + is(functionCalls[0].argsPreview, "0, 0, 128, 128", + "The first called function's args preview is correct."); + is(functionCalls[0].callerPreview, "Object", + "The first called function's caller preview is correct."); + + is(functionCalls[6].type, CallWatcherFront.METHOD_FUNCTION, + "The penultimate called function is correctly identified as a method."); + is(functionCalls[6].name, "fillRect", + "The penultimate called function's name is correct."); + is(functionCalls[6].file, SIMPLE_CANVAS_URL, + "The penultimate called function's file is correct."); + is(functionCalls[6].line, 21, + "The penultimate called function's line is correct."); + is(functionCalls[6].argsPreview, "10, 10, 55, 50", + "The penultimate called function's args preview is correct."); + is(functionCalls[6].callerPreview, "Object", + "The penultimate called function's caller preview is correct."); + + is(functionCalls[7].type, CallWatcherFront.METHOD_FUNCTION, + "The last called function is correctly identified as a method."); + is(functionCalls[7].name, "requestAnimationFrame", + "The last called function's name is correct."); + is(functionCalls[7].file, SIMPLE_CANVAS_URL, + "The last called function's file is correct."); + is(functionCalls[7].line, 30, + "The last called function's line is correct."); + ok(functionCalls[7].argsPreview.includes("Function"), + "The last called function's args preview is correct."); + is(functionCalls[7].callerPreview, "Object", + "The last called function's caller preview is correct."); + + yield removeTab(target.tab); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-04.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-04.js new file mode 100644 index 000000000..d3c7d7661 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-04.js @@ -0,0 +1,85 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if draw calls inside a single animation frame generate and retrieve + * the correct thumbnails. + */ + +function* ifTestingSupported() { + let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL); + + let navigated = once(target, "navigate"); + + yield front.setup({ reload: true }); + ok(true, "The front was setup up successfully."); + + yield navigated; + ok(true, "Target automatically navigated when the front was set up."); + + let snapshotActor = yield front.recordAnimationFrame(); + ok(snapshotActor, + "A snapshot actor was sent after recording."); + + let animationOverview = yield snapshotActor.getOverview(); + ok(animationOverview, + "An animation overview could be retrieved after recording."); + + let thumbnails = animationOverview.thumbnails; + ok(thumbnails, + "An array of thumbnails was sent after recording."); + is(thumbnails.length, 4, + "The number of thumbnails is correct."); + + is(thumbnails[0].index, 0, + "The first thumbnail's index is correct."); + is(thumbnails[0].width, 50, + "The first thumbnail's width is correct."); + is(thumbnails[0].height, 50, + "The first thumbnail's height is correct."); + is(thumbnails[0].flipped, false, + "The first thumbnail's flipped flag is correct."); + is([].find.call(Uint32(thumbnails[0].pixels), e => e > 0), undefined, + "The first thumbnail's pixels seem to be completely transparent."); + + is(thumbnails[1].index, 2, + "The second thumbnail's index is correct."); + is(thumbnails[1].width, 50, + "The second thumbnail's width is correct."); + is(thumbnails[1].height, 50, + "The second thumbnail's height is correct."); + is(thumbnails[1].flipped, false, + "The second thumbnail's flipped flag is correct."); + is([].find.call(Uint32(thumbnails[1].pixels), e => e > 0), 4290822336, + "The second thumbnail's pixels seem to not be completely transparent."); + + is(thumbnails[2].index, 4, + "The third thumbnail's index is correct."); + is(thumbnails[2].width, 50, + "The third thumbnail's width is correct."); + is(thumbnails[2].height, 50, + "The third thumbnail's height is correct."); + is(thumbnails[2].flipped, false, + "The third thumbnail's flipped flag is correct."); + is([].find.call(Uint32(thumbnails[2].pixels), e => e > 0), 4290822336, + "The third thumbnail's pixels seem to not be completely transparent."); + + is(thumbnails[3].index, 6, + "The fourth thumbnail's index is correct."); + is(thumbnails[3].width, 50, + "The fourth thumbnail's width is correct."); + is(thumbnails[3].height, 50, + "The fourth thumbnail's height is correct."); + is(thumbnails[3].flipped, false, + "The fourth thumbnail's flipped flag is correct."); + is([].find.call(Uint32(thumbnails[3].pixels), e => e > 0), 4290822336, + "The fourth thumbnail's pixels seem to not be completely transparent."); + + yield removeTab(target.tab); + finish(); +} + +function Uint32(src) { + let charView = new Uint8Array(src); + return new Uint32Array(charView.buffer); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-05.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-05.js new file mode 100644 index 000000000..e13dab9a4 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-05.js @@ -0,0 +1,50 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if draw calls inside a single animation frame generate and retrieve + * the correct "end result" screenshot. + */ + +function* ifTestingSupported() { + let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL); + + let navigated = once(target, "navigate"); + + yield front.setup({ reload: true }); + ok(true, "The front was setup up successfully."); + + yield navigated; + ok(true, "Target automatically navigated when the front was set up."); + + let snapshotActor = yield front.recordAnimationFrame(); + ok(snapshotActor, + "A snapshot actor was sent after recording."); + + let animationOverview = yield snapshotActor.getOverview(); + ok(snapshotActor, + "An animation overview could be retrieved after recording."); + + let screenshot = animationOverview.screenshot; + ok(screenshot, + "A screenshot was sent after recording."); + + is(screenshot.index, 6, + "The screenshot's index is correct."); + is(screenshot.width, 128, + "The screenshot's width is correct."); + is(screenshot.height, 128, + "The screenshot's height is correct."); + is(screenshot.flipped, false, + "The screenshot's flipped flag is correct."); + is([].find.call(Uint32(screenshot.pixels), e => e > 0), 4290822336, + "The screenshot's pixels seem to not be completely transparent."); + + yield removeTab(target.tab); + finish(); +} + +function Uint32(src) { + let charView = new Uint8Array(src); + return new Uint32Array(charView.buffer); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-06.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-06.js new file mode 100644 index 000000000..511db6667 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-06.js @@ -0,0 +1,100 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if screenshots for arbitrary draw calls are generated properly. + */ + +function* ifTestingSupported() { + let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_TRANSPARENT_URL); + + let navigated = once(target, "navigate"); + + yield front.setup({ reload: true }); + ok(true, "The front was setup up successfully."); + + yield navigated; + ok(true, "Target automatically navigated when the front was set up."); + + let snapshotActor = yield front.recordAnimationFrame(); + let animationOverview = yield snapshotActor.getOverview(); + + let functionCalls = animationOverview.calls; + ok(functionCalls, + "An array of function call actors was sent after recording."); + is(functionCalls.length, 8, + "The number of function call actors is correct."); + + is(functionCalls[0].name, "clearRect", + "The first called function's name is correct."); + is(functionCalls[2].name, "fillRect", + "The second called function's name is correct."); + is(functionCalls[4].name, "fillRect", + "The third called function's name is correct."); + is(functionCalls[6].name, "fillRect", + "The fourth called function's name is correct."); + + let firstDrawCallScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[0]); + let secondDrawCallScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[2]); + let thirdDrawCallScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[4]); + let fourthDrawCallScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[6]); + + ok(firstDrawCallScreenshot, + "The first draw call has a screenshot attached."); + is(firstDrawCallScreenshot.index, 0, + "The first draw call has the correct screenshot index."); + is(firstDrawCallScreenshot.width, 128, + "The first draw call has the correct screenshot width."); + is(firstDrawCallScreenshot.height, 128, + "The first draw call has the correct screenshot height."); + is([].find.call(Uint32(firstDrawCallScreenshot.pixels), e => e > 0), undefined, + "The first draw call's screenshot's pixels seems to be completely transparent."); + + ok(secondDrawCallScreenshot, + "The second draw call has a screenshot attached."); + is(secondDrawCallScreenshot.index, 2, + "The second draw call has the correct screenshot index."); + is(secondDrawCallScreenshot.width, 128, + "The second draw call has the correct screenshot width."); + is(secondDrawCallScreenshot.height, 128, + "The second draw call has the correct screenshot height."); + is([].find.call(Uint32(firstDrawCallScreenshot.pixels), e => e > 0), undefined, + "The second draw call's screenshot's pixels seems to be completely transparent."); + + ok(thirdDrawCallScreenshot, + "The third draw call has a screenshot attached."); + is(thirdDrawCallScreenshot.index, 4, + "The third draw call has the correct screenshot index."); + is(thirdDrawCallScreenshot.width, 128, + "The third draw call has the correct screenshot width."); + is(thirdDrawCallScreenshot.height, 128, + "The third draw call has the correct screenshot height."); + is([].find.call(Uint32(thirdDrawCallScreenshot.pixels), e => e > 0), 2160001024, + "The third draw call's screenshot's pixels seems to not be completely transparent."); + + ok(fourthDrawCallScreenshot, + "The fourth draw call has a screenshot attached."); + is(fourthDrawCallScreenshot.index, 6, + "The fourth draw call has the correct screenshot index."); + is(fourthDrawCallScreenshot.width, 128, + "The fourth draw call has the correct screenshot width."); + is(fourthDrawCallScreenshot.height, 128, + "The fourth draw call has the correct screenshot height."); + is([].find.call(Uint32(fourthDrawCallScreenshot.pixels), e => e > 0), 2147483839, + "The fourth draw call's screenshot's pixels seems to not be completely transparent."); + + isnot(firstDrawCallScreenshot.pixels, secondDrawCallScreenshot.pixels, + "The screenshots taken on consecutive draw calls are different (1)."); + isnot(secondDrawCallScreenshot.pixels, thirdDrawCallScreenshot.pixels, + "The screenshots taken on consecutive draw calls are different (2)."); + isnot(thirdDrawCallScreenshot.pixels, fourthDrawCallScreenshot.pixels, + "The screenshots taken on consecutive draw calls are different (3)."); + + yield removeTab(target.tab); + finish(); +} + +function Uint32(src) { + let charView = new Uint8Array(src); + return new Uint32Array(charView.buffer); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-07.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-07.js new file mode 100644 index 000000000..8e6c8c25a --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-07.js @@ -0,0 +1,94 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if screenshots for non-draw calls can still be retrieved properly, + * by deferring the the most recent previous draw-call. + */ + +function* ifTestingSupported() { + let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL); + + let navigated = once(target, "navigate"); + + yield front.setup({ reload: true }); + ok(true, "The front was setup up successfully."); + + yield navigated; + ok(true, "Target automatically navigated when the front was set up."); + + let snapshotActor = yield front.recordAnimationFrame(); + let animationOverview = yield snapshotActor.getOverview(); + + let functionCalls = animationOverview.calls; + ok(functionCalls, + "An array of function call actors was sent after recording."); + is(functionCalls.length, 8, + "The number of function call actors is correct."); + + let firstNonDrawCall = yield functionCalls[1].getDetails(); + let secondNonDrawCall = yield functionCalls[3].getDetails(); + let lastNonDrawCall = yield functionCalls[7].getDetails(); + + is(firstNonDrawCall.name, "fillStyle", + "The first non-draw function's name is correct."); + is(secondNonDrawCall.name, "fillStyle", + "The second non-draw function's name is correct."); + is(lastNonDrawCall.name, "requestAnimationFrame", + "The last non-draw function's name is correct."); + + let firstScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[1]); + let secondScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[3]); + let lastScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[7]); + + ok(firstScreenshot, + "A screenshot was successfully retrieved for the first non-draw function."); + ok(secondScreenshot, + "A screenshot was successfully retrieved for the second non-draw function."); + ok(lastScreenshot, + "A screenshot was successfully retrieved for the last non-draw function."); + + let firstActualScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[0]); + ok(sameArray(firstScreenshot.pixels, firstActualScreenshot.pixels), + "The screenshot for the first non-draw function is correct."); + is(firstScreenshot.width, 128, + "The screenshot for the first non-draw function has the correct width."); + is(firstScreenshot.height, 128, + "The screenshot for the first non-draw function has the correct height."); + + let secondActualScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[2]); + ok(sameArray(secondScreenshot.pixels, secondActualScreenshot.pixels), + "The screenshot for the second non-draw function is correct."); + is(secondScreenshot.width, 128, + "The screenshot for the second non-draw function has the correct width."); + is(secondScreenshot.height, 128, + "The screenshot for the second non-draw function has the correct height."); + + let lastActualScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[6]); + ok(sameArray(lastScreenshot.pixels, lastActualScreenshot.pixels), + "The screenshot for the last non-draw function is correct."); + is(lastScreenshot.width, 128, + "The screenshot for the last non-draw function has the correct width."); + is(lastScreenshot.height, 128, + "The screenshot for the last non-draw function has the correct height."); + + ok(!sameArray(firstScreenshot.pixels, secondScreenshot.pixels), + "The screenshots taken on consecutive draw calls are different (1)."); + ok(!sameArray(secondScreenshot.pixels, lastScreenshot.pixels), + "The screenshots taken on consecutive draw calls are different (2)."); + + yield removeTab(target.tab); + finish(); +} + +function sameArray(a, b) { + if (a.length != b.length) { + return false; + } + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) { + return false; + } + } + return true; +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-08.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-08.js new file mode 100644 index 000000000..f3aeda1a9 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-08.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests that integers used in arguments are not cast to their constant, enum value + * forms if the method's signature does not expect an enum. Bug 999687. + */ + +function* ifTestingSupported() { + let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_BITMASKS_URL); + + let navigated = once(target, "navigate"); + + yield front.setup({ reload: true }); + ok(true, "The front was setup up successfully."); + + yield navigated; + ok(true, "Target automatically navigated when the front was set up."); + + let snapshotActor = yield front.recordAnimationFrame(); + let animationOverview = yield snapshotActor.getOverview(); + let functionCalls = animationOverview.calls; + + is(functionCalls[0].name, "clearRect", + "The first called function's name is correct."); + is(functionCalls[0].argsPreview, "0, 0, 4, 4", + "The first called function's args preview is not cast to enums."); + + is(functionCalls[2].name, "fillRect", + "The fillRect called function's name is correct."); + is(functionCalls[2].argsPreview, "0, 0, 1, 1", + "The fillRect called function's args preview is not casted to enums."); + + yield removeTab(target.tab); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-09.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-09.js new file mode 100644 index 000000000..d123e3319 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-09.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests that integers used in arguments are not cast to their constant, enum value + * forms if the method's signature does not expect an enum. Bug 999687. + */ + +function* ifTestingSupported() { + let { target, front } = yield initCanvasDebuggerBackend(WEBGL_ENUM_URL); + + let navigated = once(target, "navigate"); + + yield front.setup({ reload: true }); + ok(true, "The front was setup up successfully."); + + yield navigated; + ok(true, "Target automatically navigated when the front was set up."); + + let snapshotActor = yield front.recordAnimationFrame(); + let animationOverview = yield snapshotActor.getOverview(); + let functionCalls = animationOverview.calls; + + is(functionCalls[0].name, "clear", + "The function's name is correct."); + is(functionCalls[0].argsPreview, "DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT | COLOR_BUFFER_BIT", + "The bits passed into `gl.clear` have been cast to their enum values."); + + is(functionCalls[1].name, "bindTexture", + "The function's name is correct."); + is(functionCalls[1].argsPreview, "TEXTURE_2D, null", + "The bits passed into `gl.bindTexture` have been cast to their enum values."); + + yield removeTab(target.tab); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-10.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-10.js new file mode 100644 index 000000000..672ef9662 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-10.js @@ -0,0 +1,107 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests that the correct framebuffer, renderbuffer and textures are re-bound + * after generating screenshots using the actor. + */ + +function* ifTestingSupported() { + let { target, front } = yield initCanvasDebuggerBackend(WEBGL_BINDINGS_URL); + loadFrameScripts(); + + let navigated = once(target, "navigate"); + + yield front.setup({ reload: true }); + ok(true, "The front was setup up successfully."); + + yield navigated; + ok(true, "Target automatically navigated when the front was set up."); + + let snapshotActor = yield front.recordAnimationFrame(); + let animationOverview = yield snapshotActor.getOverview(); + let functionCalls = animationOverview.calls; + + let firstScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[0]); + is(firstScreenshot.index, -1, + "The first screenshot didn't encounter any draw call."); + is(firstScreenshot.scaling, 0.25, + "The first screenshot has the correct scaling."); + is(firstScreenshot.width, CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT, + "The first screenshot has the correct width."); + is(firstScreenshot.height, CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT, + "The first screenshot has the correct height."); + is(firstScreenshot.flipped, true, + "The first screenshot has the correct 'flipped' flag."); + is(firstScreenshot.pixels.length, 0, + "The first screenshot should be empty."); + + is((yield evalInDebuggee("gl.getParameter(gl.FRAMEBUFFER_BINDING) === customFramebuffer")), + true, + "The debuggee's gl context framebuffer wasn't changed."); + is((yield evalInDebuggee("gl.getParameter(gl.RENDERBUFFER_BINDING) === customRenderbuffer")), + true, + "The debuggee's gl context renderbuffer wasn't changed."); + is((yield evalInDebuggee("gl.getParameter(gl.TEXTURE_BINDING_2D) === customTexture")), + true, + "The debuggee's gl context texture binding wasn't changed."); + is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[0]")), + 128, + "The debuggee's gl context viewport's left coord. wasn't changed."); + is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[1]")), + 256, + "The debuggee's gl context viewport's left coord. wasn't changed."); + is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[2]")), + 384, + "The debuggee's gl context viewport's left coord. wasn't changed."); + is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[3]")), + 512, + "The debuggee's gl context viewport's left coord. wasn't changed."); + + let secondScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[1]); + is(secondScreenshot.index, 1, + "The second screenshot has the correct index."); + is(secondScreenshot.width, CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT, + "The second screenshot has the correct width."); + is(secondScreenshot.height, CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT, + "The second screenshot has the correct height."); + is(secondScreenshot.scaling, 0.25, + "The second screenshot has the correct scaling."); + is(secondScreenshot.flipped, true, + "The second screenshot has the correct 'flipped' flag."); + is(secondScreenshot.pixels.length, Math.pow(CanvasFront.WEBGL_SCREENSHOT_MAX_HEIGHT, 2) * 4, + "The second screenshot should not be empty."); + is(secondScreenshot.pixels[0], 0, + "The second screenshot has the correct red component."); + is(secondScreenshot.pixels[1], 0, + "The second screenshot has the correct green component."); + is(secondScreenshot.pixels[2], 255, + "The second screenshot has the correct blue component."); + is(secondScreenshot.pixels[3], 255, + "The second screenshot has the correct alpha component."); + + is((yield evalInDebuggee("gl.getParameter(gl.FRAMEBUFFER_BINDING) === customFramebuffer")), + true, + "The debuggee's gl context framebuffer still wasn't changed."); + is((yield evalInDebuggee("gl.getParameter(gl.RENDERBUFFER_BINDING) === customRenderbuffer")), + true, + "The debuggee's gl context renderbuffer still wasn't changed."); + is((yield evalInDebuggee("gl.getParameter(gl.TEXTURE_BINDING_2D) === customTexture")), + true, + "The debuggee's gl context texture binding still wasn't changed."); + is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[0]")), + 128, + "The debuggee's gl context viewport's left coord. still wasn't changed."); + is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[1]")), + 256, + "The debuggee's gl context viewport's left coord. still wasn't changed."); + is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[2]")), + 384, + "The debuggee's gl context viewport's left coord. still wasn't changed."); + is((yield evalInDebuggee("gl.getParameter(gl.VIEWPORT)[3]")), + 512, + "The debuggee's gl context viewport's left coord. still wasn't changed."); + + yield removeTab(target.tab); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-11.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-11.js new file mode 100644 index 000000000..a1e5010b6 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-11.js @@ -0,0 +1,138 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests that loops using setTimeout are recorded and stored + * for a canvas context, and that the generated screenshots are correct. + */ + +function* ifTestingSupported() { + let { target, front } = yield initCanvasDebuggerBackend(SET_TIMEOUT_URL); + + let navigated = once(target, "navigate"); + + yield front.setup({ reload: true }); + ok(true, "The front was setup up successfully."); + + yield navigated; + ok(true, "Target automatically navigated when the front was set up."); + + let snapshotActor = yield front.recordAnimationFrame(); + ok(snapshotActor, + "A snapshot actor was sent after recording."); + + let animationOverview = yield snapshotActor.getOverview(); + ok(snapshotActor, + "An animation overview could be retrieved after recording."); + + let functionCalls = animationOverview.calls; + ok(functionCalls, + "An array of function call actors was sent after recording."); + is(functionCalls.length, 8, + "The number of function call actors is correct."); + + is(functionCalls[0].type, CallWatcherFront.METHOD_FUNCTION, + "The first called function is correctly identified as a method."); + is(functionCalls[0].name, "clearRect", + "The first called function's name is correct."); + is(functionCalls[0].file, SET_TIMEOUT_URL, + "The first called function's file is correct."); + is(functionCalls[0].line, 25, + "The first called function's line is correct."); + is(functionCalls[0].argsPreview, "0, 0, 128, 128", + "The first called function's args preview is correct."); + is(functionCalls[0].callerPreview, "Object", + "The first called function's caller preview is correct."); + + is(functionCalls[6].type, CallWatcherFront.METHOD_FUNCTION, + "The penultimate called function is correctly identified as a method."); + is(functionCalls[6].name, "fillRect", + "The penultimate called function's name is correct."); + is(functionCalls[6].file, SET_TIMEOUT_URL, + "The penultimate called function's file is correct."); + is(functionCalls[6].line, 21, + "The penultimate called function's line is correct."); + is(functionCalls[6].argsPreview, "10, 10, 55, 50", + "The penultimate called function's args preview is correct."); + is(functionCalls[6].callerPreview, "Object", + "The penultimate called function's caller preview is correct."); + + is(functionCalls[7].type, CallWatcherFront.METHOD_FUNCTION, + "The last called function is correctly identified as a method."); + is(functionCalls[7].name, "setTimeout", + "The last called function's name is correct."); + is(functionCalls[7].file, SET_TIMEOUT_URL, + "The last called function's file is correct."); + is(functionCalls[7].line, 30, + "The last called function's line is correct."); + ok(functionCalls[7].argsPreview.includes("Function"), + "The last called function's args preview is correct."); + is(functionCalls[7].callerPreview, "Object", + "The last called function's caller preview is correct."); + + let firstNonDrawCall = yield functionCalls[1].getDetails(); + let secondNonDrawCall = yield functionCalls[3].getDetails(); + let lastNonDrawCall = yield functionCalls[7].getDetails(); + + is(firstNonDrawCall.name, "fillStyle", + "The first non-draw function's name is correct."); + is(secondNonDrawCall.name, "fillStyle", + "The second non-draw function's name is correct."); + is(lastNonDrawCall.name, "setTimeout", + "The last non-draw function's name is correct."); + + let firstScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[1]); + let secondScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[3]); + let lastScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[7]); + + ok(firstScreenshot, + "A screenshot was successfully retrieved for the first non-draw function."); + ok(secondScreenshot, + "A screenshot was successfully retrieved for the second non-draw function."); + ok(lastScreenshot, + "A screenshot was successfully retrieved for the last non-draw function."); + + let firstActualScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[0]); + ok(sameArray(firstScreenshot.pixels, firstActualScreenshot.pixels), + "The screenshot for the first non-draw function is correct."); + is(firstScreenshot.width, 128, + "The screenshot for the first non-draw function has the correct width."); + is(firstScreenshot.height, 128, + "The screenshot for the first non-draw function has the correct height."); + + let secondActualScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[2]); + ok(sameArray(secondScreenshot.pixels, secondActualScreenshot.pixels), + "The screenshot for the second non-draw function is correct."); + is(secondScreenshot.width, 128, + "The screenshot for the second non-draw function has the correct width."); + is(secondScreenshot.height, 128, + "The screenshot for the second non-draw function has the correct height."); + + let lastActualScreenshot = yield snapshotActor.generateScreenshotFor(functionCalls[6]); + ok(sameArray(lastScreenshot.pixels, lastActualScreenshot.pixels), + "The screenshot for the last non-draw function is correct."); + is(lastScreenshot.width, 128, + "The screenshot for the last non-draw function has the correct width."); + is(lastScreenshot.height, 128, + "The screenshot for the last non-draw function has the correct height."); + + ok(!sameArray(firstScreenshot.pixels, secondScreenshot.pixels), + "The screenshots taken on consecutive draw calls are different (1)."); + ok(!sameArray(secondScreenshot.pixels, lastScreenshot.pixels), + "The screenshots taken on consecutive draw calls are different (2)."); + + yield removeTab(target.tab); + finish(); +} + +function sameArray(a, b) { + if (a.length != b.length) { + return false; + } + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) { + return false; + } + } + return true; +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-actor-test-12.js b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-12.js new file mode 100644 index 000000000..86e51931e --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-actor-test-12.js @@ -0,0 +1,29 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests that the recording can be disabled via stopRecordingAnimationFrame + * in the event no rAF loop is found. + */ + +function* ifTestingSupported() { + let { target, front } = yield initCanvasDebuggerBackend(NO_CANVAS_URL); + loadFrameScripts(); + + let navigated = once(target, "navigate"); + + yield front.setup({ reload: true }); + ok(true, "The front was setup up successfully."); + + yield navigated; + ok(true, "Target automatically navigated when the front was set up."); + + let startRecording = front.recordAnimationFrame(); + yield front.stopRecordingAnimationFrame(); + + ok(!(yield startRecording), + "recordAnimationFrame() does not return a SnapshotActor when cancelled."); + + yield removeTab(target.tab); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-highlight.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-highlight.js new file mode 100644 index 000000000..2270f0ccf --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-highlight.js @@ -0,0 +1,41 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if certain function calls are properly highlighted in the UI. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin; + + yield reload(target); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED); + SnapshotsListView._onRecordButtonClick(); + yield promise.all([recordingFinished, callListPopulated]); + + is(CallsListView.itemCount, 8, + "All the function calls should now be displayed in the UI."); + + is($(".call-item-view", CallsListView.getItemAtIndex(0).target).hasAttribute("draw-call"), true, + "The first item's node should have a draw-call attribute."); + is($(".call-item-view", CallsListView.getItemAtIndex(1).target).hasAttribute("draw-call"), false, + "The second item's node should not have a draw-call attribute."); + is($(".call-item-view", CallsListView.getItemAtIndex(2).target).hasAttribute("draw-call"), true, + "The third item's node should have a draw-call attribute."); + is($(".call-item-view", CallsListView.getItemAtIndex(3).target).hasAttribute("draw-call"), false, + "The fourth item's node should not have a draw-call attribute."); + is($(".call-item-view", CallsListView.getItemAtIndex(4).target).hasAttribute("draw-call"), true, + "The fifth item's node should have a draw-call attribute."); + is($(".call-item-view", CallsListView.getItemAtIndex(5).target).hasAttribute("draw-call"), false, + "The sixth item's node should not have a draw-call attribute."); + is($(".call-item-view", CallsListView.getItemAtIndex(6).target).hasAttribute("draw-call"), true, + "The seventh item's node should have a draw-call attribute."); + is($(".call-item-view", CallsListView.getItemAtIndex(7).target).hasAttribute("draw-call"), false, + "The eigth item's node should not have a draw-call attribute."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-list.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-list.js new file mode 100644 index 000000000..5f9ce876f --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-list.js @@ -0,0 +1,70 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if all the function calls associated with an animation frame snapshot + * are properly displayed in the UI. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin; + + yield reload(target); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED); + SnapshotsListView._onRecordButtonClick(); + yield promise.all([recordingFinished, callListPopulated]); + + is(CallsListView.itemCount, 8, + "All the function calls should now be displayed in the UI."); + + testItem(CallsListView.getItemAtIndex(0), + "1", "Object", "clearRect", "(0, 0, 128, 128)", "doc_simple-canvas.html:25"); + + testItem(CallsListView.getItemAtIndex(1), + "2", "Object", "fillStyle", " = rgb(192, 192, 192)", "doc_simple-canvas.html:20"); + testItem(CallsListView.getItemAtIndex(2), + "3", "Object", "fillRect", "(0, 0, 128, 128)", "doc_simple-canvas.html:21"); + + testItem(CallsListView.getItemAtIndex(3), + "4", "Object", "fillStyle", " = rgba(0, 0, 192, 0.5)", "doc_simple-canvas.html:20"); + testItem(CallsListView.getItemAtIndex(4), + "5", "Object", "fillRect", "(30, 30, 55, 50)", "doc_simple-canvas.html:21"); + + testItem(CallsListView.getItemAtIndex(5), + "6", "Object", "fillStyle", " = rgba(192, 0, 0, 0.5)", "doc_simple-canvas.html:20"); + testItem(CallsListView.getItemAtIndex(6), + "7", "Object", "fillRect", "(10, 10, 55, 50)", "doc_simple-canvas.html:21"); + + testItem(CallsListView.getItemAtIndex(7), + "8", "", "requestAnimationFrame", "(Function)", "doc_simple-canvas.html:30"); + + function testItem(item, index, context, name, args, location) { + let i = CallsListView.indexOfItem(item); + is(i, index - 1, + "The item at index " + index + " is correctly displayed in the UI."); + + is($(".call-item-index", item.target).getAttribute("value"), index, + "The item's gutter label has the correct text."); + + if (context) { + is($(".call-item-context", item.target).getAttribute("value"), context, + "The item's context label has the correct text."); + } else { + is($(".call-item-context", item.target) + "", "[object XULElement]", + "The item's context label should not be available."); + } + + is($(".call-item-name", item.target).getAttribute("value"), name, + "The item's name label has the correct text."); + is($(".call-item-args", item.target).getAttribute("value"), args, + "The item's args label has the correct text."); + is($(".call-item-location", item.target).getAttribute("value"), location, + "The item's location label has the correct text."); + } + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-search.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-search.js new file mode 100644 index 000000000..e865df391 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-search.js @@ -0,0 +1,72 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if filtering the items in the call list works properly. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin; + let searchbox = $("#calls-searchbox"); + + yield reload(target); + + let firstRecordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED); + SnapshotsListView._onRecordButtonClick(); + yield promise.all([firstRecordingFinished, callListPopulated]); + + is(searchbox.value, "", + "The searchbox should be initially empty."); + is(CallsListView.visibleItems.length, 8, + "All the items should be initially visible in the calls list."); + + searchbox.focus(); + EventUtils.sendString("clear", window); + + is(searchbox.value, "clear", + "The searchbox should now contain the 'clear' string."); + is(CallsListView.visibleItems.length, 1, + "Only one item should now be visible in the calls list."); + + is(CallsListView.visibleItems[0].attachment.actor.type, CallWatcherFront.METHOD_FUNCTION, + "The visible item's type has the expected value."); + is(CallsListView.visibleItems[0].attachment.actor.name, "clearRect", + "The visible item's name has the expected value."); + is(CallsListView.visibleItems[0].attachment.actor.file, SIMPLE_CANVAS_URL, + "The visible item's file has the expected value."); + is(CallsListView.visibleItems[0].attachment.actor.line, 25, + "The visible item's line has the expected value."); + is(CallsListView.visibleItems[0].attachment.actor.argsPreview, "0, 0, 128, 128", + "The visible item's args have the expected value."); + is(CallsListView.visibleItems[0].attachment.actor.callerPreview, "Object", + "The visible item's caller has the expected value."); + + let secondRecordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED); + + SnapshotsListView._onRecordButtonClick(); + yield secondRecordingFinished; + + SnapshotsListView.selectedIndex = 1; + yield callListPopulated; + + is(searchbox.value, "clear", + "The searchbox should still contain the 'clear' string."); + is(CallsListView.visibleItems.length, 1, + "Only one item should still be visible in the calls list."); + + for (let i = 0; i < 5; i++) { + searchbox.focus(); + EventUtils.sendKey("BACK_SPACE", window); + } + + is(searchbox.value, "", + "The searchbox should now be emptied."); + is(CallsListView.visibleItems.length, 8, + "All the items should be initially visible again in the calls list."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-01.js new file mode 100644 index 000000000..964683c84 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-01.js @@ -0,0 +1,82 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the a function call's stack is properly displayed in the UI. + */ + +// Force the old debugger UI since it's directly used (see Bug 1301705) +Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false); +registerCleanupFunction(function* () { + Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend"); +}); + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL); + let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin; + + yield reload(target); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED); + SnapshotsListView._onRecordButtonClick(); + yield promise.all([recordingFinished, callListPopulated]); + + let callItem = CallsListView.getItemAtIndex(2); + let locationLink = $(".call-item-location", callItem.target); + + is($(".call-item-stack", callItem.target), null, + "There should be no stack container available yet for the draw call."); + + let callStackDisplayed = once(window, EVENTS.CALL_STACK_DISPLAYED); + EventUtils.sendMouseEvent({ type: "mousedown" }, locationLink, window); + yield callStackDisplayed; + + isnot($(".call-item-stack", callItem.target), null, + "There should be a stack container available now for the draw call."); + // We may have more than 4 functions, depending on whether async + // stacks are available. + ok($all(".call-item-stack-fn", callItem.target).length >= 4, + "There should be at least 4 functions on the stack for the draw call."); + + ok($all(".call-item-stack-fn-name", callItem.target)[0].getAttribute("value") + .includes("C()"), + "The first function on the stack has the correct name."); + ok($all(".call-item-stack-fn-name", callItem.target)[1].getAttribute("value") + .includes("B()"), + "The second function on the stack has the correct name."); + ok($all(".call-item-stack-fn-name", callItem.target)[2].getAttribute("value") + .includes("A()"), + "The third function on the stack has the correct name."); + ok($all(".call-item-stack-fn-name", callItem.target)[3].getAttribute("value") + .includes("drawRect()"), + "The fourth function on the stack has the correct name."); + + is($all(".call-item-stack-fn-location", callItem.target)[0].getAttribute("value"), + "doc_simple-canvas-deep-stack.html:26", + "The first function on the stack has the correct location."); + is($all(".call-item-stack-fn-location", callItem.target)[1].getAttribute("value"), + "doc_simple-canvas-deep-stack.html:28", + "The second function on the stack has the correct location."); + is($all(".call-item-stack-fn-location", callItem.target)[2].getAttribute("value"), + "doc_simple-canvas-deep-stack.html:30", + "The third function on the stack has the correct location."); + is($all(".call-item-stack-fn-location", callItem.target)[3].getAttribute("value"), + "doc_simple-canvas-deep-stack.html:35", + "The fourth function on the stack has the correct location."); + + let jumpedToSource = once(window, EVENTS.SOURCE_SHOWN_IN_JS_DEBUGGER); + EventUtils.sendMouseEvent({ type: "mousedown" }, $(".call-item-stack-fn-location", callItem.target)); + yield jumpedToSource; + + let toolbox = yield gDevTools.getToolbox(target); + let { panelWin: { DebuggerView: view } } = toolbox.getPanel("jsdebugger"); + + is(view.Sources.selectedValue, getSourceActor(view.Sources, SIMPLE_CANVAS_DEEP_STACK_URL), + "The expected source was shown in the debugger."); + is(view.editor.getCursor().line, 25, + "The expected source line is highlighted in the debugger."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-02.js new file mode 100644 index 000000000..9b5c65839 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-02.js @@ -0,0 +1,57 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the a function call's stack is properly displayed in the UI + * and jumping to source in the debugger for the topmost call item works. + */ + +// Force the old debugger UI since it's directly used (see Bug 1301705) +Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false); +registerCleanupFunction(function* () { + Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend"); +}); + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL); + let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin; + + yield reload(target); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED); + SnapshotsListView._onRecordButtonClick(); + yield promise.all([recordingFinished, callListPopulated]); + + let callItem = CallsListView.getItemAtIndex(2); + let locationLink = $(".call-item-location", callItem.target); + + is($(".call-item-stack", callItem.target), null, + "There should be no stack container available yet for the draw call."); + + let callStackDisplayed = once(window, EVENTS.CALL_STACK_DISPLAYED); + EventUtils.sendMouseEvent({ type: "mousedown" }, locationLink, window); + yield callStackDisplayed; + + isnot($(".call-item-stack", callItem.target), null, + "There should be a stack container available now for the draw call."); + // We may have more than 4 functions, depending on whether async + // stacks are available. + ok($all(".call-item-stack-fn", callItem.target).length >= 4, + "There should be at least 4 functions on the stack for the draw call."); + + let jumpedToSource = once(window, EVENTS.SOURCE_SHOWN_IN_JS_DEBUGGER); + EventUtils.sendMouseEvent({ type: "mousedown" }, $(".call-item-location", callItem.target)); + yield jumpedToSource; + + let toolbox = yield gDevTools.getToolbox(target); + let { panelWin: { DebuggerView: view } } = toolbox.getPanel("jsdebugger"); + + is(view.Sources.selectedValue, getSourceActor(view.Sources, SIMPLE_CANVAS_DEEP_STACK_URL), + "The expected source was shown in the debugger."); + is(view.editor.getCursor().line, 23, + "The expected source line is highlighted in the debugger."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-03.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-03.js new file mode 100644 index 000000000..24780c566 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-03.js @@ -0,0 +1,65 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the a function call's stack can be shown/hidden by double-clicking + * on a function call item. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL); + let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin; + + yield reload(target); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED); + SnapshotsListView._onRecordButtonClick(); + yield promise.all([recordingFinished, callListPopulated]); + + let callItem = CallsListView.getItemAtIndex(2); + let view = $(".call-item-view", callItem.target); + let contents = $(".call-item-contents", callItem.target); + + is(view.hasAttribute("call-stack-populated"), false, + "The call item's view should not have the stack populated yet."); + is(view.hasAttribute("call-stack-expanded"), false, + "The call item's view should not have the stack populated yet."); + is($(".call-item-stack", callItem.target), null, + "There should be no stack container available yet for the draw call."); + + let callStackDisplayed = once(window, EVENTS.CALL_STACK_DISPLAYED); + EventUtils.sendMouseEvent({ type: "dblclick" }, contents, window); + yield callStackDisplayed; + + is(view.hasAttribute("call-stack-populated"), true, + "The call item's view should have the stack populated now."); + is(view.getAttribute("call-stack-expanded"), "true", + "The call item's view should have the stack expanded now."); + isnot($(".call-item-stack", callItem.target), null, + "There should be a stack container available now for the draw call."); + is($(".call-item-stack", callItem.target).hidden, false, + "The stack container should now be visible."); + // We may have more than 4 functions, depending on whether async + // stacks are available. + ok($all(".call-item-stack-fn", callItem.target).length >= 4, + "There should be at least 4 functions on the stack for the draw call."); + + EventUtils.sendMouseEvent({ type: "dblclick" }, contents, window); + + is(view.hasAttribute("call-stack-populated"), true, + "The call item's view should still have the stack populated."); + is(view.getAttribute("call-stack-expanded"), "false", + "The call item's view should not have the stack expanded anymore."); + isnot($(".call-item-stack", callItem.target), null, + "There should still be a stack container available for the draw call."); + is($(".call-item-stack", callItem.target).hidden, true, + "The stack container should now be hidden."); + // We may have more than 4 functions, depending on whether async + // stacks are available. + ok($all(".call-item-stack-fn", callItem.target).length >= 4, + "There should still be at least 4 functions on the stack for the draw call."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-clear.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-clear.js new file mode 100644 index 000000000..c80082046 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-clear.js @@ -0,0 +1,43 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if clearing the snapshots list works as expected. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, EVENTS, SnapshotsListView } = panel.panelWin; + + yield reload(target); + + let firstRecordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + SnapshotsListView._onRecordButtonClick(); + + yield firstRecordingFinished; + ok(true, "Finished recording a snapshot of the animation loop."); + + is(SnapshotsListView.itemCount, 1, + "There should be one item available in the snapshots list."); + + let secondRecordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + SnapshotsListView._onRecordButtonClick(); + + yield secondRecordingFinished; + ok(true, "Finished recording another snapshot of the animation loop."); + + is(SnapshotsListView.itemCount, 2, + "There should be two items available in the snapshots list."); + + let clearingFinished = once(window, EVENTS.SNAPSHOTS_LIST_CLEARED); + SnapshotsListView._onClearButtonClick(); + + yield clearingFinished; + ok(true, "Finished recording all snapshots."); + + is(SnapshotsListView.itemCount, 0, + "There should be no items available in the snapshots list."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-screenshots.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-screenshots.js new file mode 100644 index 000000000..e96543e10 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-screenshots.js @@ -0,0 +1,34 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if screenshots are properly displayed in the UI. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, $, EVENTS, SnapshotsListView } = panel.panelWin; + + yield reload(target); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED); + let screenshotDisplayed = once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED); + SnapshotsListView._onRecordButtonClick(); + yield promise.all([recordingFinished, callListPopulated, screenshotDisplayed]); + + is($("#screenshot-container").hidden, false, + "The screenshot container should now be visible."); + + is($("#screenshot-dimensions").getAttribute("value"), "128" + "\u00D7" + "128", + "The screenshot dimensions label has the expected value."); + + is($("#screenshot-image").getAttribute("flipped"), "false", + "The screenshot element should not be flipped vertically."); + + ok(window.getComputedStyle($("#screenshot-image")).backgroundImage.includes("#screenshot-rendering"), + "The screenshot element should have an offscreen canvas element as a background."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-01.js new file mode 100644 index 000000000..41e8f7383 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-01.js @@ -0,0 +1,65 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if thumbnails are properly displayed in the UI. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, $, $all, EVENTS, SnapshotsListView } = panel.panelWin; + + yield reload(target); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED); + let thumbnailsDisplayed = once(window, EVENTS.THUMBNAILS_DISPLAYED); + SnapshotsListView._onRecordButtonClick(); + yield promise.all([recordingFinished, callListPopulated, thumbnailsDisplayed]); + + is($all(".filmstrip-thumbnail").length, 4, + "There should be 4 thumbnails displayed in the UI."); + + let firstThumbnail = $(".filmstrip-thumbnail[index='0']"); + ok(firstThumbnail, + "The first thumbnail element should be for the function call at index 0."); + is(firstThumbnail.width, 50, + "The first thumbnail's width is correct."); + is(firstThumbnail.height, 50, + "The first thumbnail's height is correct."); + is(firstThumbnail.getAttribute("flipped"), "false", + "The first thumbnail should not be flipped vertically."); + + let secondThumbnail = $(".filmstrip-thumbnail[index='2']"); + ok(secondThumbnail, + "The second thumbnail element should be for the function call at index 2."); + is(secondThumbnail.width, 50, + "The second thumbnail's width is correct."); + is(secondThumbnail.height, 50, + "The second thumbnail's height is correct."); + is(secondThumbnail.getAttribute("flipped"), "false", + "The second thumbnail should not be flipped vertically."); + + let thirdThumbnail = $(".filmstrip-thumbnail[index='4']"); + ok(thirdThumbnail, + "The third thumbnail element should be for the function call at index 4."); + is(thirdThumbnail.width, 50, + "The third thumbnail's width is correct."); + is(thirdThumbnail.height, 50, + "The third thumbnail's height is correct."); + is(thirdThumbnail.getAttribute("flipped"), "false", + "The third thumbnail should not be flipped vertically."); + + let fourthThumbnail = $(".filmstrip-thumbnail[index='6']"); + ok(fourthThumbnail, + "The fourth thumbnail element should be for the function call at index 6."); + is(fourthThumbnail.width, 50, + "The fourth thumbnail's width is correct."); + is(fourthThumbnail.height, 50, + "The fourth thumbnail's height is correct."); + is(fourthThumbnail.getAttribute("flipped"), "false", + "The fourth thumbnail should not be flipped vertically."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-02.js new file mode 100644 index 000000000..798bc090b --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-02.js @@ -0,0 +1,67 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if thumbnails are correctly linked with other UI elements like + * function call items and their respective screenshots. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin; + + yield reload(target); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED); + let thumbnailsDisplayed = once(window, EVENTS.THUMBNAILS_DISPLAYED); + let screenshotDisplayed = once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED); + SnapshotsListView._onRecordButtonClick(); + yield promise.all([ + recordingFinished, + callListPopulated, + thumbnailsDisplayed, + screenshotDisplayed + ]); + + is($all(".filmstrip-thumbnail[highlighted]").length, 0, + "There should be no highlighted thumbnail available yet."); + is(CallsListView.selectedIndex, -1, + "There should be no selected item in the calls list view."); + + EventUtils.sendMouseEvent({ type: "mousedown" }, $all(".filmstrip-thumbnail")[0], window); + yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED); + info("The first draw call was selected, by clicking the first thumbnail."); + + isnot($(".filmstrip-thumbnail[highlighted][index='0']"), null, + "There should be a highlighted thumbnail available now, for the first draw call."); + is($all(".filmstrip-thumbnail[highlighted]").length, 1, + "There should be only one highlighted thumbnail available now."); + is(CallsListView.selectedIndex, 0, + "The first draw call should be selected in the calls list view."); + + EventUtils.sendMouseEvent({ type: "mousedown" }, $all(".call-item-view")[1], window); + yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED); + info("The second context call was selected, by clicking the second call item."); + + isnot($(".filmstrip-thumbnail[highlighted][index='0']"), null, + "There should be a highlighted thumbnail available, for the first draw call."); + is($all(".filmstrip-thumbnail[highlighted]").length, 1, + "There should be only one highlighted thumbnail available."); + is(CallsListView.selectedIndex, 1, + "The second draw call should be selected in the calls list view."); + + EventUtils.sendMouseEvent({ type: "mousedown" }, $all(".call-item-view")[2], window); + yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED); + info("The second draw call was selected, by clicking the third call item."); + + isnot($(".filmstrip-thumbnail[highlighted][index='2']"), null, + "There should be a highlighted thumbnail available, for the second draw call."); + is($all(".filmstrip-thumbnail[highlighted]").length, 1, + "There should be only one highlighted thumbnail available."); + is(CallsListView.selectedIndex, 2, + "The second draw call should be selected in the calls list view."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-open.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-open.js new file mode 100644 index 000000000..59c4d4cfb --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-open.js @@ -0,0 +1,41 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests that the frontend UI is properly configured when opening the tool. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { $ } = panel.panelWin; + + is($("#snapshots-pane").hasAttribute("hidden"), false, + "The snapshots pane should initially be visible."); + is($("#debugging-pane").hasAttribute("hidden"), false, + "The debugging pane should initially be visible."); + + is($("#record-snapshot").getAttribute("hidden"), "true", + "The 'record snapshot' button should initially be hidden."); + is($("#import-snapshot").hasAttribute("hidden"), false, + "The 'import snapshot' button should initially be visible."); + is($("#clear-snapshots").hasAttribute("hidden"), false, + "The 'clear snapshots' button should initially be visible."); + + is($("#reload-notice").hasAttribute("hidden"), false, + "The reload notice should initially be visible."); + is($("#empty-notice").getAttribute("hidden"), "true", + "The empty notice should initially be hidden."); + is($("#waiting-notice").getAttribute("hidden"), "true", + "The waiting notice should initially be hidden."); + + is($("#screenshot-container").getAttribute("hidden"), "true", + "The screenshot container should initially be hidden."); + is($("#snapshot-filmstrip").getAttribute("hidden"), "true", + "The snapshot filmstrip should initially be hidden."); + + is($("#debugging-pane-contents").getAttribute("hidden"), "true", + "The rest of the UI should initially be hidden."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-01.js new file mode 100644 index 000000000..cd0358d3c --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-01.js @@ -0,0 +1,60 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests whether the frontend behaves correctly while reording a snapshot. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, EVENTS, $, SnapshotsListView } = panel.panelWin; + + yield reload(target); + + is($("#record-snapshot").hasAttribute("checked"), false, + "The 'record snapshot' button should initially be unchecked."); + is($("#record-snapshot").hasAttribute("disabled"), false, + "The 'record snapshot' button should initially be enabled."); + is($("#record-snapshot").hasAttribute("hidden"), false, + "The 'record snapshot' button should now be visible."); + + is(SnapshotsListView.itemCount, 0, + "There should be no items available in the snapshots list view."); + is(SnapshotsListView.selectedIndex, -1, + "There should be no selected item in the snapshots list view."); + + let recordingStarted = once(window, EVENTS.SNAPSHOT_RECORDING_STARTED); + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + SnapshotsListView._onRecordButtonClick(); + + yield recordingStarted; + ok(true, "Started recording a snapshot of the animation loop."); + + is($("#record-snapshot").getAttribute("checked"), "true", + "The 'record snapshot' button should now be checked."); + is($("#record-snapshot").hasAttribute("hidden"), false, + "The 'record snapshot' button should still be visible."); + + is(SnapshotsListView.itemCount, 1, + "There should be one item available in the snapshots list view now."); + is(SnapshotsListView.selectedIndex, -1, + "There should be no selected item in the snapshots list view yet."); + + yield recordingFinished; + ok(true, "Finished recording a snapshot of the animation loop."); + + is($("#record-snapshot").hasAttribute("checked"), false, + "The 'record snapshot' button should now be unchecked."); + is($("#record-snapshot").hasAttribute("disabled"), false, + "The 'record snapshot' button should now be re-enabled."); + is($("#record-snapshot").hasAttribute("hidden"), false, + "The 'record snapshot' button should still be visible."); + + is(SnapshotsListView.itemCount, 1, + "There should still be only one item available in the snapshots list view."); + is(SnapshotsListView.selectedIndex, 0, + "There should be one selected item in the snapshots list view now."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-02.js new file mode 100644 index 000000000..aee63a574 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-02.js @@ -0,0 +1,73 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests whether the frontend displays a placeholder snapshot while recording. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, EVENTS, L10N, $, SnapshotsListView } = panel.panelWin; + + yield reload(target); + + let recordingStarted = once(window, EVENTS.SNAPSHOT_RECORDING_STARTED); + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let recordingSelected = once(window, EVENTS.SNAPSHOT_RECORDING_SELECTED); + SnapshotsListView._onRecordButtonClick(); + + yield recordingStarted; + ok(true, "Started recording a snapshot of the animation loop."); + + let item = SnapshotsListView.getItemAtIndex(0); + + is($(".snapshot-item-title", item.target).getAttribute("value"), + L10N.getFormatStr("snapshotsList.itemLabel", 1), + "The placeholder item's title label is correct."); + + is($(".snapshot-item-calls", item.target).getAttribute("value"), + L10N.getStr("snapshotsList.loadingLabel"), + "The placeholder item's calls label is correct."); + + is($(".snapshot-item-save", item.target).getAttribute("value"), "", + "The placeholder item's save label should not have a value yet."); + + is($("#reload-notice").getAttribute("hidden"), "true", + "The reload notice should now be hidden."); + is($("#empty-notice").getAttribute("hidden"), "true", + "The empty notice should now be hidden."); + is($("#waiting-notice").hasAttribute("hidden"), false, + "The waiting notice should now be visible."); + + is($("#screenshot-container").getAttribute("hidden"), "true", + "The screenshot container should still be hidden."); + is($("#snapshot-filmstrip").getAttribute("hidden"), "true", + "The snapshot filmstrip should still be hidden."); + + is($("#debugging-pane-contents").getAttribute("hidden"), "true", + "The rest of the UI should still be hidden."); + + yield recordingFinished; + ok(true, "Finished recording a snapshot of the animation loop."); + + yield recordingSelected; + ok(true, "Finished selecting a snapshot of the animation loop."); + + is($("#reload-notice").getAttribute("hidden"), "true", + "The reload notice should now be hidden."); + is($("#empty-notice").getAttribute("hidden"), "true", + "The empty notice should now be hidden."); + is($("#waiting-notice").getAttribute("hidden"), "true", + "The waiting notice should now be hidden."); + + is($("#screenshot-container").hasAttribute("hidden"), false, + "The screenshot container should now be visible."); + is($("#snapshot-filmstrip").hasAttribute("hidden"), false, + "The snapshot filmstrip should now be visible."); + + is($("#debugging-pane-contents").hasAttribute("hidden"), false, + "The rest of the UI should now be visible."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-03.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-03.js new file mode 100644 index 000000000..c3638610e --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-03.js @@ -0,0 +1,37 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests whether the frontend displays the correct info for a snapshot + * after finishing recording. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, EVENTS, $, SnapshotsListView } = panel.panelWin; + + yield reload(target); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + SnapshotsListView._onRecordButtonClick(); + + yield recordingFinished; + ok(true, "Finished recording a snapshot of the animation loop."); + + let item = SnapshotsListView.getItemAtIndex(0); + + is(SnapshotsListView.selectedItem, item, + "The first item should now be selected in the snapshots list view (1)."); + is(SnapshotsListView.selectedIndex, 0, + "The first item should now be selected in the snapshots list view (2)."); + + is($(".snapshot-item-calls", item.target).getAttribute("value"), "4 draws, 8 calls", + "The placeholder item's calls label is correct."); + is($(".snapshot-item-save", item.target).getAttribute("value"), "Save", + "The placeholder item's save label is correct."); + is($(".snapshot-item-save", item.target).getAttribute("disabled"), "false", + "The placeholder item's save label should be clickable."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-04.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-04.js new file mode 100644 index 000000000..fde8501e6 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-record-04.js @@ -0,0 +1,34 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Bug 1122766 + * Tests that the canvas actor correctly returns from recordAnimationFrame + * in the scenario where a loop starts with rAF and has rAF in the beginning + * of its loop, when the recording starts before the rAFs start. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(RAF_BEGIN_URL); + let { window, EVENTS, gFront, SnapshotsListView } = panel.panelWin; + loadFrameScripts(); + + yield reload(target); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + SnapshotsListView._onRecordButtonClick(); + + // Wait until after the recording started to trigger the content. + // Use the gFront method rather than the SNAPSHOT_RECORDING_STARTED event + // which triggers before the underlying actor call + yield waitUntil(function* () { return !(yield gFront.isRecording()); }); + + // Start animation in content + evalInDebuggee("start();"); + + yield recordingFinished; + ok(true, "Finished recording a snapshot of the animation loop."); + + yield removeTab(target.tab); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-01.js new file mode 100644 index 000000000..cf353aa27 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-01.js @@ -0,0 +1,55 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests that the frontend UI is properly reconfigured after reloading. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, $, EVENTS } = panel.panelWin; + + let reset = once(window, EVENTS.UI_RESET); + let navigated = reload(target); + + yield reset; + ok(true, "The UI was reset after the refresh button was clicked."); + + yield navigated; + ok(true, "The target finished reloading."); + + is($("#snapshots-pane").hasAttribute("hidden"), false, + "The snapshots pane should still be visible."); + is($("#debugging-pane").hasAttribute("hidden"), false, + "The debugging pane should still be visible."); + + is($("#record-snapshot").hasAttribute("checked"), false, + "The 'record snapshot' button should not be checked."); + is($("#record-snapshot").hasAttribute("disabled"), false, + "The 'record snapshot' button should not be disabled."); + + is($("#record-snapshot").hasAttribute("hidden"), false, + "The 'record snapshot' button should now be visible."); + is($("#import-snapshot").hasAttribute("hidden"), false, + "The 'import snapshot' button should still be visible."); + is($("#clear-snapshots").hasAttribute("hidden"), false, + "The 'clear snapshots' button should still be visible."); + + is($("#reload-notice").getAttribute("hidden"), "true", + "The reload notice should now be hidden."); + is($("#empty-notice").hasAttribute("hidden"), false, + "The empty notice should now be visible."); + is($("#waiting-notice").getAttribute("hidden"), "true", + "The waiting notice should now be hidden."); + + is($("#snapshot-filmstrip").getAttribute("hidden"), "true", + "The snapshot filmstrip should still be hidden."); + is($("#screenshot-container").getAttribute("hidden"), "true", + "The screenshot container should still be hidden."); + + is($("#debugging-pane-contents").getAttribute("hidden"), "true", + "The rest of the UI should still be hidden."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-02.js new file mode 100644 index 000000000..2747fd13f --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-02.js @@ -0,0 +1,70 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests that the frontend UI is properly reconfigured after reloading. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin; + + is(SnapshotsListView.itemCount, 0, + "There should be no snapshots initially displayed in the UI."); + is(CallsListView.itemCount, 0, + "There should be no function calls initially displayed in the UI."); + + is($("#screenshot-container").hidden, true, + "The screenshot should not be initially displayed in the UI."); + is($("#snapshot-filmstrip").hidden, true, + "There should be no thumbnails initially displayed in the UI (1)."); + is($all(".filmstrip-thumbnail").length, 0, + "There should be no thumbnails initially displayed in the UI (2)."); + + yield reload(target); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED); + let thumbnailsDisplayed = once(window, EVENTS.THUMBNAILS_DISPLAYED); + let screenshotDisplayed = once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED); + SnapshotsListView._onRecordButtonClick(); + yield promise.all([ + recordingFinished, + callListPopulated, + thumbnailsDisplayed, + screenshotDisplayed + ]); + + is(SnapshotsListView.itemCount, 1, + "There should be one snapshot displayed in the UI."); + is(CallsListView.itemCount, 8, + "All the function calls should now be displayed in the UI."); + + is($("#screenshot-container").hidden, false, + "The screenshot should now be displayed in the UI."); + is($("#snapshot-filmstrip").hidden, false, + "All the thumbnails should now be displayed in the UI (1)."); + is($all(".filmstrip-thumbnail").length, 4, + "All the thumbnails should now be displayed in the UI (2)."); + + let reset = once(window, EVENTS.UI_RESET); + let navigated = reload(target); + + yield reset; + ok(true, "The UI was reset after the refresh button was clicked."); + + is(SnapshotsListView.itemCount, 0, + "There should be no snapshots displayed in the UI after navigating."); + is(CallsListView.itemCount, 0, + "There should be no function calls displayed in the UI after navigating."); + is($("#snapshot-filmstrip").hidden, true, + "There should be no thumbnails displayed in the UI after navigating."); + is($("#screenshot-container").hidden, true, + "The screenshot should not be displayed in the UI after navigating."); + + yield navigated; + ok(true, "The target finished reloading."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-01.js new file mode 100644 index 000000000..cdce00bd1 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-01.js @@ -0,0 +1,39 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the slider in the calls list view works as advertised. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin; + + yield reload(target); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED); + SnapshotsListView._onRecordButtonClick(); + yield promise.all([recordingFinished, callListPopulated]); + + is(CallsListView.selectedIndex, -1, + "No item in the function calls list should be initially selected."); + + is($("#calls-slider").value, 0, + "The slider should be moved all the way to the start."); + is($("#calls-slider").min, 0, + "The slider minimum value should be 0."); + is($("#calls-slider").max, 7, + "The slider maximum value should be 7."); + + CallsListView.selectedIndex = 1; + is($("#calls-slider").value, 1, + "The slider should be changed according to the current selection."); + + $("#calls-slider").value = 2; + is(CallsListView.selectedIndex, 2, + "The calls selection should be changed according to the current slider value."); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-02.js new file mode 100644 index 000000000..5074ab206 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-02.js @@ -0,0 +1,97 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the slider in the calls list view works as advertised. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, $, EVENTS, gFront, SnapshotsListView, CallsListView } = panel.panelWin; + + yield reload(target); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED); + let thumbnailsDisplayed = once(window, EVENTS.THUMBNAILS_DISPLAYED); + SnapshotsListView._onRecordButtonClick(); + yield promise.all([recordingFinished, callListPopulated, thumbnailsDisplayed]); + + let firstSnapshot = SnapshotsListView.getItemAtIndex(0); + let firstSnapshotOverview = yield firstSnapshot.attachment.actor.getOverview(); + + let thumbnails = firstSnapshotOverview.thumbnails; + is(thumbnails.length, 4, + "There should be 4 thumbnails cached for the snapshot item."); + + let thumbnailImageElementSet = waitForMozSetImageElement(window); + $("#calls-slider").value = 1; + let thumbnailPixels = yield thumbnailImageElementSet; + + ok(sameArray(thumbnailPixels, thumbnails[0].pixels), + "The screenshot element should have a thumbnail as an immediate background."); + + yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED); + ok(true, "The full-sized screenshot was displayed for the item at index 1."); + + thumbnailImageElementSet = waitForMozSetImageElement(window); + $("#calls-slider").value = 2; + thumbnailPixels = yield thumbnailImageElementSet; + + ok(sameArray(thumbnailPixels, thumbnails[1].pixels), + "The screenshot element should have a thumbnail as an immediate background."); + + yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED); + ok(true, "The full-sized screenshot was displayed for the item at index 2."); + + thumbnailImageElementSet = waitForMozSetImageElement(window); + $("#calls-slider").value = 7; + thumbnailPixels = yield thumbnailImageElementSet; + + ok(sameArray(thumbnailPixels, thumbnails[3].pixels), + "The screenshot element should have a thumbnail as an immediate background."); + + yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED); + ok(true, "The full-sized screenshot was displayed for the item at index 7."); + + thumbnailImageElementSet = waitForMozSetImageElement(window); + $("#calls-slider").value = 4; + thumbnailPixels = yield thumbnailImageElementSet; + + ok(sameArray(thumbnailPixels, thumbnails[2].pixels), + "The screenshot element should have a thumbnail as an immediate background."); + + yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED); + ok(true, "The full-sized screenshot was displayed for the item at index 4."); + + thumbnailImageElementSet = waitForMozSetImageElement(window); + $("#calls-slider").value = 0; + thumbnailPixels = yield thumbnailImageElementSet; + + ok(sameArray(thumbnailPixels, thumbnails[0].pixels), + "The screenshot element should have a thumbnail as an immediate background."); + + yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED); + ok(true, "The full-sized screenshot was displayed for the item at index 0."); + + yield teardown(panel); + finish(); +} + +function waitForMozSetImageElement(panel) { + let deferred = promise.defer(); + panel._onMozSetImageElement = deferred.resolve; + return deferred.promise; +} + +function sameArray(a, b) { + if (a.length != b.length) { + return false; + } + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) { + return false; + } + } + return true; +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-01.js new file mode 100644 index 000000000..4dc275282 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-01.js @@ -0,0 +1,93 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if selecting snapshots in the frontend displays the appropriate data + * respective to their recorded animation frame. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin; + + yield reload(target); + + yield recordAndWaitForFirstSnapshot(); + info("First snapshot recorded."); + + is(SnapshotsListView.selectedIndex, 0, + "A snapshot should be automatically selected after first recording."); + is(CallsListView.selectedIndex, -1, + "There should be no call item automatically selected in the snapshot."); + + yield recordAndWaitForAnotherSnapshot(); + info("Second snapshot recorded."); + + is(SnapshotsListView.selectedIndex, 0, + "A snapshot should not be automatically selected after another recording."); + is(CallsListView.selectedIndex, -1, + "There should still be no call item automatically selected in the snapshot."); + + let secondSnapshotTarget = SnapshotsListView.getItemAtIndex(1).target; + let snapshotSelected = waitForSnapshotSelection(); + EventUtils.sendMouseEvent({ type: "mousedown" }, secondSnapshotTarget, window); + + yield snapshotSelected; + info("Second snapshot selected."); + + is(SnapshotsListView.selectedIndex, 1, + "The second snapshot should now be selected."); + is(CallsListView.selectedIndex, -1, + "There should still be no call item automatically selected in the snapshot."); + + let firstDrawCallContents = $(".call-item-contents", CallsListView.getItemAtIndex(2).target); + let screenshotDisplayed = once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED); + EventUtils.sendMouseEvent({ type: "mousedown" }, firstDrawCallContents, window); + + yield screenshotDisplayed; + info("First draw call in the second snapshot selected."); + + is(SnapshotsListView.selectedIndex, 1, + "The second snapshot should still be selected."); + is(CallsListView.selectedIndex, 2, + "The first draw call should now be selected in the snapshot."); + + let firstSnapshotTarget = SnapshotsListView.getItemAtIndex(0).target; + snapshotSelected = waitForSnapshotSelection(); + EventUtils.sendMouseEvent({ type: "mousedown" }, firstSnapshotTarget, window); + + yield snapshotSelected; + info("First snapshot re-selected."); + + is(SnapshotsListView.selectedIndex, 0, + "The first snapshot should now be re-selected."); + is(CallsListView.selectedIndex, -1, + "There should still be no call item automatically selected in the snapshot."); + + function recordAndWaitForFirstSnapshot() { + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let snapshotSelected = waitForSnapshotSelection(); + SnapshotsListView._onRecordButtonClick(); + return promise.all([recordingFinished, snapshotSelected]); + } + + function recordAndWaitForAnotherSnapshot() { + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + SnapshotsListView._onRecordButtonClick(); + return recordingFinished; + } + + function waitForSnapshotSelection() { + let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED); + let thumbnailsDisplayed = once(window, EVENTS.THUMBNAILS_DISPLAYED); + let screenshotDisplayed = once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED); + return promise.all([ + callListPopulated, + thumbnailsDisplayed, + screenshotDisplayed + ]); + } + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-02.js new file mode 100644 index 000000000..27a03fb51 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-02.js @@ -0,0 +1,30 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if selecting snapshots in the frontend displays the appropriate data + * respective to their recorded animation frame. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin; + + yield reload(target); + + SnapshotsListView._onRecordButtonClick(); + let snapshotTarget = SnapshotsListView.getItemAtIndex(0).target; + + EventUtils.sendMouseEvent({ type: "mousedown" }, snapshotTarget, window); + EventUtils.sendMouseEvent({ type: "mousedown" }, snapshotTarget, window); + EventUtils.sendMouseEvent({ type: "mousedown" }, snapshotTarget, window); + + ok(true, "clicking in-progress snapshot does not fail"); + + let finished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + SnapshotsListView._onRecordButtonClick(); + yield finished; + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stepping.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stepping.js new file mode 100644 index 000000000..d76449b91 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stepping.js @@ -0,0 +1,76 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the stepping buttons in the call list toolbar work as advertised. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL); + let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin; + + yield reload(target); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED); + SnapshotsListView._onRecordButtonClick(); + yield promise.all([recordingFinished, callListPopulated]); + + checkSteppingButtons(1, 1, 1, 1); + is(CallsListView.selectedIndex, -1, + "There should be no selected item in the calls list view initially."); + + CallsListView._onResume(); + checkSteppingButtons(1, 1, 1, 1); + is(CallsListView.selectedIndex, 0, + "The first draw call should now be selected."); + + CallsListView._onResume(); + checkSteppingButtons(1, 1, 1, 1); + is(CallsListView.selectedIndex, 2, + "The second draw call should now be selected."); + + CallsListView._onStepOver(); + checkSteppingButtons(1, 1, 1, 1); + is(CallsListView.selectedIndex, 3, + "The next context call should now be selected."); + + CallsListView._onStepOut(); + checkSteppingButtons(0, 0, 1, 0); + is(CallsListView.selectedIndex, 7, + "The last context call should now be selected."); + + function checkSteppingButtons(resume, stepOver, stepIn, stepOut) { + if (!resume) { + is($("#resume").getAttribute("disabled"), "true", + "The resume button doesn't have the expected disabled state."); + } else { + is($("#resume").hasAttribute("disabled"), false, + "The resume button doesn't have the expected enabled state."); + } + if (!stepOver) { + is($("#step-over").getAttribute("disabled"), "true", + "The stepOver button doesn't have the expected disabled state."); + } else { + is($("#step-over").hasAttribute("disabled"), false, + "The stepOver button doesn't have the expected enabled state."); + } + if (!stepIn) { + is($("#step-in").getAttribute("disabled"), "true", + "The stepIn button doesn't have the expected disabled state."); + } else { + is($("#step-in").hasAttribute("disabled"), false, + "The stepIn button doesn't have the expected enabled state."); + } + if (!stepOut) { + is($("#step-out").getAttribute("disabled"), "true", + "The stepOut button doesn't have the expected disabled state."); + } else { + is($("#step-out").hasAttribute("disabled"), false, + "The stepOut button doesn't have the expected enabled state."); + } + } + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-01.js new file mode 100644 index 000000000..3a74e4b44 --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-01.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests that you can stop a recording that does not have a rAF cycle. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(NO_CANVAS_URL); + let { window, EVENTS, $, SnapshotsListView } = panel.panelWin; + + yield reload(target); + + let recordingStarted = once(window, EVENTS.SNAPSHOT_RECORDING_STARTED); + SnapshotsListView._onRecordButtonClick(); + + yield recordingStarted; + + is($("#empty-notice").hidden, true, "Empty notice not shown"); + is($("#waiting-notice").hidden, false, "Waiting notice shown"); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let recordingCancelled = once(window, EVENTS.SNAPSHOT_RECORDING_CANCELLED); + SnapshotsListView._onRecordButtonClick(); + + yield promise.all([recordingFinished, recordingCancelled]); + + ok(true, "Recording stopped and was considered failed."); + + is(SnapshotsListView.itemCount, 0, "No snapshots in the list."); + is($("#empty-notice").hidden, false, "Empty notice shown"); + is($("#waiting-notice").hidden, true, "Waiting notice not shown"); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-02.js new file mode 100644 index 000000000..b062fbc5e --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-02.js @@ -0,0 +1,35 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests that a recording that does not have a rAF cycle fails after timeout. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(NO_CANVAS_URL); + let { window, EVENTS, $, SnapshotsListView } = panel.panelWin; + + yield reload(target); + + let recordingStarted = once(window, EVENTS.SNAPSHOT_RECORDING_STARTED); + SnapshotsListView._onRecordButtonClick(); + + yield recordingStarted; + + is($("#empty-notice").hidden, true, "Empty notice not shown"); + is($("#waiting-notice").hidden, false, "Waiting notice shown"); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let recordingCancelled = once(window, EVENTS.SNAPSHOT_RECORDING_CANCELLED); + + yield promise.all([recordingFinished, recordingCancelled]); + + ok(true, "Recording stopped and was considered failed."); + + is(SnapshotsListView.itemCount, 0, "No snapshots in the list."); + is($("#empty-notice").hidden, false, "Empty notice shown"); + is($("#waiting-notice").hidden, true, "Waiting notice not shown"); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-03.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-03.js new file mode 100644 index 000000000..70948311d --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-03.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests that a recording that has a rAF cycle, but no draw calls, fails + * after timeout. + */ + +function* ifTestingSupported() { + let { target, panel } = yield initCanvasDebuggerFrontend(RAF_NO_CANVAS_URL); + let { window, EVENTS, $, SnapshotsListView } = panel.panelWin; + + yield reload(target); + + let recordingStarted = once(window, EVENTS.SNAPSHOT_RECORDING_STARTED); + SnapshotsListView._onRecordButtonClick(); + + yield recordingStarted; + + is($("#empty-notice").hidden, true, "Empty notice not shown"); + is($("#waiting-notice").hidden, false, "Waiting notice shown"); + + let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED); + let recordingCancelled = once(window, EVENTS.SNAPSHOT_RECORDING_CANCELLED); + + yield promise.all([recordingFinished, recordingCancelled]); + + ok(true, "Recording stopped and was considered failed."); + + is(SnapshotsListView.itemCount, 0, "No snapshots in the list."); + is($("#empty-notice").hidden, false, "Empty notice shown"); + is($("#waiting-notice").hidden, true, "Waiting notice not shown"); + + yield teardown(panel); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_profiling-canvas.js b/devtools/client/canvasdebugger/test/browser_profiling-canvas.js new file mode 100644 index 000000000..ede8a4dbf --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_profiling-canvas.js @@ -0,0 +1,45 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if functions inside a single animation frame are recorded and stored + * for a canvas context profiling. + */ + +function* ifTestingSupported() { + let currentTime = window.performance.now(); + let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL); + + let navigated = once(target, "navigate"); + + yield front.setup({ reload: true }); + ok(true, "The front was setup up successfully."); + + yield navigated; + ok(true, "Target automatically navigated when the front was set up."); + + let snapshotActor = yield front.recordAnimationFrame(); + ok(snapshotActor, + "A snapshot actor was sent after recording."); + + let animationOverview = yield snapshotActor.getOverview(); + ok(animationOverview, + "An animation overview could be retrieved after recording."); + + let functionCalls = animationOverview.calls; + ok(functionCalls, + "An array of function call actors was sent after recording."); + is(functionCalls.length, 8, + "The number of function call actors is correct."); + + info("Check the timestamps of function calls"); + + for (let i = 0; i < functionCalls.length - 1; i += 2) { + ok(functionCalls[i].timestamp > 0, "The timestamp of the called function is larger than 0."); + ok(functionCalls[i].timestamp < currentTime, "The timestamp has been minus the frame start time."); + ok(functionCalls[i + 1].timestamp > functionCalls[i].timestamp, "The timestamp of the called function is correct."); + } + + yield removeTab(target.tab); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/browser_profiling-webgl.js b/devtools/client/canvasdebugger/test/browser_profiling-webgl.js new file mode 100644 index 000000000..83009317f --- /dev/null +++ b/devtools/client/canvasdebugger/test/browser_profiling-webgl.js @@ -0,0 +1,91 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if functions inside a single animation frame are recorded and stored + * for a canvas context profiling. + */ + +function* ifTestingSupported() { + let currentTime = window.performance.now(); + info("Start to estimate WebGL drawArrays function."); + var { target, front } = yield initCanvasDebuggerBackend(WEBGL_DRAW_ARRAYS); + + let navigated = once(target, "navigate"); + + yield front.setup({ reload: true }); + ok(true, "The front was setup up successfully."); + + yield navigated; + ok(true, "Target automatically navigated when the front was set up."); + + let snapshotActor = yield front.recordAnimationFrame(); + ok(snapshotActor, + "A snapshot actor was sent after recording."); + + let animationOverview = yield snapshotActor.getOverview(); + ok(animationOverview, + "An animation overview could be retrieved after recording."); + + let functionCalls = animationOverview.calls; + ok(functionCalls, + "An array of function call actors was sent after recording."); + + testFunctionCallTimestamp(functionCalls, currentTime); + + info("Check triangle and vertex counts in drawArrays()"); + is(animationOverview.primitive.tris, 5, "The count of triangles is correct."); + is(animationOverview.primitive.vertices, 26, "The count of vertices is correct."); + is(animationOverview.primitive.points, 4, "The count of points is correct."); + is(animationOverview.primitive.lines, 8, "The count of lines is correct."); + + yield removeTab(target.tab); + + info("Start to estimate WebGL drawElements function."); + var { target, front } = yield initCanvasDebuggerBackend(WEBGL_DRAW_ELEMENTS); + + navigated = once(target, "navigate"); + + yield front.setup({ reload: true }); + ok(true, "The front was setup up successfully."); + + yield navigated; + ok(true, "Target automatically navigated when the front was set up."); + + snapshotActor = yield front.recordAnimationFrame(); + ok(snapshotActor, + "A snapshot actor was sent after recording."); + + animationOverview = yield snapshotActor.getOverview(); + ok(animationOverview, + "An animation overview could be retrieved after recording."); + + functionCalls = animationOverview.calls; + ok(functionCalls, + "An array of function call actors was sent after recording."); + + testFunctionCallTimestamp(functionCalls, currentTime); + + info("Check triangle and vertex counts in drawElements()"); + is(animationOverview.primitive.tris, 5, "The count of triangles is correct."); + is(animationOverview.primitive.vertices, 26, "The count of vertices is correct."); + is(animationOverview.primitive.points, 4, "The count of points is correct."); + is(animationOverview.primitive.lines, 8, "The count of lines is correct."); + + yield removeTab(target.tab); + finish(); +} + +function testFunctionCallTimestamp(functionCalls, currentTime) { + + info("Check the timestamps of function calls"); + + for ( let i = 0; i < functionCalls.length-1; i += 2 ) { + ok( functionCalls[i].timestamp > 0, "The timestamp of the called function is larger than 0." ); + ok( functionCalls[i].timestamp < currentTime, "The timestamp has been minus the frame start time." ); + ok( functionCalls[i+1].timestamp > functionCalls[i].timestamp, "The timestamp of the called function is correct." ); + } + + yield removeTab(target.tab); + finish(); +} diff --git a/devtools/client/canvasdebugger/test/doc_no-canvas.html b/devtools/client/canvasdebugger/test/doc_no-canvas.html new file mode 100644 index 000000000..a5934e3e7 --- /dev/null +++ b/devtools/client/canvasdebugger/test/doc_no-canvas.html @@ -0,0 +1,14 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <title>Canvas inspector test page</title> + </head> + + <body> + </body> + +</html> diff --git a/devtools/client/canvasdebugger/test/doc_raf-begin.html b/devtools/client/canvasdebugger/test/doc_raf-begin.html new file mode 100644 index 000000000..8727f8306 --- /dev/null +++ b/devtools/client/canvasdebugger/test/doc_raf-begin.html @@ -0,0 +1,36 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <title>Canvas inspector test page</title> + </head> + + <body> + <canvas width="128" height="128"></canvas> + + <script type="text/javascript;version=1.8"> + "use strict"; + + var ctx = document.querySelector("canvas").getContext("2d"); + + function drawRect(fill, size) { + ctx.fillStyle = fill; + ctx.fillRect(size[0], size[1], size[2], size[3]); + } + + function drawScene() { + window.requestAnimationFrame(drawScene); + ctx.clearRect(0, 0, 128, 128); + drawRect("rgb(192, 192, 192)", [0, 0, 128, 128]); + drawRect("rgba(0, 0, 192, 0.5)", [30, 30, 55, 50]); + drawRect("rgba(192, 0, 0, 0.5)", [10, 10, 55, 50]); + } + + function start () { window.requestAnimationFrame(drawScene); } + </script> + </body> + +</html> diff --git a/devtools/client/canvasdebugger/test/doc_raf-no-canvas.html b/devtools/client/canvasdebugger/test/doc_raf-no-canvas.html new file mode 100644 index 000000000..fa937623c --- /dev/null +++ b/devtools/client/canvasdebugger/test/doc_raf-no-canvas.html @@ -0,0 +1,18 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <title>Canvas inspector test page</title> + </head> + + <body> + <script> + function render () { window.requestAnimationFrame(render); } + window.requestAnimationFrame(render); + </script> + </body> + +</html> diff --git a/devtools/client/canvasdebugger/test/doc_settimeout.html b/devtools/client/canvasdebugger/test/doc_settimeout.html new file mode 100644 index 000000000..57cfbdab0 --- /dev/null +++ b/devtools/client/canvasdebugger/test/doc_settimeout.html @@ -0,0 +1,37 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <title>Canvas inspector test page</title> + </head> + + <body> + <canvas width="128" height="128"></canvas> + + <script type="text/javascript;version=1.8"> + "use strict"; + + var ctx = document.querySelector("canvas").getContext("2d"); + + function drawRect(fill, size) { + ctx.fillStyle = fill; + ctx.fillRect(size[0], size[1], size[2], size[3]); + } + + function drawScene() { + ctx.clearRect(0, 0, 128, 128); + drawRect("rgb(192, 192, 192)", [0, 0, 128, 128]); + drawRect("rgba(0, 0, 192, 0.5)", [30, 30, 55, 50]); + drawRect("rgba(192, 0, 0, 0.5)", [10, 10, 55, 50]); + + window.setTimeout(drawScene, 50); + } + + drawScene(); + </script> + </body> + +</html> diff --git a/devtools/client/canvasdebugger/test/doc_simple-canvas-bitmasks.html b/devtools/client/canvasdebugger/test/doc_simple-canvas-bitmasks.html new file mode 100644 index 000000000..bd5f67a6a --- /dev/null +++ b/devtools/client/canvasdebugger/test/doc_simple-canvas-bitmasks.html @@ -0,0 +1,34 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <title>Canvas inspector test page</title> + </head> + + <body> + <canvas width="128" height="128"></canvas> + + <script type="text/javascript;version=1.8"> + "use strict"; + + var ctx = document.querySelector("canvas").getContext("2d"); + + function drawRect(fill, size) { + ctx.fillStyle = fill; + ctx.fillRect(size[0], size[1], size[2], size[3]); + } + + function drawScene() { + ctx.clearRect(0, 0, 4, 4); + drawRect("rgb(192, 192, 192)", [0, 0, 1, 1]); + window.requestAnimationFrame(drawScene); + } + + drawScene(); + </script> + </body> + +</html> diff --git a/devtools/client/canvasdebugger/test/doc_simple-canvas-deep-stack.html b/devtools/client/canvasdebugger/test/doc_simple-canvas-deep-stack.html new file mode 100644 index 000000000..f5ecc45d6 --- /dev/null +++ b/devtools/client/canvasdebugger/test/doc_simple-canvas-deep-stack.html @@ -0,0 +1,46 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <title>Canvas inspector test page</title> + </head> + + <body> + <canvas width="128" height="128"></canvas> + + <script type="text/javascript;version=1.8"> + "use strict"; + + var ctx = document.querySelector("canvas").getContext("2d"); + + function drawRect(fill, size) { + function A() { + function B() { + function C() { + ctx.fillStyle = fill; + ctx.fillRect(size[0], size[1], size[2], size[3]); + } + C(); + } + B(); + } + A(); + } + + function drawScene() { + ctx.clearRect(0, 0, 128, 128); + drawRect("rgb(192, 192, 192)", [0, 0, 128, 128]); + drawRect("rgba(0, 0, 192, 0.5)", [30, 30, 55, 50]); + drawRect("rgba(192, 0, 0, 0.5)", [10, 10, 55, 50]); + + window.requestAnimationFrame(drawScene); + } + + drawScene(); + </script> + </body> + +</html> diff --git a/devtools/client/canvasdebugger/test/doc_simple-canvas-transparent.html b/devtools/client/canvasdebugger/test/doc_simple-canvas-transparent.html new file mode 100644 index 000000000..f8daf1e24 --- /dev/null +++ b/devtools/client/canvasdebugger/test/doc_simple-canvas-transparent.html @@ -0,0 +1,37 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <title>Canvas inspector test page</title> + </head> + + <body> + <canvas width="128" height="128"></canvas> + + <script type="text/javascript;version=1.8"> + "use strict"; + + var ctx = document.querySelector("canvas").getContext("2d"); + + function drawRect(fill, size) { + ctx.fillStyle = fill; + ctx.fillRect(size[0], size[1], size[2], size[3]); + } + + function drawScene() { + ctx.clearRect(0, 0, 128, 128); + drawRect("rgba(255, 255, 255, 0)", [0, 0, 128, 128]); + drawRect("rgba(0, 0, 192, 0.5)", [30, 30, 55, 50]); + drawRect("rgba(192, 0, 0, 0.5)", [10, 10, 55, 50]); + + window.requestAnimationFrame(drawScene); + } + + drawScene(); + </script> + </body> + +</html> diff --git a/devtools/client/canvasdebugger/test/doc_simple-canvas.html b/devtools/client/canvasdebugger/test/doc_simple-canvas.html new file mode 100644 index 000000000..4fe6b587a --- /dev/null +++ b/devtools/client/canvasdebugger/test/doc_simple-canvas.html @@ -0,0 +1,37 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <title>Canvas inspector test page</title> + </head> + + <body> + <canvas width="128" height="128"></canvas> + + <script type="text/javascript;version=1.8"> + "use strict"; + + var ctx = document.querySelector("canvas").getContext("2d"); + + function drawRect(fill, size) { + ctx.fillStyle = fill; + ctx.fillRect(size[0], size[1], size[2], size[3]); + } + + function drawScene() { + ctx.clearRect(0, 0, 128, 128); + drawRect("rgb(192, 192, 192)", [0, 0, 128, 128]); + drawRect("rgba(0, 0, 192, 0.5)", [30, 30, 55, 50]); + drawRect("rgba(192, 0, 0, 0.5)", [10, 10, 55, 50]); + + window.requestAnimationFrame(drawScene); + } + + drawScene(); + </script> + </body> + +</html> diff --git a/devtools/client/canvasdebugger/test/doc_webgl-bindings.html b/devtools/client/canvasdebugger/test/doc_webgl-bindings.html new file mode 100644 index 000000000..eb1405359 --- /dev/null +++ b/devtools/client/canvasdebugger/test/doc_webgl-bindings.html @@ -0,0 +1,61 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <title>WebGL editor test page</title> + </head> + + <body> + <canvas id="canvas" width="1024" height="1024"></canvas> + + <script type="text/javascript;version=1.8"> + "use strict"; + + let canvas, gl; + let customFramebuffer; + let customRenderbuffer; + let customTexture; + + window.onload = function() { + canvas = document.querySelector("canvas"); + gl = canvas.getContext("webgl", { preserveDrawingBuffer: true }); + gl.clearColor(1.0, 0.0, 0.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + + customFramebuffer = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, customFramebuffer); + + customRenderbuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, customRenderbuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 1024, 1024); + + customTexture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, customTexture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1024, 1024, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, customTexture, 0); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, customRenderbuffer); + + gl.viewport(128, 256, 384, 512); + gl.clearColor(0.0, 1.0, 0.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + + drawScene(); + } + + function drawScene() { + gl.clearColor(0.0, 0.0, 1.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + window.requestAnimationFrame(drawScene); + } + </script> + </body> + +</html> diff --git a/devtools/client/canvasdebugger/test/doc_webgl-drawArrays.html b/devtools/client/canvasdebugger/test/doc_webgl-drawArrays.html new file mode 100644 index 000000000..7a6aea907 --- /dev/null +++ b/devtools/client/canvasdebugger/test/doc_webgl-drawArrays.html @@ -0,0 +1,187 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <title>WebGL editor test page</title> + </head> + + <body> + <canvas id="canvas" width="128" height="128"></canvas> + <script id="shader-fs" type="x-shader/x-fragment"> + precision mediump float; + uniform vec4 mtrColor; + + void main(void) { + gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) * mtrColor; + } + </script> + <script id="shader-vs" type="x-shader/x-vertex"> + attribute vec3 aVertexPosition; + + void main(void) { + gl_PointSize = 5.0; + gl_Position = vec4(aVertexPosition, 1.0); + } + </script> + <script type="text/javascript;version=1.8"> + "use strict"; + + let canvas, gl, shaderProgram; + let triangleVertexPositionBuffer, squareVertexPositionBuffer; + + window.onload = function() { + canvas = document.querySelector("canvas"); + gl = canvas.getContext("webgl", { preserveDrawingBuffer: true }); + gl.viewportWidth = canvas.width; + gl.viewportHeight = canvas.height; + + initShaders(); + initBuffers(); + + gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); + gl.clearColor(0.0, 0.0, 0.0, 1.0); + gl.disable(gl.DEPTH_TEST); + drawScene(); + } + + function getShader(gl, id) { + var shaderScript = document.getElementById(id); + if (!shaderScript) { + return null; + } + + var str = ""; + var k = shaderScript.firstChild; + while (k) { + if (k.nodeType == 3) { + str += k.textContent; + } + k = k.nextSibling; + } + + var shader; + if (shaderScript.type == "x-shader/x-fragment") { + shader = gl.createShader(gl.FRAGMENT_SHADER); + } else if (shaderScript.type == "x-shader/x-vertex") { + shader = gl.createShader(gl.VERTEX_SHADER); + } else { + return null; + } + + gl.shaderSource(shader, str); + gl.compileShader(shader); + + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + alert(gl.getShaderInfoLog(shader)); + return null; + } + + return shader; + } + + function initShaders() { + var fragmentShader = getShader(gl, "shader-fs"); + var vertexShader = getShader(gl, "shader-vs"); + + shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + gl.linkProgram(shaderProgram); + + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + alert("Could not initialise shaders"); + } + + gl.useProgram(shaderProgram); + + shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); + shaderProgram.pMaterialColor = gl.getUniformLocation(shaderProgram, "mtrColor"); + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + } + + function initBuffers() { + // Create triangle vertex/index buffer + triangleVertexPositionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); + var vertices = [ + 0.0, 0.5, 0.0, + -0.5, -0.5, 0.0, + 0.5, -0.5, 0.0 + ]; + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); + triangleVertexPositionBuffer.itemSize = 3; + triangleVertexPositionBuffer.numItems = 3; + + // Create square vertex/index buffer + squareVertexPositionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); + vertices = [ + 0.8, 0.8, 0.0, + -0.8, 0.8, 0.0, + 0.8, -0.8, 0.0, + -0.8, -0.8, 0.0 + ]; + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); + squareVertexPositionBuffer.itemSize = 3; + squareVertexPositionBuffer.numItems = 4; + } + + function drawScene() { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + + // DrawArrays + // -------------- + // draw square - triangle strip + gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.uniform4f(shaderProgram.pMaterialColor, 1, 1, 1, 1); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems); + + // draw square - triangle fan + gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.uniform4f(shaderProgram.pMaterialColor, 0, 1, 0, 1); + gl.drawArrays(gl.TRIANGLE_FAN, 0, squareVertexPositionBuffer.numItems); + + // draw triangle + gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.uniform4f(shaderProgram.pMaterialColor, 1, 0, 0, 1); + gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems); + + // draw points + gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.uniform4f(shaderProgram.pMaterialColor, 1, 0, 1, 1); + gl.drawArrays(gl.POINTS, 0, squareVertexPositionBuffer.numItems); + + // draw lines + gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.uniform4f(shaderProgram.pMaterialColor, 0, 0, 1, 1); + gl.lineWidth(8.0); + gl.drawArrays(gl.LINES, 0, squareVertexPositionBuffer.numItems); + + // draw line strip + gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.uniform4f(shaderProgram.pMaterialColor, 0.9, 0.6, 0, 1); + gl.lineWidth(3.0); + gl.drawArrays(gl.LINE_STRIP, 0, squareVertexPositionBuffer.numItems); + + // draw line loop + gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.uniform4f(shaderProgram.pMaterialColor, 0, 1, 1, 1); + gl.lineWidth(3.0); + gl.drawArrays(gl.LINE_LOOP, 0, triangleVertexPositionBuffer.numItems); + + window.requestAnimationFrame(drawScene); + } + </script> + </body> + +</html>
\ No newline at end of file diff --git a/devtools/client/canvasdebugger/test/doc_webgl-drawElements.html b/devtools/client/canvasdebugger/test/doc_webgl-drawElements.html new file mode 100644 index 000000000..a8ba4a3e8 --- /dev/null +++ b/devtools/client/canvasdebugger/test/doc_webgl-drawElements.html @@ -0,0 +1,225 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <title>WebGL editor test page</title> + </head> + + <body> + <canvas id="canvas" width="128" height="128"></canvas> + <script id="shader-fs" type="x-shader/x-fragment"> + precision mediump float; + uniform vec4 mtrColor; + + void main(void) { + gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) * mtrColor; + } + </script> + <script id="shader-vs" type="x-shader/x-vertex"> + attribute vec3 aVertexPosition; + + void main(void) { + gl_PointSize = 5.0; + gl_Position = vec4(aVertexPosition, 1.0); + } + </script> + <script type="text/javascript;version=1.8"> + "use strict"; + + let canvas, gl, shaderProgram; + let triangleVertexPositionBuffer, squareVertexPositionBuffer; + let triangleIndexBuffer; + let squareIndexBuffer, squareStripIndexBuffer, squareFanIndexBuffer + + window.onload = function() { + canvas = document.querySelector("canvas"); + gl = canvas.getContext("webgl", { preserveDrawingBuffer: true }); + gl.viewportWidth = canvas.width; + gl.viewportHeight = canvas.height; + + initShaders(); + initBuffers(); + + gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); + gl.clearColor(0.0, 0.0, 0.0, 1.0); + gl.disable(gl.DEPTH_TEST); + drawScene(); + } + + function getShader(gl, id) { + var shaderScript = document.getElementById(id); + if (!shaderScript) { + return null; + } + + var str = ""; + var k = shaderScript.firstChild; + while (k) { + if (k.nodeType == 3) { + str += k.textContent; + } + k = k.nextSibling; + } + + var shader; + if (shaderScript.type == "x-shader/x-fragment") { + shader = gl.createShader(gl.FRAGMENT_SHADER); + } else if (shaderScript.type == "x-shader/x-vertex") { + shader = gl.createShader(gl.VERTEX_SHADER); + } else { + return null; + } + + gl.shaderSource(shader, str); + gl.compileShader(shader); + + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + alert(gl.getShaderInfoLog(shader)); + return null; + } + + return shader; + } + + function initShaders() { + var fragmentShader = getShader(gl, "shader-fs"); + var vertexShader = getShader(gl, "shader-vs"); + + shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + gl.linkProgram(shaderProgram); + + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + alert("Could not initialise shaders"); + } + + gl.useProgram(shaderProgram); + + shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); + shaderProgram.pMaterialColor = gl.getUniformLocation(shaderProgram, "mtrColor"); + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + } + + function initBuffers() { + // Create triangle vertex/index buffer + triangleVertexPositionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); + var vertices = [ + 0.0, 0.5, 0.0, + -0.5, -0.5, 0.0, + 0.5, -0.5, 0.0 + ]; + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); + triangleVertexPositionBuffer.itemSize = 3; + triangleVertexPositionBuffer.numItems = 3; + + triangleIndexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, triangleIndexBuffer); + var indices = [ + 0, 1, 2 + ]; + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); + triangleIndexBuffer.itemSize = 1; + triangleIndexBuffer.numItems = 3; + + // Create square vertex/index buffer + squareVertexPositionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); + vertices = [ + 0.8, 0.8, 0.0, + -0.8, 0.8, 0.0, + 0.8, -0.8, 0.0, + -0.8, -0.8, 0.0 + ]; + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); + squareVertexPositionBuffer.itemSize = 3; + squareVertexPositionBuffer.numItems = 4; + + squareIndexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, squareIndexBuffer); + indices = [ + 0, 1, 2, + 1, 3, 2 + ]; + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); + squareIndexBuffer.itemSize = 1; + squareIndexBuffer.numItems = 6; + + squareStripIndexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, squareStripIndexBuffer); + indices = [ + 0, 1, 2, 3 + ]; + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); + squareStripIndexBuffer.itemSize = 1; + squareStripIndexBuffer.numItems = 4; + + } + + function drawScene() { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + + // DrawElements + // -------------- + // draw triangle + gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.uniform4f(shaderProgram.pMaterialColor, 1, 1, 1, 1); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, squareIndexBuffer); + gl.drawElements(gl.TRIANGLES, squareIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0); + + // draw square - triangle strip + gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.uniform4f(shaderProgram.pMaterialColor, 0, 1, 0, 1); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, squareStripIndexBuffer); + gl.drawElements(gl.TRIANGLE_FAN, squareStripIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0); + + // draw square - triangle fan + gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.uniform4f(shaderProgram.pMaterialColor, 1, 0, 0, 1); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, triangleIndexBuffer); + gl.drawElements(gl.TRIANGLE_FAN, triangleIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0); + + // draw points + gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.uniform4f(shaderProgram.pMaterialColor, 1, 0, 1, 1); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, squareStripIndexBuffer); + gl.drawElements(gl.POINTS, squareStripIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0); + + // draw lines + gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.uniform4f(shaderProgram.pMaterialColor, 0, 0, 1, 1); + gl.lineWidth(8.0); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, squareStripIndexBuffer); + gl.drawElements(gl.LINES, squareStripIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0); + + // draw line strip + gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.uniform4f(shaderProgram.pMaterialColor, 0.9, 0.6, 0, 1); + gl.lineWidth(3.0); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, squareStripIndexBuffer); + gl.drawElements(gl.LINE_STRIP, squareStripIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0); + + // draw line loop + gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.uniform4f(shaderProgram.pMaterialColor, 0, 1, 1, 1); + gl.lineWidth(3.0); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, triangleIndexBuffer); + gl.drawElements(gl.LINE_LOOP, triangleIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0); + + window.requestAnimationFrame(drawScene); + } + </script> + </body> + +</html>
\ No newline at end of file diff --git a/devtools/client/canvasdebugger/test/doc_webgl-enum.html b/devtools/client/canvasdebugger/test/doc_webgl-enum.html new file mode 100644 index 000000000..f7f4d6d1e --- /dev/null +++ b/devtools/client/canvasdebugger/test/doc_webgl-enum.html @@ -0,0 +1,34 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <title>WebGL editor test page</title> + </head> + + <body> + <canvas id="canvas" width="128" height="128"></canvas> + + <script type="text/javascript;version=1.8"> + "use strict"; + + let canvas, gl; + + window.onload = function() { + canvas = document.querySelector("canvas"); + gl = canvas.getContext("webgl", { preserveDrawingBuffer: true }); + gl.clearColor(0.0, 0.0, 0.0, 1.0); + drawScene(); + } + + function drawScene() { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + gl.bindTexture(gl.TEXTURE_2D, null); + window.requestAnimationFrame(drawScene); + } + </script> + </body> + +</html> diff --git a/devtools/client/canvasdebugger/test/head.js b/devtools/client/canvasdebugger/test/head.js new file mode 100644 index 000000000..a718551ce --- /dev/null +++ b/devtools/client/canvasdebugger/test/head.js @@ -0,0 +1,305 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; + +var { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); +var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); + +var Services = require("Services"); +var promise = require("promise"); +var { gDevTools } = require("devtools/client/framework/devtools"); +var { DebuggerClient } = require("devtools/shared/client/main"); +var { DebuggerServer } = require("devtools/server/main"); +var { CallWatcherFront } = require("devtools/shared/fronts/call-watcher"); +var { CanvasFront } = require("devtools/shared/fronts/canvas"); +var { setTimeout } = require("sdk/timers"); +var DevToolsUtils = require("devtools/shared/DevToolsUtils"); +var flags = require("devtools/shared/flags"); +var { TargetFactory } = require("devtools/client/framework/target"); +var { Toolbox } = require("devtools/client/framework/toolbox"); +var { isWebGLSupported } = require("devtools/client/shared/webgl-utils"); +var mm = null; + +const FRAME_SCRIPT_UTILS_URL = "chrome://devtools/content/shared/frame-script-utils.js"; +const EXAMPLE_URL = "http://example.com/browser/devtools/client/canvasdebugger/test/"; +const SET_TIMEOUT_URL = EXAMPLE_URL + "doc_settimeout.html"; +const NO_CANVAS_URL = EXAMPLE_URL + "doc_no-canvas.html"; +const RAF_NO_CANVAS_URL = EXAMPLE_URL + "doc_raf-no-canvas.html"; +const SIMPLE_CANVAS_URL = EXAMPLE_URL + "doc_simple-canvas.html"; +const SIMPLE_BITMASKS_URL = EXAMPLE_URL + "doc_simple-canvas-bitmasks.html"; +const SIMPLE_CANVAS_TRANSPARENT_URL = EXAMPLE_URL + "doc_simple-canvas-transparent.html"; +const SIMPLE_CANVAS_DEEP_STACK_URL = EXAMPLE_URL + "doc_simple-canvas-deep-stack.html"; +const WEBGL_ENUM_URL = EXAMPLE_URL + "doc_webgl-enum.html"; +const WEBGL_BINDINGS_URL = EXAMPLE_URL + "doc_webgl-bindings.html"; +const WEBGL_DRAW_ARRAYS = EXAMPLE_URL + "doc_webgl-drawArrays.html"; +const WEBGL_DRAW_ELEMENTS = EXAMPLE_URL + "doc_webgl-drawElements.html"; +const RAF_BEGIN_URL = EXAMPLE_URL + "doc_raf-begin.html"; + +// Disable logging for all the tests. Both the debugger server and frontend will +// be affected by this pref. +var gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log"); +Services.prefs.setBoolPref("devtools.debugger.log", false); + +// All tests are asynchronous. +waitForExplicitFinish(); + +var gToolEnabled = Services.prefs.getBoolPref("devtools.canvasdebugger.enabled"); + +flags.testing = true; + +registerCleanupFunction(() => { + info("finish() was called, cleaning up..."); + flags.testing = false; + Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging); + Services.prefs.setBoolPref("devtools.canvasdebugger.enabled", gToolEnabled); + + // Some of yhese tests use a lot of memory due to GL contexts, so force a GC + // to help fragmentation. + info("Forcing GC after canvas debugger test."); + Cu.forceGC(); +}); + +/** + * Call manually in tests that use frame script utils after initializing + * the shader editor. Call after init but before navigating to different pages. + */ +function loadFrameScripts() { + mm = gBrowser.selectedBrowser.messageManager; + mm.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false); +} + +function addTab(aUrl, aWindow) { + info("Adding tab: " + aUrl); + + let deferred = promise.defer(); + let targetWindow = aWindow || window; + let targetBrowser = targetWindow.gBrowser; + + targetWindow.focus(); + let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl); + let linkedBrowser = tab.linkedBrowser; + + BrowserTestUtils.browserLoaded(linkedBrowser) + .then(function () { + info("Tab added and finished loading: " + aUrl); + deferred.resolve(tab); + }); + + return deferred.promise; +} + +function removeTab(aTab, aWindow) { + info("Removing tab."); + + let deferred = promise.defer(); + let targetWindow = aWindow || window; + let targetBrowser = targetWindow.gBrowser; + let tabContainer = targetBrowser.tabContainer; + + tabContainer.addEventListener("TabClose", function onClose(aEvent) { + tabContainer.removeEventListener("TabClose", onClose, false); + info("Tab removed and finished closing."); + deferred.resolve(); + }, false); + + targetBrowser.removeTab(aTab); + return deferred.promise; +} + +function handleError(aError) { + ok(false, "Got an error: " + aError.message + "\n" + aError.stack); + finish(); +} + +var gRequiresWebGL = false; + +function ifTestingSupported() { + ok(false, "You need to define a 'ifTestingSupported' function."); + finish(); +} + +function ifTestingUnsupported() { + todo(false, "Skipping test because some required functionality isn't supported."); + finish(); +} + +function test() { + let generator = isTestingSupported() ? ifTestingSupported : ifTestingUnsupported; + Task.spawn(generator).then(null, handleError); +} + +function createCanvas() { + return document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); +} + +function isTestingSupported() { + if (!gRequiresWebGL) { + info("This test does not require WebGL support."); + return true; + } + + let supported = isWebGLSupported(document); + + info("This test requires WebGL support."); + info("Apparently, WebGL is" + (supported ? "" : " not") + " supported."); + return supported; +} + +function once(aTarget, aEventName, aUseCapture = false) { + info("Waiting for event: '" + aEventName + "' on " + aTarget + "."); + + let deferred = promise.defer(); + + for (let [add, remove] of [ + ["on", "off"], // Use event emitter before DOM events for consistency + ["addEventListener", "removeEventListener"], + ["addListener", "removeListener"] + ]) { + if ((add in aTarget) && (remove in aTarget)) { + aTarget[add](aEventName, function onEvent(...aArgs) { + info("Got event: '" + aEventName + "' on " + aTarget + "."); + aTarget[remove](aEventName, onEvent, aUseCapture); + deferred.resolve(...aArgs); + }, aUseCapture); + break; + } + } + + return deferred.promise; +} + +function waitForTick() { + let deferred = promise.defer(); + executeSoon(deferred.resolve); + return deferred.promise; +} + +function navigateInHistory(aTarget, aDirection, aWaitForTargetEvent = "navigate") { + executeSoon(() => content.history[aDirection]()); + return once(aTarget, aWaitForTargetEvent); +} + +function navigate(aTarget, aUrl, aWaitForTargetEvent = "navigate") { + executeSoon(() => aTarget.activeTab.navigateTo(aUrl)); + return once(aTarget, aWaitForTargetEvent); +} + +function reload(aTarget, aWaitForTargetEvent = "navigate") { + executeSoon(() => aTarget.activeTab.reload()); + return once(aTarget, aWaitForTargetEvent); +} + +function initServer() { + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + DebuggerServer.addBrowserActors(); + } +} + +function initCallWatcherBackend(aUrl) { + info("Initializing a call watcher front."); + initServer(); + + return Task.spawn(function* () { + let tab = yield addTab(aUrl); + let target = TargetFactory.forTab(tab); + + yield target.makeRemote(); + + let front = new CallWatcherFront(target.client, target.form); + return { target, front }; + }); +} + +function initCanvasDebuggerBackend(aUrl) { + info("Initializing a canvas debugger front."); + initServer(); + + return Task.spawn(function* () { + let tab = yield addTab(aUrl); + let target = TargetFactory.forTab(tab); + + yield target.makeRemote(); + + let front = new CanvasFront(target.client, target.form); + return { target, front }; + }); +} + +function initCanvasDebuggerFrontend(aUrl) { + info("Initializing a canvas debugger pane."); + + return Task.spawn(function* () { + let tab = yield addTab(aUrl); + let target = TargetFactory.forTab(tab); + + yield target.makeRemote(); + + Services.prefs.setBoolPref("devtools.canvasdebugger.enabled", true); + let toolbox = yield gDevTools.showToolbox(target, "canvasdebugger"); + let panel = toolbox.getCurrentPanel(); + return { target, panel }; + }); +} + +function teardown({target}) { + info("Destroying the specified canvas debugger."); + + let {tab} = target; + return gDevTools.closeToolbox(target).then(() => { + removeTab(tab); + }); +} + +/** + * Takes a string `script` and evaluates it directly in the content + * in potentially a different process. + */ +function evalInDebuggee(script) { + let deferred = promise.defer(); + + if (!mm) { + throw new Error("`loadFrameScripts()` must be called when using MessageManager."); + } + + let id = generateUUID().toString(); + mm.sendAsyncMessage("devtools:test:eval", { script: script, id: id }); + mm.addMessageListener("devtools:test:eval:response", handler); + + function handler({ data }) { + if (id !== data.id) { + return; + } + + mm.removeMessageListener("devtools:test:eval:response", handler); + deferred.resolve(data.value); + } + + return deferred.promise; +} + +function getSourceActor(aSources, aURL) { + let item = aSources.getItemForAttachment(a => a.source.url === aURL); + return item ? item.value : null; +} + +/** + * Waits until a predicate returns true. + * + * @param function predicate + * Invoked once in a while until it returns true. + * @param number interval [optional] + * How often the predicate is invoked, in milliseconds. + */ +function* waitUntil(predicate, interval = 10) { + if (yield predicate()) { + return Promise.resolve(true); + } + let deferred = Promise.defer(); + setTimeout(function () { + waitUntil(predicate).then(() => deferred.resolve(true)); + }, interval); + return deferred.promise; +} |