summaryrefslogtreecommitdiff
path: root/toolkit/devtools/timeline/test
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/devtools/timeline/test')
-rw-r--r--toolkit/devtools/timeline/test/browser.ini21
-rw-r--r--toolkit/devtools/timeline/test/browser_timeline_aaa_run_first_leaktest.js18
-rw-r--r--toolkit/devtools/timeline/test/browser_timeline_filters.js93
-rw-r--r--toolkit/devtools/timeline/test/browser_timeline_overview-initial-selection-01.js44
-rw-r--r--toolkit/devtools/timeline/test/browser_timeline_overview-initial-selection-02.js35
-rw-r--r--toolkit/devtools/timeline/test/browser_timeline_overview-theme.js84
-rw-r--r--toolkit/devtools/timeline/test/browser_timeline_overview-update.js74
-rw-r--r--toolkit/devtools/timeline/test/browser_timeline_panels.js39
-rw-r--r--toolkit/devtools/timeline/test/browser_timeline_recording-without-memory.js33
-rw-r--r--toolkit/devtools/timeline/test/browser_timeline_recording.js45
-rw-r--r--toolkit/devtools/timeline/test/browser_timeline_waterfall-background.js44
-rw-r--r--toolkit/devtools/timeline/test/browser_timeline_waterfall-generic.js65
-rw-r--r--toolkit/devtools/timeline/test/browser_timeline_waterfall-sidebar.js58
-rw-r--r--toolkit/devtools/timeline/test/browser_timeline_waterfall-styles.js88
-rw-r--r--toolkit/devtools/timeline/test/doc_simple-test.html25
-rw-r--r--toolkit/devtools/timeline/test/head.js148
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;
+}