setup({ explicit_done: true });

test(t => {
  assert_throws({name: "TypeError"}, function() {
    new PerformanceObserver();
  }, "PerformanceObserver constructor should throw TypeError if no argument is specified.");

  assert_throws({name: "TypeError"}, function() {
    new PerformanceObserver({});
  }, "PerformanceObserver constructor should throw TypeError if the argument is not a function.");
}, "Test that PerformanceObserver constructor throws exception");

test(t => {
  var observer = new PerformanceObserver(() => {
  });

  assert_throws({name: "TypeError"}, function() {
    observer.observe();
  }, "observe() should throw TypeError exception if no option specified.");

  assert_throws({name: "TypeError"}, function() {
    observer.observe({ unsupportedAttribute: "unsupported" });
  }, "obsrve() should throw TypeError exception if the option has no 'entryTypes' attribute.");

  assert_throws({name: "TypeError"}, function() {
    observer.observe({ entryTypes: [] });
  }, "obsrve() should throw TypeError exception if 'entryTypes' attribute is an empty sequence.");

  assert_throws({name: "TypeError"}, function() {
    observer.observe({ entryTypes: null });
  }, "obsrve() should throw TypeError exception if 'entryTypes' attribute is null.");

  assert_throws({name: "TypeError"}, function() {
    observer.observe({ entryTypes: ["invalid"]});
  }, "obsrve() should throw TypeError exception if 'entryTypes' attribute value is invalid.");
}, "Test that PerformanceObserver.observe throws exception");

function promiseObserve(test, options) {
  return new Promise(resolve => {
    performance.clearMarks();
    performance.clearMeasures();

    var observer = new PerformanceObserver(list => resolve(list));
    observer.observe(options);
    test.add_cleanup(() => observer.disconnect());
  });
}

promise_test(t => {
  var promise = promiseObserve(t, {entryTypes: ['mark', 'measure']});

  performance.mark("test-start");
  performance.mark("test-end");
  performance.measure("test-measure", "test-start", "test-end");

  return promise.then(list => {
    assert_equals(list.getEntries().length, 3,
      "There should be three observed entries.");

    var markEntries = list.getEntries().filter(entry => {
      return entry.entryType == "mark";
    });
    assert_array_equals(markEntries, performance.getEntriesByType("mark"),
      "Observed 'mark' entries should equal to entries obtained by getEntriesByType.");

    var measureEntries = list.getEntries().filter(entry => {
      return entry.entryType == "measure";
    });
    assert_array_equals(measureEntries, performance.getEntriesByType("measure"),
      "Observed 'measure' entries should equal to entries obtained by getEntriesByType.");
  });
}, "Test for user-timing with PerformanceObserver");

promise_test(t => {
  var promise = new Promise((resolve, reject) => {
    performance.clearMarks();
    performance.clearMeasures();

    var observer = new PerformanceObserver(list => reject(list));
    observer.observe({entryTypes: ['mark', 'measure']});
    observer.disconnect();
    t.step_timeout(resolve, 100);
  });

  performance.mark("test-start");
  performance.mark("test-end");
  performance.measure("test-measure", "test-start", "test-end");

  return promise.then(() => {
    assert_equals(performance.getEntriesByType("mark").length, 2);
    assert_equals(performance.getEntriesByType("measure").length, 1);
  }, list => {
    assert_unreached("Observer callback should never be called.");
  });

}, "Nothing should be notified after disconnecting observer");

