tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

testharnessreport.js (10911B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 var W3CTest = {
      6  /**
      7   * Dictionary mapping a test URL to either the string "all", which means that
      8   * all tests in this file are expected to fail, or a dictionary mapping test
      9   * names to either the boolean |true|, or the string "debug". The former
     10   * means that this test is expected to fail in all builds, and the latter
     11   * that it is only expected to fail in debug builds.
     12   */
     13  "expectedFailures": {},
     14 
     15  /**
     16   * If set to true, we will dump the test failures to the console.
     17   */
     18  "dumpFailures": false,
     19 
     20  /**
     21   * If dumpFailures is true, this holds a structure like necessary for
     22   * expectedFailures, for ease of updating the expectations.
     23   */
     24  "failures": {},
     25 
     26  /**
     27   * List of test results, needed by TestRunner to update the UI.
     28   */
     29  "tests": [],
     30 
     31  /**
     32   * Number of unlogged passes, to stop buildbot from truncating the log.
     33   * We will print a message every MAX_COLLAPSED_MESSAGES passes.
     34   */
     35  "collapsedMessages": 0,
     36  "MAX_COLLAPSED_MESSAGES": 100,
     37 
     38  /**
     39   * Reference to the TestRunner object in the parent frame if the
     40   * test and the parent frame are same-origin. Otherwise, return
     41   * a stub object that relays method calls to the parent frame's
     42   * TestRunner via postMessage calls.
     43   */
     44  "runner": function() {
     45 
     46    /**
     47     * If true, these tests are running in cross-origin iframes
     48     */
     49    var xOrigin = function(){
     50        try {
     51            void parent.TestRunner;
     52            return false;
     53        } catch {
     54            return true;
     55        }
     56    }();
     57    if (!xOrigin) {
     58      return parent === this ? null : parent.TestRunner || parent.wrappedJSObject.TestRunner;
     59    }
     60    let documentURL = new URL(document.URL);
     61    return {
     62      currentTestURL: function() {
     63        return documentURL.searchParams.get("currentTestURL");
     64      }(),
     65      testFinished(tests) {
     66        parent.postMessage(
     67          {
     68            harnessType: "SimpleTest",
     69            command: "testFinished",
     70            applyOn: "runner",
     71            params: [tests],
     72          },
     73          "*"
     74        );
     75      },
     76      getParameterInfo() {
     77        return { closeWhenDone: documentURL.searchParams.get("closeWhenDone") };
     78      },
     79      structuredLogger: {
     80        testStatus(url, subtest, status, expected, diagnostic, stack) {
     81          parent.postMessage(
     82            {
     83              harnessType: "SimpleTest",
     84              command: "structuredLogger.testStatus",
     85              applyOn: "logger",
     86              params: [url, subtest, status, expected, diagnostic, stack],
     87            },
     88            "*"
     89          );
     90        },
     91      },
     92      expectAssertions(min, max) {
     93        parent.postMessage(
     94          {
     95            harnessType: "SimpleTest",
     96            command: "expectAssertions",
     97            applyOn: "runner",
     98            params: [min, max],
     99          },
    100          "*"
    101        );
    102      },
    103    };
    104   }(),
    105 
    106 
    107  /**
    108   * Prefixes for the error logging. Indexed first by int(todo) and second by
    109   * int(result). Also contains the test's status, and expected status.
    110   */
    111  "prefixes": [
    112    [
    113      {status: 'FAIL', expected: 'PASS', message: "TEST-UNEXPECTED-FAIL"},
    114      {status: 'PASS', expected: 'PASS', message: "TEST-PASS"}
    115    ],
    116    [
    117      {status: 'FAIL', expected: 'FAIL', message: "TEST-KNOWN-FAIL"},
    118      {status: 'PASS', expected: 'FAIL', message: "TEST-UNEXPECTED-PASS"}
    119    ]
    120  ],
    121 
    122  /**
    123   * Prefix of the path to parent of the the failures directory.
    124   */
    125  "pathprefix": "/tests/dom/imptests/",
    126 
    127  /**
    128   * Returns the URL of the current test, relative to the root W3C tests
    129   * directory. Used as a key into the expectedFailures dictionary.
    130   */
    131  "getPath": function() {
    132    var url = this.getURL();
    133    if (!url.startsWith(this.pathprefix)) {
    134      return "";
    135    }
    136    return url.substring(this.pathprefix.length);
    137  },
    138 
    139  /**
    140   * Returns the root-relative URL of the current test.
    141   */
    142  "getURL": function() {
    143    return this.runner ? this.runner.currentTestURL : location.pathname;
    144  },
    145 
    146  /**
    147   * Report the results in the tests array.
    148   */
    149  "reportResults": function() {
    150    var element = function element(aLocalName) {
    151      var xhtmlNS = "http://www.w3.org/1999/xhtml";
    152      return document.createElementNS(xhtmlNS, aLocalName);
    153    };
    154 
    155    var stylesheet = element("link");
    156    stylesheet.setAttribute("rel", "stylesheet");
    157    stylesheet.setAttribute("href", "/resources/testharness.css");
    158    var heads = document.getElementsByTagName("head");
    159    if (heads.length) {
    160      heads[0].appendChild(stylesheet);
    161    }
    162 
    163    var log = document.getElementById("log");
    164    if (!log) {
    165      return;
    166    }
    167    var section = log.appendChild(element("section"));
    168    section.id = "summary";
    169    section.appendChild(element("h2")).textContent = "Details";
    170 
    171    var table = section.appendChild(element("table"));
    172    table.id = "results";
    173 
    174    var tr = table.appendChild(element("thead")).appendChild(element("tr"));
    175    for (var header of ["Result", "Test Name", "Message"]) {
    176      tr.appendChild(element("th")).textContent = header;
    177    }
    178    var statuses = [
    179      ["Unexpected Fail", "Pass"],
    180      ["Known Fail", "Unexpected Pass"]
    181    ];
    182    var tbody = table.appendChild(element("tbody"));
    183    for (var test of this.tests) {
    184      tr = tbody.appendChild(element("tr"));
    185      tr.className = (test.result === !test.todo ? "pass" : "fail");
    186      tr.appendChild(element("td")).textContent =
    187        statuses[+test.todo][+test.result];
    188      tr.appendChild(element("td")).textContent = test.name;
    189      tr.appendChild(element("td")).textContent = test.message;
    190    }
    191  },
    192 
    193  /**
    194   * Returns a printable message based on aTest's 'name' and 'message'
    195   * properties.
    196   */
    197  "formatTestMessage": function(aTest) {
    198    return aTest.name + (aTest.message ? ": " + aTest.message : "");
    199  },
    200 
    201  /**
    202   * Lets the test runner know about a test result.
    203   */
    204  "_log": function(test) {
    205    var url = this.getURL();
    206    var message = this.formatTestMessage(test);
    207    var result = this.prefixes[+test.todo][+test.result];
    208 
    209    if (this.runner) {
    210      this.runner.structuredLogger.testStatus(url,
    211                                              test.name,
    212                                              result.status,
    213                                              result.expected,
    214                                              message);
    215    } else {
    216      var msg = result.message + " | ";
    217      if (url) {
    218        msg += url;
    219      }
    220      msg += " | " + this.formatTestMessage(test);
    221      dump(msg + "\n");
    222    }
    223  },
    224 
    225  /**
    226   * Logs a message about collapsed messages (if any), and resets the counter.
    227   */
    228  "_logCollapsedMessages": function() {
    229    if (this.collapsedMessages) {
    230      this._log({
    231        "name": document.title,
    232        "result": true,
    233        "todo": false,
    234        "message": "Elided " + this.collapsedMessages + " passes or known failures."
    235      });
    236    }
    237    this.collapsedMessages = 0;
    238  },
    239 
    240  /**
    241   * Maybe logs a result, eliding up to MAX_COLLAPSED_MESSAGES consecutive
    242   * passes.
    243   */
    244  "_maybeLog": function(test) {
    245    var success = (test.result === !test.todo);
    246    if (success && ++this.collapsedMessages < this.MAX_COLLAPSED_MESSAGES) {
    247      return;
    248    }
    249    this._logCollapsedMessages();
    250    this._log(test);
    251  },
    252 
    253  /**
    254   * Reports a test result. The argument is an object with the following
    255   * properties:
    256   *
    257   * o message (string): message to be reported
    258   * o result (boolean): whether this test failed
    259   * o todo (boolean): whether this test is expected to fail
    260   */
    261  "report": function(test) {
    262    this.tests.push(test);
    263    this._maybeLog(test);
    264  },
    265 
    266  /**
    267   * Returns true if this test is expected to fail, and false otherwise.
    268   */
    269  "_todo": function(test) {
    270    if (this.expectedFailures === "all") {
    271      return true;
    272    }
    273    var value = this.expectedFailures[test.name];
    274    return value === true || (value === "debug" && !!SpecialPowers.isDebugBuild);
    275  },
    276 
    277  /**
    278   * Callback function for testharness.js. Called when one test in a file
    279   * finishes.
    280   */
    281  "result": function(test) {
    282    var url = this.getPath();
    283    this.report({
    284      "name": test.name,
    285      "message": test.message || "",
    286      "result": test.status === test.PASS,
    287      "todo": this._todo(test)
    288    });
    289    if (this.dumpFailures && test.status !== test.PASS) {
    290      this.failures[test.name] = true;
    291    }
    292  },
    293 
    294  /**
    295   * Callback function for testharness.js. Called when the entire test file
    296   * finishes.
    297   */
    298  "finish": function(tests, status) {
    299    var url = this.getPath();
    300    this.report({
    301      "name": "Finished test",
    302      "message": "Status: " + status.status,
    303      "result": status.status === status.OK,
    304      "todo":
    305        url in this.expectedFailures &&
    306        this.expectedFailures[url] === "error"
    307    });
    308 
    309    this._logCollapsedMessages();
    310 
    311    if (this.dumpFailures) {
    312      dump("@@@ @@@ Failures\n");
    313      dump(url + "@@@" + JSON.stringify(this.failures) + "\n");
    314    }
    315    if (this.runner) {
    316      this.runner.testFinished(this.tests.map(function(aTest) {
    317        return {
    318          "message": this.formatTestMessage(aTest),
    319          "result": aTest.result,
    320          "todo": aTest.todo
    321        };
    322      }, this));
    323    } else {
    324      this.reportResults();
    325    }
    326  },
    327 
    328  /**
    329   * Log an unexpected failure. Intended to be used from harness code, not
    330   * from tests.
    331   */
    332  "logFailure": function(aTestName, aMessage) {
    333    this.report({
    334      "name": aTestName,
    335      "message": aMessage,
    336      "result": false,
    337      "todo": false
    338    });
    339  },
    340 
    341  /**
    342   * Timeout the current test. Intended to be used from harness code, not
    343   * from tests.
    344   */
    345  "timeout": async function() {
    346    this.logFailure("Timeout", "Test runner timed us out.");
    347    timeout();
    348  }
    349 };
    350 (function() {
    351  try {
    352    var path = W3CTest.getPath();
    353    if (path) {
    354      // Get expected fails.  If there aren't any, there will be a 404, which is
    355      // fine.  Anything else is unexpected.
    356      var url = W3CTest.pathprefix + "failures/" + path + ".json";
    357      var request = new XMLHttpRequest();
    358      request.open("GET", url, false);
    359      request.send();
    360      if (request.status === 200) {
    361        W3CTest.expectedFailures = JSON.parse(request.responseText);
    362      } else if (request.status !== 404) {
    363        W3CTest.logFailure("Fetching failures file", "Request status was " + request.status);
    364      }
    365    }
    366 
    367    add_result_callback(W3CTest.result.bind(W3CTest));
    368    add_completion_callback(W3CTest.finish.bind(W3CTest));
    369    setup({
    370      "output": W3CTest.runner && !W3CTest.runner.getParameterInfo().closeWhenDone,
    371      "explicit_timeout": true
    372    });
    373  } catch (e) {
    374    W3CTest.logFailure("Harness setup", "Unexpected exception: " + e);
    375  }
    376 })();