<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>window.performance User Timing measure() method is working properly</title> <link rel="author" title="Microsoft" href="http://www.microsoft.com/" /> <link rel="help" href="https://w3c.github.io/user-timing/#dom-performance-measure"/> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/common/performance-timeline-utils.js"></script> <script src="resources/webperftestharness.js"></script> <script> // test data var startMarkName = "mark_start"; var startMarkValue; var endMarkName = "mark_end"; var endMarkValue; var measures; var testThreshold = 20; // test measures var measureTestDelay = 200; var TEST_MEASURES = [ { name: "measure_no_start_no_end", startMark: undefined, endMark: undefined, startTime: undefined, duration: undefined, entryType: "measure", entryMatch: undefined, order: undefined, found: false }, { name: "measure_start_no_end", startMark: "mark_start", endMark: undefined, startTime: undefined, duration: undefined, entryType: "measure", entryMatch: undefined, order: undefined, found: false }, { name: "measure_start_end", startMark: "mark_start", endMark: "mark_end", startTime: undefined, duration: undefined, entryType: "measure", entryMatch: undefined, order: undefined, found: false }, { name: "measure_no_start_end", startMark: undefined, endMark: "mark_end", startTime: undefined, duration: undefined, entryType: "measure", entryMatch: undefined, order: undefined, found: false }, // intentional duplicate of the first measure, used to confirm names can be re-used { name: "measure_no_start_no_end", startMark: undefined, endMark: undefined, startTime: undefined, duration: undefined, entryType: "measure", entryMatch: undefined, order: undefined, found: false } ]; // the index of the duplicate "measure_no_start_no_end" const duplicate_index = TEST_MEASURES.map(m=>m.name).lastIndexOf('measure_no_start_no_end'); setup({explicit_done: true}); test_namespace(); function onload_test() { // test for existence of User Timing and Performance Timeline interface if (!has_required_interfaces()) { test_true(false, "The User Timing and Performance Timeline interfaces, which are required for this test, " + "are defined."); done(); } else { // create the start mark for the test measures window.performance.mark(startMarkName); // get the start mark's value startMarkValue = window.performance.getEntriesByName(startMarkName)[0].startTime; // create the test end mark using the test delay; this will allow for a significant difference between // the mark values that should be represented in the duration of measures using these marks step_timeout(measure_test_cb, measureTestDelay); } } function measure_test_cb() { // create the end mark for the test measures window.performance.mark(endMarkName); // get the end mark's value endMarkValue = window.performance.getEntriesByName(endMarkName)[0].startTime; // loop through all measure scenarios and create the corresponding measures for (var i in TEST_MEASURES) { var scenario = TEST_MEASURES[i]; if (scenario.startMark == undefined && scenario.endMark == undefined) { // both startMark and endMark are undefined, don't provide either parameters window.performance.measure(scenario.name); // when startMark isn't provided to the measure() call, a DOMHighResTimeStamp corresponding // to the navigationStart attribute with a timebase of the same attribute is used; this is // equivalent to 0 scenario.startTime = 0; // when endMark isn't provided to the measure() call, a DOMHighResTimeStamp corresponding to // the current time with a timebase of the navigationStart attribute is used scenario.duration = (new Date()) - window.performance.timing.navigationStart; } else if (scenario.startMark != undefined && scenario.endMark == undefined) { // only startMark is defined, provide startMark and don't provide endMark window.performance.measure(scenario.name, scenario.startMark); // when startMark is provided to the measure() call, the value of the mark whose name is // provided is used for the startMark scenario.startTime = startMarkValue; // when endMark isn't provided to the measure() call, a DOMHighResTimeStamp corresponding to // the current time with a timebase of the navigationStart attribute is used scenario.duration = window.performance.now() - startMarkValue; } else if (scenario.startMark != undefined && scenario.endMark != undefined) { // both startMark and endMark are defined, provide both parameters window.performance.measure(scenario.name, scenario.startMark, scenario.endMark); // when startMark is provided to the measure() call, the value of the mark whose name is // provided is used for the startMark scenario.startTime = startMarkValue; // when endMark is provided to the measure() call, the value of the mark whose name is // provided is used for the endMark scenario.duration = endMarkValue - startMarkValue; } else if (scenario.startMark == undefined && scenario.endMark != undefined) { // endMark is defined but startMark is undefined, provide both parameters window.performance.measure(scenario.name, scenario.startMark, scenario.endMark); // when startMark isn't provided to the measure() call, a DOMHighResTimeStamp corresponding // to the navigationStart attribute with a timebase of the same attribute is used; this is // equivalent to 0 scenario.startTime = 0; // when endMark is provided to the measure() call, the value of the mark whose name is // provided is used for the endMark scenario.duration = endMarkValue; } else { test_true(false, 'Test measure scenario unhandled'); } } // test that expected measures are returned by getEntriesByName for (var i in TEST_MEASURES) { entries = window.performance.getEntriesByName(TEST_MEASURES[i].name); // for all test measures, the test will be validate the test measure against the first entry returned // by getEntriesByName(), except for the last measure, where since it is a duplicate measure, the test // will validate it against the second entry returned by getEntriesByName() test_measure(entries[(i == duplicate_index ? 1 : 0)], "window.performance.getEntriesByName(\"" + TEST_MEASURES[i].name + "\")[" + (i == duplicate_index ? 1 : 0) + "]", TEST_MEASURES[i].name, TEST_MEASURES[i].startTime, TEST_MEASURES[i].duration); TEST_MEASURES[i].entryMatch = entries[(i == duplicate_index ? 1 : 0)]; } // test that expected measures are returned by getEntriesByName with the entryType parameter provided for (var i in TEST_MEASURES) { entries = window.performance.getEntriesByName(TEST_MEASURES[i].name, "measure"); test_true(match_entries(entries[(i == duplicate_index ? 1 : 0)], TEST_MEASURES[i].entryMatch), "window.performance.getEntriesByName(\"" + TEST_MEASURES[i].name + "\", \"measure\")[" + (i == duplicate_index ? 1 : 0) + "] returns an object containing the \"" + TEST_MEASURES[i].name + "\" measure in the correct order, and its value matches the \"" + TEST_MEASURES[i].name + "\" measure returned by window.performance.getEntriesByName(\"" + TEST_MEASURES[i].name + "\")"); } // test that expected measures are returned by getEntries entries = get_test_entries(window.performance.getEntries(), "measure"); test_measure_list(entries, "window.performance.getEntries()", TEST_MEASURES); // test that expected measures are returned by getEntriesByType entries = window.performance.getEntriesByType("measure"); test_measure_list(entries, "window.performance.getEntriesByType(\"measure\")", TEST_MEASURES); done(); } function match_entries(entry1, entry2, threshold) { if (threshold == undefined) { threshold = 0; } var pass = true; // match name pass = pass && (entry1.name == entry2.name); // match startTime pass = pass && (Math.abs(entry1.startTime - entry2.startTime) <= testThreshold); // match entryType pass = pass && (entry1.entryType == entry2.entryType); // match duration pass = pass && (Math.abs(entry1.duration - entry2.duration) <= testThreshold); return pass; } function test_measure(measureEntry, measureEntryCommand, expectedName, expectedStartTime, expectedDuration) { // test name test_true(measureEntry.name == expectedName, measureEntryCommand + ".name == \"" + expectedName + "\""); // test startTime; since for a mark, the startTime is always equal to a mark's value or the value of a // navigation timing attribute, the actual startTime should match the expected value exactly test_true(Math.abs(measureEntry.startTime - expectedStartTime) == 0, measureEntryCommand + ".startTime is correct"); // test entryType test_true(measureEntry.entryType == "measure", measureEntryCommand + ".entryType == \"measure\""); // test duration, allow for an acceptable threshold in the difference between the actual duration and the // expected value for the duration test_true(Math.abs(measureEntry.duration - expectedDuration) <= testThreshold, measureEntryCommand + ".duration is approximately correct (up to " + testThreshold + "ms difference allowed)"); } function test_measure_list(measureEntryList, measureEntryListCommand, measureScenarios) { // give all entries a "found" property that can be set to ensure it isn't tested twice for (var i in measureEntryList) { measureEntryList[i].found = false; } for (var i in measureScenarios) { measureScenarios[i].found = false; for (var j in measureEntryList) { if (match_entries(measureEntryList[j], measureScenarios[i]) && !measureEntryList[j].found) { test_true(match_entries(measureEntryList[j], measureScenarios[i].entryMatch), measureEntryListCommand + " returns an object containing the \"" + measureScenarios[i].name + "\" measure, and it's value matches the measure " + "returned by window.performance.getEntriesByName(\"" + measureScenarios[i].name + "\")[" + (i == duplicate_index ? 1 : 0) + "]."); measureEntryList[j].found = true; measureScenarios[i].found = true; break; } } if (!measureScenarios[i].found) { test_true(false, measureEntryListCommand + " returns an object containing the \"" + measureScenarios[i].name + "\" measure."); } } // verify order of output of getEntriesByType var startTimeCurr = 0; var pass = true; for (var i in measureEntryList) { if (measureEntryList[i].startTime < startTimeCurr) { pass = false; } startTimeCurr = measureEntryList[i].startTime; } test_true(pass, measureEntryListCommand + " returns an object containing all test " + "measures in order."); } function get_test_entries(entryList, entryType) { var testEntries = new Array(); // filter entryList for (var i in entryList) { if (entryList[i].entryType == entryType) { testEntries.push(entryList[i]); } } return testEntries; } </script> </head> <body onload="onload_test();"> <h1>Description</h1> <p>This test validates that the performance.measure() method is working properly. This test creates the following measures to test this method: <ul> <li>"measure_no_start_no_end": created using a measure() call without a startMark or endMark provided</li> <li>"measure_start_no_end": created using a measure() call with only the startMark provided</li> <li>"measure_start_end": created using a measure() call with both a startMark or endMark provided</li> <li>"measure_no_start_end": created using a measure() call with only the endMark provided</li> <li>"measure_no_start_no_end": duplicate of the first measure, used to confirm names can be re-used</li> </ul> After creating each measure, the existence of these measures is validated by calling performance.getEntriesByName() (both with and without the entryType parameter provided), performance.getEntriesByType(), and performance.getEntries() </p> <div id="log"></div> </body> </html>