promise_test(t => {
  var promise = promiseObserve(t, {entryTypes: ['mark']});

  performance.mark("test");

  return promise.then(list => {
    assert_array_equals(list.getEntries({"entryType": "mark"}),
                        performance.getEntriesByType("mark"),
                        "getEntries with entryType filter should return correct results.");

    assert_array_equals(list.getEntries({"name": "test"}),
                        performance.getEntriesByName("test"),
                        "getEntries with name filter should return correct results.");

    assert_array_equals(list.getEntries({"name": "test",
                                                      "entryType": "mark"}),
                        performance.getEntriesByName("test"),
                        "getEntries with name and entryType filter should return correct results.");

    assert_array_equals(list.getEntries({"name": "invalid"}),
                        [],
                        "getEntries with non-existent name filter should return an empty array.");

    assert_array_equals(list.getEntries({"name": "test",
                                                      "entryType": "measure"}),
                        [],
                        "getEntries with name filter and non-existent entryType should return an empty array.");

    assert_array_equals(list.getEntries({"name": "invalid",
                                                      "entryType": "mark"}),
                        [],
                        "getEntries with non-existent name and entryType filter should return an empty array.");

    assert_array_equals(list.getEntries({initiatorType: "xmlhttprequest"}),
                        [],
                        "getEntries with initiatorType filter should return an empty array.");
  });
}, "Test for PerformanceObserverEntryList.getEntries");

promise_test(t => {
  var promise = promiseObserve(t, {entryTypes: ['mark', 'measure']});

  performance.mark("test");
  performance.measure("test-measure", "test", "test");

  return promise.then(list => {
    assert_array_equals(list.getEntriesByType("mark"),
                        performance.getEntriesByType("mark"));
    assert_array_equals(list.getEntriesByType("measure"),
                        performance.getEntriesByType("measure"));
  });
}, "Test for PerformanceObserverEntryList.getEntriesByType");

promise_test(t => {
  var promise = promiseObserve(t, {entryTypes: ['mark', 'measure']});

  performance.mark("test");
  performance.measure("test-measure", "test", "test");

  return promise.then(list => {
    assert_array_equals(list.getEntriesByName("test"),
                        performance.getEntriesByName("test"));
    assert_array_equals(list.getEntriesByName("test-measure"),
                        performance.getEntriesByName("test-measure"));
  });
}, "Test for PerformanceObserverEntryList.getEntriesByName");

promise_test(t => {
  var promise = new Promise(resolve => {
    performance.clearMarks();
    performance.clearMeasures();

    var observer = new PerformanceObserver(list => resolve(list));
    observer.observe({entryTypes: ['mark', 'measure']});
    observer.observe({entryTypes: ['mark', 'measure']});
    t.add_cleanup(() => observer.disconnect());
  });

  performance.mark("test-start");
  performance.mark("test-end");
  performance.measure("test-measure", "test-start", "test-end");

  return promise.then(list => {
    assert_equals(list.getEntries().length, 3,
                  "Observed user timing entries should have only three entries.");
  });
}, "Test that invoking observe method twice affects nothing");

promise_test(t => {
  var promise = new Promise(resolve => {
    performance.clearMarks();
    performance.clearMeasures();

    var observer = new PerformanceObserver(list => resolve(list));
    observer.observe({entryTypes: ['mark', 'measure']});
    observer.observe({entryTypes: ['mark']});
    t.add_cleanup(() => observer.disconnect());
  });

  performance.mark("test-start");
  performance.mark("test-end");
  performance.measure("test-measure", "test-start", "test-end");

  return promise.then(list => {
    assert_equals(list.getEntries().length, 2,
                  "Observed user timing entries should have only two entries.");
  });
}, "Test that observing filter is replaced by a new filter");

promise_test(t => {
  var promise = new Promise(resolve => {
    performance.clearMarks();
    performance.clearMeasures();

    var observer = new PerformanceObserver(list => resolve(list));
    observer.observe({entryTypes: ['mark']});
    observer.observe({entryTypes: ['measure']});
    t.add_cleanup(() => observer.disconnect());
  });

  performance.mark("test-start");
  performance.mark("test-end");
  performance.measure("test-measure", "test-start", "test-end");

  return promise.then(list => {
    assert_equals(list.getEntries().length, 1,
                  "Observed user timing entries should have only 1 entries.");
  });
}, "Test that observing filter is replaced by a new filter");