diff options
Diffstat (limited to 'toolkit/devtools/timeline/test')
16 files changed, 914 insertions, 0 deletions
diff --git a/toolkit/devtools/timeline/test/browser.ini b/toolkit/devtools/timeline/test/browser.ini new file mode 100644 index 000000000..6f51c8020 --- /dev/null +++ b/toolkit/devtools/timeline/test/browser.ini @@ -0,0 +1,21 @@ +[DEFAULT] +subsuite = devtools +support-files = + doc_simple-test.html + head.js + +[browser_timeline_aaa_run_first_leaktest.js] +[browser_timeline_filters.js] +[browser_timeline_overview-initial-selection-01.js] +skip-if = (os == 'mac' && debug) # bug 1139591 +[browser_timeline_overview-initial-selection-02.js] +skip-if = (os == 'mac' && debug) # bug 1139591 +[browser_timeline_overview-update.js] +[browser_timeline_overview-theme.js] +[browser_timeline_panels.js] +[browser_timeline_recording-without-memory.js] +[browser_timeline_recording.js] +[browser_timeline_waterfall-background.js] +[browser_timeline_waterfall-generic.js] +[browser_timeline_waterfall-styles.js] +[browser_timeline_waterfall-sidebar.js] diff --git a/toolkit/devtools/timeline/test/browser_timeline_aaa_run_first_leaktest.js b/toolkit/devtools/timeline/test/browser_timeline_aaa_run_first_leaktest.js new file mode 100644 index 000000000..33ecca920 --- /dev/null +++ b/toolkit/devtools/timeline/test/browser_timeline_aaa_run_first_leaktest.js @@ -0,0 +1,18 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the timeline leaks on initialization and sudden destruction. + * You can also use this initialization format as a template for other tests. + */ + +add_task(function*() { + let { target, panel } = yield initTimelinePanel(SIMPLE_URL); + + ok(target, "Should have a target available."); + ok(panel, "Should have a panel available."); + + ok(panel.panelWin.gToolbox, "Should have a toolbox reference on the panel window."); + ok(panel.panelWin.gTarget, "Should have a target reference on the panel window."); + ok(panel.panelWin.gFront, "Should have a front reference on the panel window."); +}); diff --git a/toolkit/devtools/timeline/test/browser_timeline_filters.js b/toolkit/devtools/timeline/test/browser_timeline_filters.js new file mode 100644 index 000000000..99c996a19 --- /dev/null +++ b/toolkit/devtools/timeline/test/browser_timeline_filters.js @@ -0,0 +1,93 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests markers filtering mechanism. + */ + +add_task(function*() { + let { target, panel } = yield initTimelinePanel(SIMPLE_URL); + let { $, $$, TimelineController, TimelineView } = panel.panelWin; + + yield TimelineController.toggleRecording(); + ok(true, "Recording has started."); + + yield waitUntil(() => { + // Wait until we get 3 different markers. + let markers = TimelineController.getMarkers(); + return markers.some(m => m.name == "Styles") && + markers.some(m => m.name == "Reflow") && + markers.some(m => m.name == "Paint"); + }); + + yield TimelineController.toggleRecording(); + + let overview = TimelineView.markersOverview; + let waterfall = TimelineView.waterfall; + + // Select everything + overview.setSelection({ start: 0, end: overview.width }) + + $("#filter-button").click(); + + yield waitUntil(() => !waterfall._outstandingMarkers.length); + + let menuItem1 = $("menuitem[marker-type=Styles]"); + let menuItem2 = $("menuitem[marker-type=Reflow]"); + let menuItem3 = $("menuitem[marker-type=Paint]"); + + let originalHeight = overview.fixedHeight; + + ok($(".waterfall-marker-bar[type=Styles]"), "Found at least one 'Styles' marker (1)"); + ok($(".waterfall-marker-bar[type=Reflow]"), "Found at least one 'Reflow' marker (1)"); + ok($(".waterfall-marker-bar[type=Paint]"), "Found at least one 'Paint' marker (1)"); + + let heightBefore = overview.fixedHeight; + EventUtils.synthesizeMouseAtCenter(menuItem1, {type: "mouseup"}, panel.panelWin); + yield once(menuItem1, "command"); + + yield waitUntil(() => !waterfall._outstandingMarkers.length); + + is(overview.fixedHeight, heightBefore, "Overview height hasn't changed"); + ok(!$(".waterfall-marker-bar[type=Styles]"), "No 'Styles' marker (2)"); + ok($(".waterfall-marker-bar[type=Reflow]"), "Found at least one 'Reflow' marker (2)"); + ok($(".waterfall-marker-bar[type=Paint]"), "Found at least one 'Paint' marker (2)"); + + heightBefore = overview.fixedHeight; + EventUtils.synthesizeMouseAtCenter(menuItem2, {type: "mouseup"}, panel.panelWin); + yield once(menuItem2, "command"); + + yield waitUntil(() => !waterfall._outstandingMarkers.length); + + is(overview.fixedHeight, heightBefore, "Overview height hasn't changed"); + ok(!$(".waterfall-marker-bar[type=Styles]"), "No 'Styles' marker (3)"); + ok(!$(".waterfall-marker-bar[type=Reflow]"), "No 'Reflow' marker (3)"); + ok($(".waterfall-marker-bar[type=Paint]"), "Found at least one 'Paint' marker (3)"); + + heightBefore = overview.fixedHeight; + EventUtils.synthesizeMouseAtCenter(menuItem3, {type: "mouseup"}, panel.panelWin); + yield once(menuItem3, "command"); + + yield waitUntil(() => !waterfall._outstandingMarkers.length); + + // A row is 11px. See markers-overview.js + is(overview.fixedHeight, heightBefore - 11, "Overview is smaller"); + ok(!$(".waterfall-marker-bar[type=Styles]"), "No 'Styles' marker (4)"); + ok(!$(".waterfall-marker-bar[type=Reflow]"), "No 'Reflow' marker (4)"); + ok(!$(".waterfall-marker-bar[type=Paint]"), "No 'Paint' marker (4)"); + + for (let item of [menuItem1, menuItem2, menuItem3]) { + EventUtils.synthesizeMouseAtCenter(item, {type: "mouseup"}, panel.panelWin); + yield once(item, "command"); + } + + yield waitUntil(() => !waterfall._outstandingMarkers.length); + + ok($(".waterfall-marker-bar[type=Styles]"), "Found at least one 'Styles' marker (5)"); + ok($(".waterfall-marker-bar[type=Reflow]"), "Found at least one 'Reflow' marker (5)"); + ok($(".waterfall-marker-bar[type=Paint]"), "Found at least one 'Paint' marker (5)"); + + is(overview.fixedHeight, originalHeight, "Overview restored"); + + $(".waterfall-marker-bar[type=Styles]"); +}); diff --git a/toolkit/devtools/timeline/test/browser_timeline_overview-initial-selection-01.js b/toolkit/devtools/timeline/test/browser_timeline_overview-initial-selection-01.js new file mode 100644 index 000000000..c29c05698 --- /dev/null +++ b/toolkit/devtools/timeline/test/browser_timeline_overview-initial-selection-01.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the overview has an initial selection when recording has finished + * and there is data available. + */ + +add_task(function*() { + let { target, panel } = yield initTimelinePanel(SIMPLE_URL); + let { $, EVENTS, TimelineView, TimelineController } = panel.panelWin; + let { OVERVIEW_INITIAL_SELECTION_RATIO: selectionRatio } = panel.panelWin; + + $("#memory-checkbox").checked = true; + yield TimelineController.updateMemoryRecording(); + + yield TimelineController.toggleRecording(); + ok(true, "Recording has started."); + + let updated = 0; + panel.panelWin.on(EVENTS.OVERVIEW_UPDATED, () => updated++); + + ok((yield waitUntil(() => updated > 10)), + "The overview graph was updated a bunch of times."); + ok((yield waitUntil(() => TimelineController.getMarkers().length > 0)), + "There are some markers available."); + ok((yield waitUntil(() => TimelineController.getMemory().length > 0)), + "There are some memory measurements available now."); + + yield TimelineController.toggleRecording(); + ok(true, "Recording has ended."); + + let interval = TimelineController.getInterval(); + let markers = TimelineController.getMarkers(); + let selection = TimelineView.markersOverview.getSelection(); + + is((selection.start) | 0, + (markers[0].start * TimelineView.markersOverview.dataScaleX) | 0, + "The initial selection start is correct."); + + is((selection.end - selection.start) | 0, + (selectionRatio * TimelineView.markersOverview.width) | 0, + "The initial selection end is correct."); +}); diff --git a/toolkit/devtools/timeline/test/browser_timeline_overview-initial-selection-02.js b/toolkit/devtools/timeline/test/browser_timeline_overview-initial-selection-02.js new file mode 100644 index 000000000..8a7675c0f --- /dev/null +++ b/toolkit/devtools/timeline/test/browser_timeline_overview-initial-selection-02.js @@ -0,0 +1,35 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the overview has no initial selection when recording has finished + * and there is no data available. + */ + +add_task(function*() { + let { target, panel } = yield initTimelinePanel(SIMPLE_URL); + let { $, EVENTS, TimelineView, TimelineController } = panel.panelWin; + let { OVERVIEW_INITIAL_SELECTION_RATIO: selectionRatio } = panel.panelWin; + + $("#memory-checkbox").checked = true; + yield TimelineController.updateMemoryRecording(); + + yield TimelineController.toggleRecording(); + ok(true, "Recording has started."); + + yield TimelineController._stopRecordingAndDiscardData(); + ok(true, "Recording has ended."); + + let markers = TimelineController.getMarkers(); + let memory = TimelineController.getMemory(); + let selection = TimelineView.markersOverview.getSelection(); + + is(markers.length, 0, + "There are no markers available."); + is(memory.length, 0, + "There are no memory measurements available."); + is(selection.start, null, + "The initial selection start is correct."); + is(selection.end, null, + "The initial selection end is correct."); +}); diff --git a/toolkit/devtools/timeline/test/browser_timeline_overview-theme.js b/toolkit/devtools/timeline/test/browser_timeline_overview-theme.js new file mode 100644 index 000000000..4a45bef6a --- /dev/null +++ b/toolkit/devtools/timeline/test/browser_timeline_overview-theme.js @@ -0,0 +1,84 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the markers and memory overviews render with the correct + * theme on load, and rerenders when changed. + */ + +const LIGHT_BG = "#fcfcfc"; +const DARK_BG = "#14171a"; + +add_task(function*() { + let { target, panel } = yield initTimelinePanel("about:blank"); + let { $, EVENTS, TimelineView, TimelineController } = panel.panelWin; + + $("#memory-checkbox").checked = true; + + setTheme("dark"); + + yield TimelineController.updateMemoryRecording(); + is(TimelineView.markersOverview.backgroundColor, DARK_BG, + "correct theme on load for markers."); + is(TimelineView.memoryOverview.backgroundColor, DARK_BG, + "correct theme on load for memory."); + + yield TimelineController.toggleRecording(); + ok(true, "Recording has started."); + + yield TimelineController.toggleRecording(); + ok(true, "Recording has ended."); + + let refreshed = Promise.all([ + once(TimelineView.markersOverview, "refresh"), + once(TimelineView.memoryOverview, "refresh"), + ]); + + setTheme("light"); + yield refreshed; + + ok(true, "Both memory and markers were rerendered after theme change."); + is(TimelineView.markersOverview.backgroundColor, LIGHT_BG, + "correct theme on after toggle for markers."); + is(TimelineView.memoryOverview.backgroundColor, LIGHT_BG, + "correct theme on after toggle for memory."); + + refreshed = Promise.all([ + once(TimelineView.markersOverview, "refresh"), + once(TimelineView.memoryOverview, "refresh"), + ]); + + setTheme("dark"); + yield refreshed; + + ok(true, "Both memory and markers were rerendered after theme change."); + is(TimelineView.markersOverview.backgroundColor, DARK_BG, + "correct theme on after toggle for markers once more."); + is(TimelineView.memoryOverview.backgroundColor, DARK_BG, + "correct theme on after toggle for memory once more."); + + refreshed = Promise.all([ + once(TimelineView.markersOverview, "refresh"), + once(TimelineView.memoryOverview, "refresh"), + ]); + + // Set theme back to light + setTheme("light"); + yield refreshed; +}); + +/** + * Mimics selecting the theme selector in the toolbox; + * sets the preference and emits an event on gDevTools to trigger + * the themeing. + */ +function setTheme (newTheme) { + let oldTheme = Services.prefs.getCharPref("devtools.theme"); + info("Setting `devtools.theme` to \"" + newTheme + "\""); + Services.prefs.setCharPref("devtools.theme", newTheme); + gDevTools.emit("pref-changed", { + pref: "devtools.theme", + newValue: newTheme, + oldValue: oldTheme + }); +} diff --git a/toolkit/devtools/timeline/test/browser_timeline_overview-update.js b/toolkit/devtools/timeline/test/browser_timeline_overview-update.js new file mode 100644 index 000000000..130f7db11 --- /dev/null +++ b/toolkit/devtools/timeline/test/browser_timeline_overview-update.js @@ -0,0 +1,74 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the markers and memory overviews are continuously updated. + */ + +add_task(function*() { + let { target, panel } = yield initTimelinePanel("about:blank"); + let { $, EVENTS, TimelineView, TimelineController } = panel.panelWin; + + $("#memory-checkbox").checked = true; + yield TimelineController.updateMemoryRecording(); + + yield TimelineController.toggleRecording(); + ok(true, "Recording has started."); + + ok("selectionEnabled" in TimelineView.markersOverview, + "The selection should not be enabled for the markers overview (1)."); + is(TimelineView.markersOverview.selectionEnabled, false, + "The selection should not be enabled for the markers overview (2)."); + is(TimelineView.markersOverview.hasSelection(), false, + "The markers overview shouldn't have a selection before recording."); + + ok("selectionEnabled" in TimelineView.memoryOverview, + "The selection should not be enabled for the memory overview (1)."); + is(TimelineView.memoryOverview.selectionEnabled, false, + "The selection should not be enabled for the memory overview (2)."); + is(TimelineView.memoryOverview.hasSelection(), false, + "The memory overview shouldn't have a selection before recording."); + + let updated = 0; + panel.panelWin.on(EVENTS.OVERVIEW_UPDATED, () => updated++); + + ok((yield waitUntil(() => updated > 10)), + "The overviews were updated a bunch of times."); + ok((yield waitUntil(() => TimelineController.getMemory().length > 10)), + "There are some memory measurements available now."); + + ok("selectionEnabled" in TimelineView.markersOverview, + "The selection should still not be enabled for the markers overview (3)."); + is(TimelineView.markersOverview.selectionEnabled, false, + "The selection should still not be enabled for the markers overview (4)."); + is(TimelineView.markersOverview.hasSelection(), false, + "The markers overview should not have a selection while recording."); + + ok("selectionEnabled" in TimelineView.memoryOverview, + "The selection should still not be enabled for the memory overview (3)."); + is(TimelineView.memoryOverview.selectionEnabled, false, + "The selection should still not be enabled for the memory overview (4)."); + is(TimelineView.memoryOverview.hasSelection(), false, + "The memory overview should not have a selection while recording."); + + yield TimelineController.toggleRecording(); + ok(true, "Recording has ended."); + + // TODO: Re-enable this assertion as part of bug 1120830 + // is(TimelineController.getMarkers().length, 0, + // "There are no markers available."); + isnot(TimelineController.getMemory().length, 0, + "There are some memory measurements available."); + + is(TimelineView.markersOverview.selectionEnabled, true, + "The selection should now be enabled for the markers overview."); + // TODO: Re-enable this assertion as part of bug 1120830 + // is(TimelineView.markersOverview.hasSelection(), false, + // "The markers overview should not have a selection after recording."); + + is(TimelineView.memoryOverview.selectionEnabled, true, + "The selection should now be enabled for the memory overview."); + // TODO: Re-enable this assertion as part of bug 1120830 + // is(TimelineView.memoryOverview.hasSelection(), false, + // "The memory overview should not have a selection after recording."); +}); diff --git a/toolkit/devtools/timeline/test/browser_timeline_panels.js b/toolkit/devtools/timeline/test/browser_timeline_panels.js new file mode 100644 index 000000000..f7f0de086 --- /dev/null +++ b/toolkit/devtools/timeline/test/browser_timeline_panels.js @@ -0,0 +1,39 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the timeline panels are correctly shown and hidden when + * recording starts and stops. + */ + +add_task(function*() { + let { target, panel } = yield initTimelinePanel(SIMPLE_URL); + let { $, EVENTS } = panel.panelWin; + + is($("#record-button").hasAttribute("checked"), false, + "The record button should not be checked yet."); + is($("#timeline-pane").selectedPanel, $("#empty-notice"), + "An empty notice is initially displayed instead of the waterfall view."); + + let whenRecStarted = panel.panelWin.once(EVENTS.RECORDING_STARTED); + EventUtils.synthesizeMouseAtCenter($("#record-button"), {}, panel.panelWin); + yield whenRecStarted; + + ok(true, "Recording has started."); + + is($("#record-button").getAttribute("checked"), "true", + "The record button should be checked now."); + is($("#timeline-pane").selectedPanel, $("#recording-notice"), + "A recording notice is now displayed instead of the waterfall view."); + + let whenRecEnded = panel.panelWin.once(EVENTS.RECORDING_ENDED); + EventUtils.synthesizeMouseAtCenter($("#record-button"), {}, panel.panelWin); + yield whenRecEnded; + + ok(true, "Recording has ended."); + + is($("#record-button").hasAttribute("checked"), false, + "The record button should be unchecked again."); + is($("#timeline-pane").selectedPanel, $("#timeline-waterfall-container"), + "A waterfall view is now displayed."); +}); diff --git a/toolkit/devtools/timeline/test/browser_timeline_recording-without-memory.js b/toolkit/devtools/timeline/test/browser_timeline_recording-without-memory.js new file mode 100644 index 000000000..5b8b0e0ea --- /dev/null +++ b/toolkit/devtools/timeline/test/browser_timeline_recording-without-memory.js @@ -0,0 +1,33 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the timeline actor isn't unnecessarily asked to record memory. + */ + +add_task(function*() { + let { target, panel } = yield initTimelinePanel(SIMPLE_URL); + let { $, EVENTS, TimelineView, TimelineController } = panel.panelWin; + + yield TimelineController.toggleRecording(); + ok(true, "Recording has started."); + + let updated = 0; + panel.panelWin.on(EVENTS.OVERVIEW_UPDATED, () => updated++); + + ok((yield waitUntil(() => updated > 10)), + "The overview graph was updated a bunch of times."); + ok((yield waitUntil(() => TimelineController.getMarkers().length > 0)), + "There are some markers available."); + + yield TimelineController.toggleRecording(); + ok(true, "Recording has ended."); + + let markers = TimelineController.getMarkers(); + let memory = TimelineController.getMemory(); + + isnot(markers.length, 0, + "There are some markers available."); + is(memory.length, 0, + "There are no memory measurements available."); +}); diff --git a/toolkit/devtools/timeline/test/browser_timeline_recording.js b/toolkit/devtools/timeline/test/browser_timeline_recording.js new file mode 100644 index 000000000..3cc977ff8 --- /dev/null +++ b/toolkit/devtools/timeline/test/browser_timeline_recording.js @@ -0,0 +1,45 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the timeline can properly start and stop a recording. + */ + +add_task(function*() { + let { target, panel } = yield initTimelinePanel(SIMPLE_URL); + let { $, gFront, TimelineController } = panel.panelWin; + + $("#memory-checkbox").checked = true; + yield TimelineController.updateMemoryRecording(); + + is((yield gFront.isRecording()), false, + "The timeline actor should not be recording when the tool starts."); + is(TimelineController.getMarkers().length, 0, + "There should be no markers available when the tool starts."); + is(TimelineController.getMemory().length, 0, + "There should be no memory measurements available when the tool starts."); + + yield TimelineController.toggleRecording(); + + is((yield gFront.isRecording()), true, + "The timeline actor should be recording now."); + ok((yield waitUntil(() => TimelineController.getMarkers().length > 0)), + "There are some markers available now."); + ok((yield waitUntil(() => TimelineController.getMemory().length > 0)), + "There are some memory measurements available now."); + + info("Interval: " + TimelineController.getInterval().toSource()); + info("Markers: " + TimelineController.getMarkers().toSource()); + info("Memory: " + TimelineController.getMemory().toSource()); + + ok("startTime" in TimelineController.getInterval(), + "A `startTime` field was set on the recording data."); + ok("endTime" in TimelineController.getInterval(), + "An `endTime` field was set on the recording data."); + + ok(TimelineController.getInterval().endTime > + TimelineController.getInterval().startTime, + "Some time has passed since the recording started."); + + yield TimelineController.toggleRecording(); +}); diff --git a/toolkit/devtools/timeline/test/browser_timeline_waterfall-background.js b/toolkit/devtools/timeline/test/browser_timeline_waterfall-background.js new file mode 100644 index 000000000..47c1cfba1 --- /dev/null +++ b/toolkit/devtools/timeline/test/browser_timeline_waterfall-background.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the waterfall background is a 1px high canvas stretching across + * the container bounds. + */ + +add_task(function*() { + let { target, panel } = yield initTimelinePanel(SIMPLE_URL); + let { $, EVENTS, TimelineView, TimelineController } = panel.panelWin; + + yield TimelineController.toggleRecording(); + ok(true, "Recording has started."); + + let updated = 0; + panel.panelWin.on(EVENTS.OVERVIEW_UPDATED, () => updated++); + + ok((yield waitUntil(() => updated > 0)), + "The overview graphs were updated a bunch of times."); + ok((yield waitUntil(() => TimelineController.getMarkers().length > 0)), + "There are some markers available."); + + yield TimelineController.toggleRecording(); + ok(true, "Recording has ended."); + + // Test the waterfall background. + + let parentWidth = $("#timeline-waterfall").getBoundingClientRect().width; + let waterfallWidth = TimelineView.waterfall._waterfallWidth; + let sidebarWidth = 150; // px + is(waterfallWidth, parentWidth - sidebarWidth, + "The waterfall width is correct.") + + ok(TimelineView.waterfall._canvas, + "A canvas should be created after the recording ended."); + ok(TimelineView.waterfall._ctx, + "A 2d context should be created after the recording ended."); + + is(TimelineView.waterfall._canvas.width, waterfallWidth, + "The canvas width is correct."); + is(TimelineView.waterfall._canvas.height, 1, + "The canvas height is correct."); +}); diff --git a/toolkit/devtools/timeline/test/browser_timeline_waterfall-generic.js b/toolkit/devtools/timeline/test/browser_timeline_waterfall-generic.js new file mode 100644 index 000000000..ceff2bd1f --- /dev/null +++ b/toolkit/devtools/timeline/test/browser_timeline_waterfall-generic.js @@ -0,0 +1,65 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the waterfall is properly built after finishing a recording. + */ + +add_task(function*() { + let { target, panel } = yield initTimelinePanel(SIMPLE_URL); + let { $, $$, EVENTS, TimelineController } = panel.panelWin; + + yield TimelineController.toggleRecording(); + ok(true, "Recording has started."); + + let updated = 0; + panel.panelWin.on(EVENTS.OVERVIEW_UPDATED, () => updated++); + + ok((yield waitUntil(() => updated > 0)), + "The overview graphs were updated a bunch of times."); + ok((yield waitUntil(() => TimelineController.getMarkers().length > 0)), + "There are some markers available."); + + yield TimelineController.toggleRecording(); + ok(true, "Recording has ended."); + + // Test the header container. + + ok($(".waterfall-header-container"), + "A header container should have been created."); + + // Test the header sidebar (left). + + ok($(".waterfall-header-container > .waterfall-sidebar"), + "A header sidebar node should have been created."); + ok($(".waterfall-header-container > .waterfall-sidebar > .waterfall-header-name"), + "A header name label should have been created inside the sidebar."); + + // Test the header ticks (right). + + ok($(".waterfall-header-ticks"), + "A header ticks node should have been created."); + ok($$(".waterfall-header-ticks > .waterfall-header-tick").length > 0, + "Some header tick labels should have been created inside the tick node."); + + // Test the markers container. + + ok($(".waterfall-marker-container"), + "A marker container should have been created."); + + // Test the markers sidebar (left). + + ok($$(".waterfall-marker-container > .waterfall-sidebar").length, + "Some marker sidebar nodes should have been created."); + ok($$(".waterfall-marker-container > .waterfall-sidebar:not(spacer) > .waterfall-marker-bullet").length, + "Some marker color bullets should have been created inside the sidebar."); + ok($$(".waterfall-marker-container > .waterfall-sidebar:not(spacer) > .waterfall-marker-name").length, + "Some marker name labels should have been created inside the sidebar."); + + // Test the markers waterfall (right). + + ok($$(".waterfall-marker-item").length, + "Some marker waterfall nodes should have been created."); + ok($$(".waterfall-marker-item:not(spacer) > .waterfall-marker-bar").length, + "Some marker color bars should have been created inside the waterfall."); +}); diff --git a/toolkit/devtools/timeline/test/browser_timeline_waterfall-sidebar.js b/toolkit/devtools/timeline/test/browser_timeline_waterfall-sidebar.js new file mode 100644 index 000000000..0c5564982 --- /dev/null +++ b/toolkit/devtools/timeline/test/browser_timeline_waterfall-sidebar.js @@ -0,0 +1,58 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the sidebar is properly updated when a marker is selected. + */ + +add_task(function*() { + let { target, panel } = yield initTimelinePanel(SIMPLE_URL); + let { $, $$, EVENTS, TimelineController, TimelineView, TIMELINE_BLUEPRINT} = panel.panelWin; + let { L10N } = devtools.require("devtools/shared/timeline/global"); + + yield TimelineController.toggleRecording(); + ok(true, "Recording has started."); + + yield waitUntil(() => { + // Wait until we get 3 different markers. + let markers = TimelineController.getMarkers(); + return markers.some(m => m.name == "Styles") && + markers.some(m => m.name == "Reflow") && + markers.some(m => m.name == "Paint"); + }); + + yield TimelineController.toggleRecording(); + ok(true, "Recording has ended."); + + // Select everything + TimelineView.markersOverview.setSelection({ start: 0, end: TimelineView.markersOverview.width }) + + + let bars = $$(".waterfall-marker-item:not(spacer) > .waterfall-marker-bar"); + let markers = TimelineController.getMarkers(); + + ok(bars.length > 2, "got at least 3 markers"); + + let sidebar = $("#timeline-waterfall-details"); + for (let i = 0; i < bars.length; i++) { + let bar = bars[i]; + bar.click(); + let m = markers[i]; + + let name = TIMELINE_BLUEPRINT[m.name].label; + + is($("#timeline-waterfall-details .marker-details-type").getAttribute("value"), name, + "sidebar title matches markers name"); + + let printedStartTime = $(".marker-details-start .marker-details-labelvalue").getAttribute("value"); + let printedEndTime = $(".marker-details-end .marker-details-labelvalue").getAttribute("value"); + let printedDuration= $(".marker-details-duration .marker-details-labelvalue").getAttribute("value"); + + let toMs = ms => L10N.getFormatStrWithNumbers("timeline.tick", ms); + + // Values are rounded. We don't use a strict equality. + is(toMs(m.start), printedStartTime, "sidebar start time is valid"); + is(toMs(m.end), printedEndTime, "sidebar end time is valid"); + is(toMs(m.end - m.start), printedDuration, "sidebar duration is valid"); + } +}); diff --git a/toolkit/devtools/timeline/test/browser_timeline_waterfall-styles.js b/toolkit/devtools/timeline/test/browser_timeline_waterfall-styles.js new file mode 100644 index 000000000..35ab9ae7f --- /dev/null +++ b/toolkit/devtools/timeline/test/browser_timeline_waterfall-styles.js @@ -0,0 +1,88 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if the waterfall is properly built after making a selection + * and the child nodes are styled correctly. + */ + +var gRGB_TO_HSL = { + "rgb(193, 132, 214)": "hsl(285,50%,68%)", + "rgb(152, 61, 183)": "hsl(285,50%,48%)", + "rgb(161, 223, 138)": "hsl(104,57%,71%)", + "rgb(96, 201, 58)": "hsl(104,57%,51%)", + "rgb(240, 195, 111)": "hsl(39,82%,69%)", + "rgb(227, 155, 22)": "hsl(39,82%,49%)", + "rgb(204, 204, 204)": "hsl(0,0%,80%)", + "rgb(153, 153, 153)": "hsl(0,0%,60%)", +}; + +add_task(function*() { + let { target, panel } = yield initTimelinePanel(SIMPLE_URL); + let { TIMELINE_BLUEPRINT } = devtools.require("devtools/shared/timeline/global"); + let { $, $$, EVENTS, TimelineController } = panel.panelWin; + + yield TimelineController.toggleRecording(); + ok(true, "Recording has started."); + + let updated = 0; + panel.panelWin.on(EVENTS.OVERVIEW_UPDATED, () => updated++); + + ok((yield waitUntil(() => updated > 0)), + "The overview graphs were updated a bunch of times."); + ok((yield waitUntil(() => TimelineController.getMarkers().length > 0)), + "There are some markers available."); + + yield TimelineController.toggleRecording(); + ok(true, "Recording has ended."); + + // Test the table sidebars. + + for (let sidebar of [ + ...$$(".timeline-header-sidebar"), + ...$$(".timeline-marker-sidebar") + ]) { + is(sidebar.getAttribute("width"), "150", + "The table's sidebar width is correct."); + } + + // Test the table ticks. + + for (let tick of $$(".timeline-header-tick")) { + ok(tick.getAttribute("value").match(/^\d+ ms$/), + "The table's timeline ticks appear to have correct labels."); + ok(tick.style.transform.match(/^translateX\(.*px\)$/), + "The table's timeline ticks appear to have proper translations."); + } + + // Test the marker bullets. + + for (let bullet of $$(".timeline-marker-bullet")) { + let type = bullet.getAttribute("type"); + + ok(type in TIMELINE_BLUEPRINT, + "The bullet type is present in the timeline blueprint."); + is(gRGB_TO_HSL[bullet.style.backgroundColor], TIMELINE_BLUEPRINT[type].fill, + "The bullet's background color is correct."); + is(gRGB_TO_HSL[bullet.style.borderColor], TIMELINE_BLUEPRINT[type].stroke, + "The bullet's border color is correct."); + } + + // Test the marker bars. + + for (let bar of $$(".timeline-marker-bar")) { + let type = bar.getAttribute("type"); + + ok(type in TIMELINE_BLUEPRINT, + "The bar type is present in the timeline blueprint."); + is(gRGB_TO_HSL[bar.style.backgroundColor], TIMELINE_BLUEPRINT[type].fill, + "The bar's background color is correct."); + is(gRGB_TO_HSL[bar.style.borderColor], TIMELINE_BLUEPRINT[type].stroke, + "The bar's border color is correct."); + + ok(bar.getAttribute("width") > 0, + "The bar appears to have a proper width."); + ok(bar.style.transform.match(/^translateX\(.*px\)$/), + "The bar appears to have proper translations."); + } +}); diff --git a/toolkit/devtools/timeline/test/doc_simple-test.html b/toolkit/devtools/timeline/test/doc_simple-test.html new file mode 100644 index 000000000..d038c46a7 --- /dev/null +++ b/toolkit/devtools/timeline/test/doc_simple-test.html @@ -0,0 +1,25 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!doctype html> + +<html> + <head> + <meta charset="utf-8"/> + <title>Timeline test page</title> + </head> + + <body> + <script type="text/javascript"> + var x = 1; + function test() { + document.body.style.borderTop = x + "px solid red"; + x = 1^x; + document.body.innerHeight; // flush pending reflows + } + + // Prevent this script from being garbage collected. + window.setInterval(test, 1); + </script> + </body> + +</html> diff --git a/toolkit/devtools/timeline/test/head.js b/toolkit/devtools/timeline/test/head.js new file mode 100644 index 000000000..74b185aa3 --- /dev/null +++ b/toolkit/devtools/timeline/test/head.js @@ -0,0 +1,148 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; + +let { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); + +// Disable logging for all the tests. Both the debugger server and frontend will +// be affected by this pref. +let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log"); +Services.prefs.setBoolPref("devtools.debugger.log", false); + +// Enable the tool while testing. +let gToolEnabled = Services.prefs.getBoolPref("devtools.timeline.enabled"); +Services.prefs.setBoolPref("devtools.timeline.enabled", true); + +let { Task } = Cu.import("resource://gre/modules/Task.jsm", {}); +let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {}); +let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {}); +let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}); +let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); + +let TargetFactory = devtools.TargetFactory; +let Toolbox = devtools.Toolbox; + +const EXAMPLE_URL = "http://example.com/browser/browser/devtools/timeline/test/"; +const SIMPLE_URL = EXAMPLE_URL + "doc_simple-test.html"; + +// All tests are asynchronous. +waitForExplicitFinish(); + +registerCleanupFunction(() => { + info("finish() was called, cleaning up..."); + Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging); + Services.prefs.setBoolPref("devtools.timeline.enabled", gToolEnabled); +}); + +// Close the toolbox and all opened tabs automatically. +registerCleanupFunction(function*() { + let target = TargetFactory.forTab(gBrowser.selectedTab); + yield gDevTools.closeToolbox(target); + + while (gBrowser.tabs.length > 1) { + gBrowser.removeCurrentTab(); + } +}); + +function addTab(url) { + info("Adding tab: " + url); + + let deferred = promise.defer(); + let tab = gBrowser.selectedTab = gBrowser.addTab(url); + let linkedBrowser = tab.linkedBrowser; + + linkedBrowser.addEventListener("load", function onLoad() { + linkedBrowser.removeEventListener("load", onLoad, true); + info("Tab added and finished loading: " + url); + deferred.resolve(tab); + }, true); + + return deferred.promise; +} + +/** + * Spawns a new tab and starts up a toolbox with the timeline panel + * automatically selected. + * + * Must be used within a task. + * + * @param string url + * The location of the new tab to spawn. + * @return object + * A promise resolved once the timeline is initialized, with the + * {target, panel} instances. + */ +function* initTimelinePanel(url) { + info("Initializing a timeline pane."); + + let tab = yield addTab(url); + let target = TargetFactory.forTab(tab); + + yield target.makeRemote(); + + let toolbox = yield gDevTools.showToolbox(target, "timeline"); + let panel = toolbox.getCurrentPanel(); + return { target, panel }; +} + +/** + * 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 (predicate()) { + return promise.resolve(true); + } + let deferred = promise.defer(); + setTimeout(function() { + waitUntil(predicate).then(() => deferred.resolve(true)); + }, interval); + return deferred.promise; + +} + +/** + * Wait until next tick. + */ +function nextTick() { + let def = promise.defer(); + executeSoon(() => def.resolve()) + return def.promise; +} + +/** + * Wait for eventName on target. + * @param {Object} target An observable object that either supports on/off or + * addEventListener/removeEventListener + * @param {String} eventName + * @param {Boolean} useCapture Optional, for addEventListener/removeEventListener + * @return A promise that resolves when the event has been handled + */ +function once(target, eventName, useCapture=false) { + info("Waiting for event: '" + eventName + "' on " + target + "."); + + let deferred = promise.defer(); + + for (let [add, remove] of [ + ["addEventListener", "removeEventListener"], + ["addListener", "removeListener"], + ["on", "off"] + ]) { + if ((add in target) && (remove in target)) { + target[add](eventName, function onEvent(...aArgs) { + info("Got event: '" + eventName + "' on " + target + "."); + target[remove](eventName, onEvent, useCapture); + deferred.resolve.apply(deferred, aArgs); + }, useCapture); + break; + } + } + + return deferred.promise; +} |