[UserTiming] Move UserTiming L3 tests to WPT

In preparation for shipping, move tests to WPT. Remove the Virtual Test
Suite.

Bug: 758385
Change-Id: Idc864521d35fce9e4ee4c44f2b3d32130fc88053
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1550692
Commit-Queue: Nicolás Peña Moreno <npm@chromium.org>
Reviewed-by: Liquan (Max) Gu <maxlg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#647406}
diff --git a/user-timing/mark-errors.html b/user-timing/mark-errors.html
new file mode 100644
index 0000000..c182a39
--- /dev/null
+++ b/user-timing/mark-errors.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>UserTimingL3: errors are thrown when mark() is called incorrectly.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<h1>UserTimingL3: Mark</h1>
+<script>
+  test(function() {
+    assert_throws(new TypeError(), function() { self.performance.mark("mark1", 123); }, "Number passed as a dict argument should cause type-error.")
+  }, "Number should be rejected as the mark-options.")
+
+  test(function() {
+    assert_throws(new TypeError(), function() { self.performance.mark("mark1", NaN); }, "NaN passed as a dict argument should cause type-error.")
+  }, "NaN should be rejected as the mark-options.")
+
+  test(function() {
+    assert_throws(new TypeError(), function() { self.performance.mark("mark1", Infinity); }, "Infinity passed as a dict argument should cause type-error.")
+  }, "Infinity should be rejected as the mark-options.")
+
+  test(function() {
+    assert_throws(new TypeError(), function() { self.performance.mark("mark1", "string"); }, "String passed as a dict argument should cause type-error.")
+  }, "String should be rejected as the mark-options.")
+</script>
diff --git a/user-timing/mark-l3.html b/user-timing/mark-l3.html
new file mode 100644
index 0000000..429768d
--- /dev/null
+++ b/user-timing/mark-l3.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>User Timing L3: mark</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/user-timing-helpers.js"></script>
+<script>
+  async_test(function (t) {
+    let mark_entries = [];
+    const expected_entries =
+        [{ entryType: "mark", name: "mark1", detail: null},
+        { entryType: "mark", name: "mark2", detail: null},
+        { entryType: "mark", name: "mark3", detail: null},
+        { entryType: "mark", name: "mark4", detail: null},
+        { entryType: "mark", name: "mark5", detail: null},
+        { entryType: "mark", name: "mark6", detail: {}},
+        { entryType: "mark", name: "mark7", detail: {info: 'abc'}},
+        { entryType: "mark", name: "mark8", detail: null, startTime: 234.56},
+        { entryType: "mark", name: "mark9", detail: {count: 3}, startTime: 345.67}];
+    const observer = new PerformanceObserver(
+        t.step_func(function (entryList, obs) {
+          mark_entries =
+            mark_entries.concat(entryList.getEntries());
+          if (mark_entries.length >= expected_entries.length) {
+            checkEntries(mark_entries, expected_entries);
+            observer.disconnect();
+            t.done();
+          }
+        })
+      );
+    self.performance.clearMarks();
+    observer.observe({entryTypes: ["mark"]});
+    const returned_entries = [];
+    returned_entries.push(self.performance.mark("mark1"));
+    returned_entries.push(self.performance.mark("mark2", undefined));
+    returned_entries.push(self.performance.mark("mark3", null));
+    returned_entries.push(self.performance.mark("mark4", {}));
+    returned_entries.push(self.performance.mark("mark5", {detail: null}));
+    returned_entries.push(self.performance.mark("mark6", {detail: {}}));
+    returned_entries.push(self.performance.mark("mark7", {detail: {info: 'abc'}}));
+    returned_entries.push(self.performance.mark("mark8", {startTime: 234.56}));
+    returned_entries.push(self.performance.mark("mark9", {detail: {count: 3}, startTime: 345.67}));
+    checkEntries(returned_entries, expected_entries);
+  }, "mark entries' detail and startTime are customizable.");
+</script>
diff --git a/user-timing/mark-measure-return-objects.html b/user-timing/mark-measure-return-objects.html
new file mode 100644
index 0000000..d2d8cc3
--- /dev/null
+++ b/user-timing/mark-measure-return-objects.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>UserTiming L3: mark/measure methods return objects.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p>Custom User Timing: L3 API returns a mark/measure object</p>
+<div id="log"></div>
+<script>
+  async_test(function (t) {
+    self.performance.clearMeasures();
+    const measure = self.performance.measure("measure1");
+    assert_true(measure instanceof PerformanceMeasure);
+    t.done();
+  }, "L3: performance.measure(name) should return an entry.");
+
+  async_test(function (t) {
+    self.performance.clearMeasures();
+    const measure = self.performance.measure("measure2",
+        { startTime: 12, endTime:23 });
+    assert_true(measure instanceof PerformanceMeasure);
+    t.done();
+  }, "L3: performance.measure(name, param1) should return an entry.");
+
+  async_test(function (t) {
+    self.performance.clearMeasures();
+    self.performance.mark("1");
+    self.performance.mark("2");
+    const measure = self.performance.measure("measure3", "1", "2");
+    assert_true(measure instanceof PerformanceMeasure);
+    t.done();
+  }, "L3: performance.measure(name, param1, param2) should return an entry.");
+
+  async_test(function (t) {
+    self.performance.clearMarks();
+    const mark = self.performance.mark("mark1");
+    assert_true(mark instanceof PerformanceMark);
+    t.done();
+  }, "L3: performance.mark(name) should return an entry.");
+
+  async_test(function (t) {
+    self.performance.clearMarks();
+    const mark = self.performance.mark("mark2", { startTime: 34 });
+    assert_true(mark instanceof PerformanceMark);
+    t.done();
+  }, "L3: performance.mark(name, param) should return an entry.");
+</script>
diff --git a/user-timing/measure-l3.html b/user-timing/measure-l3.html
new file mode 100644
index 0000000..0e8dacf
--- /dev/null
+++ b/user-timing/measure-l3.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>UserTiming L3: Measure basic usage</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  function endTime(entry) {
+    return entry.startTime + entry.duration;
+  }
+
+  test(function() {
+    performance.clearMarks();
+    performance.clearMeasures();
+    const markEntry = performance.mark("mark", {startTime: 123});
+    const measureEntry = performance.measure("A", undefined, "mark");
+    assert_equals(measureEntry.startTime, 0);
+    assert_equals(endTime(measureEntry), markEntry.startTime);
+  }, "When the end mark is given and the start is unprovided, the end time of the measure entry should be the end mark's time, the start time should be 0.");
+
+  test(function() {
+    performance.clearMarks();
+    performance.clearMeasures();
+    const markEntry = performance.mark("mark", {startTime: 123});
+    const endMin = performance.now();
+    const measureEntry = performance.measure("A", "mark", undefined);
+    const endMax = performance.now();
+    assert_equals(measureEntry.startTime, markEntry.startTime);
+    assert_greater_than_equal(endTime(measureEntry), endMin);
+    assert_greater_than_equal(endMax, endTime(measureEntry));
+  }, "When the start mark is given and the end is unprovided, the start time of the measure entry should be the start mark's time, the end should be now.");
+
+  test(function() {
+    performance.clearMarks();
+    performance.clearMeasures();
+    const markEntry = performance.mark("mark", {startTime: 123});
+    const measureEntry = performance.measure("A", "mark", "mark");
+    assert_equals(endTime(measureEntry), markEntry.startTime);
+    assert_equals(measureEntry.startTime, markEntry.startTime);
+  }, "When start and end mark are both given, the start time and end time of the measure entry should be the the marks' time, repectively");
+</script>
diff --git a/user-timing/measure-with-dict.html b/user-timing/measure-with-dict.html
new file mode 100644
index 0000000..83990a6
--- /dev/null
+++ b/user-timing/measure-with-dict.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>User Timing L3: measure is customizable</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/user-timing-helpers.js"></script>
+<script>
+  async_test(function (t) {
+    let measureEntries = [];
+    const timeStamp1 = 784.4;
+    const timeStamp2 = 1234.5;
+    const timeStamp3 = 66.6;
+    const timeStamp4 = 5566;
+    const expectedEntries =
+        [{ entryType: "measure", name: "measure1", detail: null, startTime: 0 },
+        { entryType: "measure", name: "measure2", detail: null, startTime: 0 },
+        { entryType: "measure", name: "measure3", detail: null, startTime: 0 },
+        { entryType: "measure", name: "measure5", detail: null },
+        { entryType: "measure", name: "measure7", detail: null, startTime: 0 },
+        { entryType: "measure", name: "measure11", detail: null, startTime: timeStamp1 },
+        { entryType: "measure", name: "measure13", detail: null, startTime: timeStamp1, duration: timeStamp2 - timeStamp1 },
+        { entryType: "measure", name: "measure14", detail: null, startTime: 0 },
+        { entryType: "measure", name: "measure15", detail: null, startTime: 0 },
+        { entryType: "measure", name: "measure16", detail: null, startTime: 0 },
+        { entryType: "measure", name: "measure17", detail: null, startTime: timeStamp1 },
+        { entryType: "measure", name: "measure18", detail: null, startTime: timeStamp3 },
+        { entryType: "measure", name: "measure19", detail: null, startTime: 0 },
+        { entryType: "measure", name: "measure20", detail: null, startTime: 0 },
+        { entryType: "measure", name: "measure21", detail: null, startTime: timeStamp3, duration: timeStamp1 - timeStamp3 },
+        { entryType: "measure", name: "measure22", detail: null, startTime: timeStamp1, duration: timeStamp2 - timeStamp1 },
+        { entryType: "measure", name: "measure23", detail: null, startTime: timeStamp1 },
+        { entryType: "measure", name: "measure24", detail: {}, startTime: 0 },
+        { entryType: "measure", name: "measure25", detail: { customInfo: 159 }, startTime: timeStamp3, duration: timeStamp2 - timeStamp3 }];
+    const observer = new PerformanceObserver(
+        t.step_func(function (entryList, obs) {
+          measureEntries =
+            measureEntries.concat(entryList.getEntries());
+          if (measureEntries.length >= expectedEntries.length) {
+            checkEntries(measureEntries, expectedEntries);
+            observer.disconnect();
+            t.done();
+          }
+        })
+      );
+    self.performance.clearMarks();
+    self.performance.clearMeasures();
+    observer.observe({ entryTypes: ["measure"] });
+    self.performance.mark("mark1", { detail: { randomInfo: 3 }, startTime: timeStamp1 });
+    self.performance.mark("mark2", { startTime: timeStamp2 });
+
+    const returnedEntries = [];
+    returnedEntries.push(self.performance.measure("measure1"));
+    returnedEntries.push(self.performance.measure("measure2", undefined));
+    returnedEntries.push(self.performance.measure("measure3", null));
+    returnedEntries.push(self.performance.measure("measure5", 'mark1'));
+    returnedEntries.push(
+        self.performance.measure("measure7", null, 'mark1'));
+    returnedEntries.push(
+        self.performance.measure("measure11", 'mark1', undefined));
+    returnedEntries.push(
+        self.performance.measure("measure13", 'mark1', 'mark2'));
+    returnedEntries.push(
+        self.performance.measure("measure14", {}));
+    returnedEntries.push(
+        self.performance.measure("measure15", { startTime: null }));
+    returnedEntries.push(
+        self.performance.measure("measure16", { startTime: undefined }));
+    returnedEntries.push(
+        self.performance.measure("measure17", { startTime: 'mark1' }));
+    returnedEntries.push(
+        self.performance.measure("measure18", { startTime: timeStamp3 }));
+    returnedEntries.push(
+        self.performance.measure("measure19", { endTime: undefined }));
+    returnedEntries.push(
+        self.performance.measure("measure20", { endTime: 'mark1' }));
+    returnedEntries.push(
+        self.performance.measure("measure21", { startTime: timeStamp3, endTime: 'mark1' }));
+    returnedEntries.push(
+        self.performance.measure("measure22", { startTime: timeStamp1, endTime: timeStamp2, detail: undefined }));
+    returnedEntries.push(
+        self.performance.measure("measure23", { startTime: 'mark1', endTime: undefined, detail: null }));
+    returnedEntries.push(
+        self.performance.measure("measure24", { startTime: null, endTime: timeStamp1, detail: {} }));
+    returnedEntries.push(
+        self.performance.measure("measure25", { startTime: timeStamp3, endTime: 'mark2', detail: { customInfo: 159 }}));
+    checkEntries(returnedEntries, expectedEntries);
+  }, "measure entries' detail and start/end are customizable");
+
+  async_test(function (t) {
+    assert_throws("SyntaxError", function() {
+      self.performance.measure("wrongUsage1", {}, 12);
+    });
+    t.done();
+  }, "measure should throw exception when passing option object and end at the same time");
+</script>
diff --git a/user-timing/resources/user-timing-helper.js b/user-timing/resources/user-timing-helper.js
new file mode 100644
index 0000000..8d43768
--- /dev/null
+++ b/user-timing/resources/user-timing-helper.js
@@ -0,0 +1,30 @@
+// Compares a list of performance entries to a predefined one.
+// actualEntries is an array of performance entries from the user agent,
+// and expectedEntries is an array of performance entries minted by the test.
+// The comparison doesn't assert the order of the entries.
+function checkEntries(actualEntries, expectedEntries) {
+  assert_equals(actualEntries.length, expectedEntries.length,
+      `The length of actual and expected entries should match.
+      actual: ${JSON.stringify(actualEntries)},
+      expected: ${JSON.stringify(expectedEntries)}`);
+  const actualEntrySet = new Set(actualEntries.map(ae=>ae.name));
+  assert_equals(actualEntrySet.size, actualEntries.length, `Actual entry names are not unique: ${JSON.stringify(actualEntries)}`);
+  const expectedEntrySet = new Set(expectedEntries.map(ee=>ee.name));
+  assert_equals(expectedEntrySet.size, expectedEntries.length, `Expected entry names are not unique: ${JSON.stringify(expectedEntries)}`);
+  actualEntries.forEach(ae=>{
+    const expectedEntry = expectedEntries.find(e=>e.name === ae.name);
+    assert_true(!!expectedEntry, `Entry name '${ae.name}' was not found.`);
+    checkEntry(ae, expectedEntry);
+  });
+}
+
+function checkEntry(entry, {name, entryType, startTime, detail, duration}) {
+  assert_equals(entry.name, name);
+  assert_equals(entry.entryType, entryType);
+  if (startTime !== undefined)
+    assert_equals(entry.startTime, startTime);
+  if (detail !== undefined)
+    assert_equals(JSON.stringify(entry.detail), JSON.stringify(detail));
+  if (duration !== undefined)
+    assert_equals(entry.duration, duration);
+}