summaryrefslogtreecommitdiffstats
path: root/devtools/shared/webconsole/test/unit
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/shared/webconsole/test/unit')
-rw-r--r--devtools/shared/webconsole/test/unit/.eslintrc.js6
-rw-r--r--devtools/shared/webconsole/test/unit/test_js_property_provider.js170
-rw-r--r--devtools/shared/webconsole/test/unit/test_network_helper.js47
-rw-r--r--devtools/shared/webconsole/test/unit/test_security-info-certificate.js68
-rw-r--r--devtools/shared/webconsole/test/unit/test_security-info-parser.js64
-rw-r--r--devtools/shared/webconsole/test/unit/test_security-info-protocol-version.js54
-rw-r--r--devtools/shared/webconsole/test/unit/test_security-info-state.js100
-rw-r--r--devtools/shared/webconsole/test/unit/test_security-info-static-hpkp.js47
-rw-r--r--devtools/shared/webconsole/test/unit/test_security-info-weakness-reasons.js47
-rw-r--r--devtools/shared/webconsole/test/unit/test_throttle.js140
-rw-r--r--devtools/shared/webconsole/test/unit/xpcshell.ini17
11 files changed, 760 insertions, 0 deletions
diff --git a/devtools/shared/webconsole/test/unit/.eslintrc.js b/devtools/shared/webconsole/test/unit/.eslintrc.js
new file mode 100644
index 000000000..59adf410a
--- /dev/null
+++ b/devtools/shared/webconsole/test/unit/.eslintrc.js
@@ -0,0 +1,6 @@
+"use strict";
+
+module.exports = {
+ // Extend from the common devtools xpcshell eslintrc config.
+ "extends": "../../../../.eslintrc.xpcshell.js"
+};
diff --git a/devtools/shared/webconsole/test/unit/test_js_property_provider.js b/devtools/shared/webconsole/test/unit/test_js_property_provider.js
new file mode 100644
index 000000000..c360cf96d
--- /dev/null
+++ b/devtools/shared/webconsole/test/unit/test_js_property_provider.js
@@ -0,0 +1,170 @@
+/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+"use strict";
+const { require } = Components.utils.import("resource://devtools/shared/Loader.jsm", {});
+const { FallibleJSPropertyProvider: JSPropertyProvider } =
+ require("devtools/shared/webconsole/js-property-provider");
+
+Components.utils.import("resource://gre/modules/jsdebugger.jsm");
+addDebuggerToGlobal(this);
+
+function run_test() {
+ const testArray = `var testArray = [
+ {propA: "A"},
+ {
+ propB: "B",
+ propC: [
+ "D"
+ ]
+ },
+ [
+ {propE: "E"}
+ ]
+ ]`;
+
+ const testObject = 'var testObject = {"propA": [{"propB": "B"}]}';
+ const testHyphenated = 'var testHyphenated = {"prop-A": "res-A"}';
+ const testLet = "let foobar = {a: ''}; const blargh = {a: 1};";
+
+ let sandbox = Components.utils.Sandbox("http://example.com");
+ let dbg = new Debugger;
+ let dbgObject = dbg.addDebuggee(sandbox);
+ let dbgEnv = dbgObject.asEnvironment();
+ Components.utils.evalInSandbox(testArray, sandbox);
+ Components.utils.evalInSandbox(testObject, sandbox);
+ Components.utils.evalInSandbox(testHyphenated, sandbox);
+ Components.utils.evalInSandbox(testLet, sandbox);
+
+ do_print("Running tests with dbgObject");
+ runChecks(dbgObject, null);
+
+ do_print("Running tests with dbgEnv");
+ runChecks(null, dbgEnv);
+
+}
+
+function runChecks(dbgObject, dbgEnv) {
+ do_print("Test that suggestions are given for 'this'");
+ let results = JSPropertyProvider(dbgObject, dbgEnv, "t");
+ test_has_result(results, "this");
+
+ if (dbgObject != null) {
+ do_print("Test that suggestions are given for 'this.'");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "this.");
+ test_has_result(results, "testObject");
+
+ do_print("Test that no suggestions are given for 'this.this'");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "this.this");
+ test_has_no_results(results);
+ }
+
+ do_print("Testing lexical scope issues (Bug 1207868)");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "foobar");
+ test_has_result(results, "foobar");
+
+ results = JSPropertyProvider(dbgObject, dbgEnv, "foobar.");
+ test_has_result(results, "a");
+
+ results = JSPropertyProvider(dbgObject, dbgEnv, "blargh");
+ test_has_result(results, "blargh");
+
+ results = JSPropertyProvider(dbgObject, dbgEnv, "blargh.");
+ test_has_result(results, "a");
+
+ do_print("Test that suggestions are given for 'foo[n]' where n is an integer.");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "testArray[0].");
+ test_has_result(results, "propA");
+
+ do_print("Test that suggestions are given for multidimensional arrays.");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "testArray[2][0].");
+ test_has_result(results, "propE");
+
+ do_print("Test that suggestions are given for nested arrays.");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "testArray[1].propC[0].");
+ test_has_result(results, "indexOf");
+
+ do_print("Test that suggestions are given for literal arrays.");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "[1,2,3].");
+ test_has_result(results, "indexOf");
+
+ do_print("Test that suggestions are given for literal arrays with newlines.");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "[1,2,3,\n4\n].");
+ test_has_result(results, "indexOf");
+
+ do_print("Test that suggestions are given for literal strings.");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "'foo'.");
+ test_has_result(results, "charAt");
+ results = JSPropertyProvider(dbgObject, dbgEnv, '"foo".');
+ test_has_result(results, "charAt");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "`foo`.");
+ test_has_result(results, "charAt");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "'[1,2,3]'.");
+ test_has_result(results, "charAt");
+
+ do_print("Test that suggestions are not given for syntax errors.");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "'foo\"");
+ do_check_null(results);
+ results = JSPropertyProvider(dbgObject, dbgEnv, "[1,',2]");
+ do_check_null(results);
+ results = JSPropertyProvider(dbgObject, dbgEnv, "'[1,2].");
+ do_check_null(results);
+ results = JSPropertyProvider(dbgObject, dbgEnv, "'foo'..");
+ do_check_null(results);
+
+ do_print("Test that suggestions are not given without a dot.");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "'foo'");
+ test_has_no_results(results);
+ results = JSPropertyProvider(dbgObject, dbgEnv, "[1,2,3]");
+ test_has_no_results(results);
+ results = JSPropertyProvider(dbgObject, dbgEnv, "[1,2,3].\n'foo'");
+ test_has_no_results(results);
+
+ do_print("Test that suggestions are not given for numeric literals.");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "1.");
+ do_check_null(results);
+
+ do_print("Test that suggestions are not given for index that's out of bounds.");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "testArray[10].");
+ do_check_null(results);
+
+ do_print("Test that no suggestions are given if an index is not numerical somewhere in the chain.");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "testArray[0]['propC'][0].");
+ do_check_null(results);
+
+ results = JSPropertyProvider(dbgObject, dbgEnv, "testObject['propA'][0].");
+ do_check_null(results);
+
+ results = JSPropertyProvider(dbgObject, dbgEnv, "testArray[0]['propC'].");
+ do_check_null(results);
+
+ results = JSPropertyProvider(dbgObject, dbgEnv, "testArray[][1].");
+ do_check_null(results);
+
+ do_print("Test that suggestions are not given if there is an hyphen in the chain.");
+ results = JSPropertyProvider(dbgObject, dbgEnv, "testHyphenated['prop-A'].");
+ do_check_null(results);
+}
+
+/**
+ * A helper that ensures an empty array of results were found.
+ * @param Object aResults
+ * The results returned by JSPropertyProvider.
+ */
+function test_has_no_results(aResults) {
+ do_check_neq(aResults, null);
+ do_check_eq(aResults.matches.length, 0);
+}
+/**
+ * A helper that ensures (required) results were found.
+ * @param Object aResults
+ * The results returned by JSPropertyProvider.
+ * @param String aRequiredSuggestion
+ * A suggestion that must be found from the results.
+ */
+function test_has_result(aResults, aRequiredSuggestion) {
+ do_check_neq(aResults, null);
+ do_check_true(aResults.matches.length > 0);
+ do_check_true(aResults.matches.indexOf(aRequiredSuggestion) !== -1);
+}
diff --git a/devtools/shared/webconsole/test/unit/test_network_helper.js b/devtools/shared/webconsole/test/unit/test_network_helper.js
new file mode 100644
index 000000000..3a43ff432
--- /dev/null
+++ b/devtools/shared/webconsole/test/unit/test_network_helper.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+var Cu = Components.utils;
+const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+
+Object.defineProperty(this, "NetworkHelper", {
+ get: function () {
+ return require("devtools/shared/webconsole/network-helper");
+ },
+ configurable: true,
+ writeable: false,
+ enumerable: true
+});
+
+function run_test() {
+ test_isTextMimeType();
+}
+
+function test_isTextMimeType() {
+ do_check_eq(NetworkHelper.isTextMimeType("text/plain"), true);
+ do_check_eq(NetworkHelper.isTextMimeType("application/javascript"), true);
+ do_check_eq(NetworkHelper.isTextMimeType("application/json"), true);
+ do_check_eq(NetworkHelper.isTextMimeType("text/css"), true);
+ do_check_eq(NetworkHelper.isTextMimeType("text/html"), true);
+ do_check_eq(NetworkHelper.isTextMimeType("image/svg+xml"), true);
+ do_check_eq(NetworkHelper.isTextMimeType("application/xml"), true);
+
+ // Test custom JSON subtype
+ do_check_eq(NetworkHelper.isTextMimeType("application/vnd.tent.posts-feed.v0+json"), true);
+ do_check_eq(NetworkHelper.isTextMimeType("application/vnd.tent.posts-feed.v0-json"), true);
+ // Test custom XML subtype
+ do_check_eq(NetworkHelper.isTextMimeType("application/vnd.tent.posts-feed.v0+xml"), true);
+ do_check_eq(NetworkHelper.isTextMimeType("application/vnd.tent.posts-feed.v0-xml"), false);
+ // Test case-insensitive
+ do_check_eq(NetworkHelper.isTextMimeType("application/vnd.BIG-CORP+json"), true);
+ // Test non-text type
+ do_check_eq(NetworkHelper.isTextMimeType("image/png"), false);
+ // Test invalid types
+ do_check_eq(NetworkHelper.isTextMimeType("application/foo-+json"), false);
+ do_check_eq(NetworkHelper.isTextMimeType("application/-foo+json"), false);
+ do_check_eq(NetworkHelper.isTextMimeType("application/foo--bar+json"), false);
+
+ // Test we do not cause internal errors with unoptimized regex. Bug 961097
+ do_check_eq(NetworkHelper.isTextMimeType("application/vnd.google.safebrowsing-chunk"), false);
+}
diff --git a/devtools/shared/webconsole/test/unit/test_security-info-certificate.js b/devtools/shared/webconsole/test/unit/test_security-info-certificate.js
new file mode 100644
index 000000000..be223d87e
--- /dev/null
+++ b/devtools/shared/webconsole/test/unit/test_security-info-certificate.js
@@ -0,0 +1,68 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Tests that NetworkHelper.parseCertificateInfo parses certificate information
+// correctly.
+
+const { require } = Components.utils.import("resource://devtools/shared/Loader.jsm", {});
+
+Object.defineProperty(this, "NetworkHelper", {
+ get: function () {
+ return require("devtools/shared/webconsole/network-helper");
+ },
+ configurable: true,
+ writeable: false,
+ enumerable: true
+});
+
+var Ci = Components.interfaces;
+const DUMMY_CERT = {
+ commonName: "cn",
+ organization: "o",
+ organizationalUnit: "ou",
+ issuerCommonName: "issuerCN",
+ issuerOrganization: "issuerO",
+ issuerOrganizationUnit: "issuerOU",
+ sha256Fingerprint: "qwertyuiopoiuytrewq",
+ sha1Fingerprint: "qwertyuiop",
+ validity: {
+ notBeforeLocalDay: "yesterday",
+ notAfterLocalDay: "tomorrow",
+ }
+};
+
+function run_test() {
+ do_print("Testing NetworkHelper.parseCertificateInfo.");
+
+ let result = NetworkHelper.parseCertificateInfo(DUMMY_CERT);
+
+ // Subject
+ equal(result.subject.commonName, DUMMY_CERT.commonName,
+ "Common name is correct.");
+ equal(result.subject.organization, DUMMY_CERT.organization,
+ "Organization is correct.");
+ equal(result.subject.organizationUnit, DUMMY_CERT.organizationUnit,
+ "Organizational unit is correct.");
+
+ // Issuer
+ equal(result.issuer.commonName, DUMMY_CERT.issuerCommonName,
+ "Common name of the issuer is correct.");
+ equal(result.issuer.organization, DUMMY_CERT.issuerOrganization,
+ "Organization of the issuer is correct.");
+ equal(result.issuer.organizationUnit, DUMMY_CERT.issuerOrganizationUnit,
+ "Organizational unit of the issuer is correct.");
+
+ // Validity
+ equal(result.validity.start, DUMMY_CERT.validity.notBeforeLocalDay,
+ "Start of the validity period is correct.");
+ equal(result.validity.end, DUMMY_CERT.validity.notAfterLocalDay,
+ "End of the validity period is correct.");
+
+ // Fingerprints
+ equal(result.fingerprint.sha1, DUMMY_CERT.sha1Fingerprint,
+ "Certificate SHA1 fingerprint is correct.");
+ equal(result.fingerprint.sha256, DUMMY_CERT.sha256Fingerprint,
+ "Certificate SHA256 fingerprint is correct.");
+}
diff --git a/devtools/shared/webconsole/test/unit/test_security-info-parser.js b/devtools/shared/webconsole/test/unit/test_security-info-parser.js
new file mode 100644
index 000000000..a5682e209
--- /dev/null
+++ b/devtools/shared/webconsole/test/unit/test_security-info-parser.js
@@ -0,0 +1,64 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that NetworkHelper.parseSecurityInfo returns correctly formatted object.
+
+const { require } = Components.utils.import("resource://devtools/shared/Loader.jsm", {});
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+Object.defineProperty(this, "NetworkHelper", {
+ get: function () {
+ return require("devtools/shared/webconsole/network-helper");
+ },
+ configurable: true,
+ writeable: false,
+ enumerable: true
+});
+
+var Ci = Components.interfaces;
+const wpl = Ci.nsIWebProgressListener;
+const MockCertificate = {
+ commonName: "cn",
+ organization: "o",
+ organizationalUnit: "ou",
+ issuerCommonName: "issuerCN",
+ issuerOrganization: "issuerO",
+ issuerOrganizationUnit: "issuerOU",
+ sha256Fingerprint: "qwertyuiopoiuytrewq",
+ sha1Fingerprint: "qwertyuiop",
+ validity: {
+ notBeforeLocalDay: "yesterday",
+ notAfterLocalDay: "tomorrow",
+ }
+};
+
+const MockSecurityInfo = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsITransportSecurityInfo,
+ Ci.nsISSLStatusProvider]),
+ securityState: wpl.STATE_IS_SECURE,
+ errorCode: 0,
+ SSLStatus: {
+ cipherSuite: "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
+ protocolVersion: 3, // TLS_VERSION_1_2
+ serverCert: MockCertificate,
+ }
+};
+
+function run_test() {
+ let result = NetworkHelper.parseSecurityInfo(MockSecurityInfo, {});
+
+ equal(result.state, "secure", "State is correct.");
+
+ equal(result.cipherSuite, MockSecurityInfo.cipherSuite,
+ "Cipher suite is correct.");
+
+ equal(result.protocolVersion, "TLSv1.2", "Protocol version is correct.");
+
+ deepEqual(result.cert, NetworkHelper.parseCertificateInfo(MockCertificate),
+ "Certificate information is correct.");
+
+ equal(result.hpkp, false, "HPKP is false when URI is not available.");
+ equal(result.hsts, false, "HSTS is false when URI is not available.");
+}
diff --git a/devtools/shared/webconsole/test/unit/test_security-info-protocol-version.js b/devtools/shared/webconsole/test/unit/test_security-info-protocol-version.js
new file mode 100644
index 000000000..b84002131
--- /dev/null
+++ b/devtools/shared/webconsole/test/unit/test_security-info-protocol-version.js
@@ -0,0 +1,54 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Tests that NetworkHelper.formatSecurityProtocol returns correct
+// protocol version strings.
+
+const { require } = Components.utils.import("resource://devtools/shared/Loader.jsm", {});
+
+Object.defineProperty(this, "NetworkHelper", {
+ get: function () {
+ return require("devtools/shared/webconsole/network-helper");
+ },
+ configurable: true,
+ writeable: false,
+ enumerable: true
+});
+
+var Ci = Components.interfaces;
+const TEST_CASES = [
+ {
+ description: "TLS_VERSION_1",
+ input: 1,
+ expected: "TLSv1"
+ }, {
+ description: "TLS_VERSION_1.1",
+ input: 2,
+ expected: "TLSv1.1"
+ }, {
+ description: "TLS_VERSION_1.2",
+ input: 3,
+ expected: "TLSv1.2"
+ }, {
+ description: "TLS_VERSION_1.3",
+ input: 4,
+ expected: "TLSv1.3"
+ }, {
+ description: "invalid version",
+ input: -1,
+ expected: "Unknown"
+ },
+];
+
+function run_test() {
+ do_print("Testing NetworkHelper.formatSecurityProtocol.");
+
+ for (let {description, input, expected} of TEST_CASES) {
+ do_print("Testing " + description);
+
+ equal(NetworkHelper.formatSecurityProtocol(input), expected,
+ "Got the expected protocol string.");
+ }
+}
diff --git a/devtools/shared/webconsole/test/unit/test_security-info-state.js b/devtools/shared/webconsole/test/unit/test_security-info-state.js
new file mode 100644
index 000000000..efa493a95
--- /dev/null
+++ b/devtools/shared/webconsole/test/unit/test_security-info-state.js
@@ -0,0 +1,100 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Tests that security info parser gives correct general security state for
+// different cases.
+
+const { require } = Components.utils.import("resource://devtools/shared/Loader.jsm", {});
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+Object.defineProperty(this, "NetworkHelper", {
+ get: function () {
+ return require("devtools/shared/webconsole/network-helper");
+ },
+ configurable: true,
+ writeable: false,
+ enumerable: true
+});
+
+var Ci = Components.interfaces;
+const wpl = Ci.nsIWebProgressListener;
+const MockSecurityInfo = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsITransportSecurityInfo,
+ Ci.nsISSLStatusProvider]),
+ securityState: wpl.STATE_IS_BROKEN,
+ errorCode: 0,
+ SSLStatus: {
+ protocolVersion: 3, // nsISSLStatus.TLS_VERSION_1_2
+ cipherSuite: "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
+ }
+};
+
+function run_test() {
+ test_nullSecurityInfo();
+ test_insecureSecurityInfoWithNSSError();
+ test_insecureSecurityInfoWithoutNSSError();
+ test_brokenSecurityInfo();
+ test_secureSecurityInfo();
+}
+
+/**
+ * Test that undefined security information is returns "insecure".
+ */
+function test_nullSecurityInfo() {
+ let result = NetworkHelper.parseSecurityInfo(null, {});
+ equal(result.state, "insecure",
+ "state == 'insecure' when securityInfo was undefined");
+}
+
+/**
+ * Test that STATE_IS_INSECURE with NSSError returns "broken"
+ */
+function test_insecureSecurityInfoWithNSSError() {
+ MockSecurityInfo.securityState = wpl.STATE_IS_INSECURE;
+
+ // Taken from security/manager/ssl/tests/unit/head_psm.js.
+ MockSecurityInfo.errorCode = -8180;
+
+ let result = NetworkHelper.parseSecurityInfo(MockSecurityInfo, {});
+ equal(result.state, "broken",
+ "state == 'broken' if securityState contains STATE_IS_INSECURE flag AND " +
+ "errorCode is NSS error.");
+
+ MockSecurityInfo.errorCode = 0;
+}
+
+/**
+ * Test that STATE_IS_INSECURE without NSSError returns "insecure"
+ */
+function test_insecureSecurityInfoWithoutNSSError() {
+ MockSecurityInfo.securityState = wpl.STATE_IS_INSECURE;
+
+ let result = NetworkHelper.parseSecurityInfo(MockSecurityInfo, {});
+ equal(result.state, "insecure",
+ "state == 'insecure' if securityState contains STATE_IS_INSECURE flag BUT " +
+ "errorCode is not NSS error.");
+}
+
+/**
+ * Test that STATE_IS_SECURE returns "secure"
+ */
+function test_secureSecurityInfo() {
+ MockSecurityInfo.securityState = wpl.STATE_IS_SECURE;
+
+ let result = NetworkHelper.parseSecurityInfo(MockSecurityInfo, {});
+ equal(result.state, "secure",
+ "state == 'secure' if securityState contains STATE_IS_SECURE flag");
+}
+
+/**
+ * Test that STATE_IS_BROKEN returns "weak"
+ */
+function test_brokenSecurityInfo() {
+ MockSecurityInfo.securityState = wpl.STATE_IS_BROKEN;
+
+ let result = NetworkHelper.parseSecurityInfo(MockSecurityInfo, {});
+ equal(result.state, "weak",
+ "state == 'weak' if securityState contains STATE_IS_BROKEN flag");
+}
diff --git a/devtools/shared/webconsole/test/unit/test_security-info-static-hpkp.js b/devtools/shared/webconsole/test/unit/test_security-info-static-hpkp.js
new file mode 100644
index 000000000..b76fa141a
--- /dev/null
+++ b/devtools/shared/webconsole/test/unit/test_security-info-static-hpkp.js
@@ -0,0 +1,47 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that NetworkHelper.parseSecurityInfo correctly detects static hpkp pins
+
+const { require } = Components.utils.import("resource://devtools/shared/Loader.jsm", {});
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+const Services = require("Services");
+
+Object.defineProperty(this, "NetworkHelper", {
+ get: function () {
+ return require("devtools/shared/webconsole/network-helper");
+ },
+ configurable: true,
+ writeable: false,
+ enumerable: true
+});
+
+var Ci = Components.interfaces;
+const wpl = Ci.nsIWebProgressListener;
+
+const MockSecurityInfo = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsITransportSecurityInfo,
+ Ci.nsISSLStatusProvider]),
+ securityState: wpl.STATE_IS_SECURE,
+ errorCode: 0,
+ SSLStatus: {
+ cipherSuite: "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
+ protocolVersion: 3, // TLS_VERSION_1_2
+ serverCert: {
+ validity: {}
+ },
+ }
+};
+
+const MockHttpInfo = {
+ hostname: "include-subdomains.pinning.example.com",
+ private: false,
+};
+
+function run_test() {
+ Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 1);
+ let result = NetworkHelper.parseSecurityInfo(MockSecurityInfo, MockHttpInfo);
+ equal(result.hpkp, true, "Static HPKP detected.");
+}
diff --git a/devtools/shared/webconsole/test/unit/test_security-info-weakness-reasons.js b/devtools/shared/webconsole/test/unit/test_security-info-weakness-reasons.js
new file mode 100644
index 000000000..f91d8049e
--- /dev/null
+++ b/devtools/shared/webconsole/test/unit/test_security-info-weakness-reasons.js
@@ -0,0 +1,47 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Tests that NetworkHelper.getReasonsForWeakness returns correct reasons for
+// weak requests.
+
+const { require } = Components.utils.import("resource://devtools/shared/Loader.jsm", {});
+
+Object.defineProperty(this, "NetworkHelper", {
+ get: function () {
+ return require("devtools/shared/webconsole/network-helper");
+ },
+ configurable: true,
+ writeable: false,
+ enumerable: true
+});
+
+var Ci = Components.interfaces;
+const wpl = Ci.nsIWebProgressListener;
+const TEST_CASES = [
+ {
+ description: "weak cipher",
+ input: wpl.STATE_IS_BROKEN | wpl.STATE_USES_WEAK_CRYPTO,
+ expected: ["cipher"]
+ }, {
+ description: "only STATE_IS_BROKEN flag",
+ input: wpl.STATE_IS_BROKEN,
+ expected: []
+ }, {
+ description: "only STATE_IS_SECURE flag",
+ input: wpl.STATE_IS_SECURE,
+ expected: []
+ },
+];
+
+function run_test() {
+ do_print("Testing NetworkHelper.getReasonsForWeakness.");
+
+ for (let {description, input, expected} of TEST_CASES) {
+ do_print("Testing " + description);
+
+ deepEqual(NetworkHelper.getReasonsForWeakness(input), expected,
+ "Got the expected reasons for weakness.");
+ }
+}
diff --git a/devtools/shared/webconsole/test/unit/test_throttle.js b/devtools/shared/webconsole/test/unit/test_throttle.js
new file mode 100644
index 000000000..fa8b26b61
--- /dev/null
+++ b/devtools/shared/webconsole/test/unit/test_throttle.js
@@ -0,0 +1,140 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const promise = require("promise");
+const { NetworkThrottleManager } =
+ require("devtools/shared/webconsole/throttle");
+const nsIScriptableInputStream = Ci.nsIScriptableInputStream;
+
+function TestStreamListener() {
+ this.state = "initial";
+}
+TestStreamListener.prototype = {
+ onStartRequest: function() {
+ this.setState("start");
+ },
+
+ onStopRequest: function() {
+ this.setState("stop");
+ },
+
+ onDataAvailable: function(request, context, inputStream, offset, count) {
+ const sin = Components.classes["@mozilla.org/scriptableinputstream;1"]
+ .createInstance(nsIScriptableInputStream);
+ sin.init(inputStream);
+ this.data = sin.read(count);
+ this.setState("data");
+ },
+
+ setState: function(state) {
+ this.state = state;
+ if (this._deferred) {
+ this._deferred.resolve(state);
+ this._deferred = null;
+ }
+ },
+
+ onStateChanged: function() {
+ if (!this._deferred) {
+ this._deferred = promise.defer();
+ }
+ return this._deferred.promise;
+ }
+};
+
+function TestChannel() {
+ this.state = "initial";
+ this.testListener = new TestStreamListener();
+ this._throttleQueue = null;
+}
+TestChannel.prototype = {
+ QueryInterface: function() {
+ return this;
+ },
+
+ get throttleQueue() {
+ return this._throttleQueue;
+ },
+
+ set throttleQueue(q) {
+ this._throttleQueue = q;
+ this.state = "throttled";
+ },
+
+ setNewListener: function(listener) {
+ this.listener = listener;
+ this.state = "listener";
+ return this.testListener;
+ },
+};
+
+add_task(function*() {
+ let throttler = new NetworkThrottleManager({
+ roundTripTimeMean: 1,
+ roundTripTimeMax: 1,
+ downloadBPSMean: 500,
+ downloadBPSMax: 500,
+ uploadBPSMean: 500,
+ uploadBPSMax: 500,
+ });
+
+ let uploadChannel = new TestChannel();
+ throttler.manageUpload(uploadChannel);
+ equal(uploadChannel.state, "throttled",
+ "NetworkThrottleManager set throttleQueue");
+
+ let downloadChannel = new TestChannel();
+ let testListener = downloadChannel.testListener;
+
+ let listener = throttler.manage(downloadChannel);
+ equal(downloadChannel.state, "listener",
+ "NetworkThrottleManager called setNewListener");
+
+ equal(testListener.state, "initial", "test listener in initial state");
+
+ // This method must be passed through immediately.
+ listener.onStartRequest(null, null);
+ equal(testListener.state, "start", "test listener started");
+
+ const TEST_INPUT = "hi bob";
+
+ let testStream = Cc["@mozilla.org/storagestream;1"]
+ .createInstance(Ci.nsIStorageStream);
+ testStream.init(512, 512);
+ let out = testStream.getOutputStream(0);
+ out.write(TEST_INPUT, TEST_INPUT.length);
+ out.close();
+ let testInputStream = testStream.newInputStream(0);
+
+ let activityDistributor =
+ Cc["@mozilla.org/network/http-activity-distributor;1"]
+ .getService(Ci.nsIHttpActivityDistributor);
+ let activitySeen = false;
+ listener.addActivityCallback(() => activitySeen = true, null, null, null,
+ activityDistributor
+ .ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
+ null, TEST_INPUT.length, null);
+
+ // onDataAvailable is required to immediately read the data.
+ listener.onDataAvailable(null, null, testInputStream, 0, 6);
+ equal(testInputStream.available(), 0, "no more data should be available");
+ equal(testListener.state, "start",
+ "test listener should not have received data");
+ equal(activitySeen, false, "activity not distributed yet");
+
+ let newState = yield testListener.onStateChanged();
+ equal(newState, "data", "test listener received data");
+ equal(testListener.data, TEST_INPUT, "test listener received all the data");
+ equal(activitySeen, true, "activity has been distributed");
+
+ let onChange = testListener.onStateChanged();
+ listener.onStopRequest(null, null, null);
+ newState = yield onChange;
+ equal(newState, "stop", "onStateChanged reported");
+});
diff --git a/devtools/shared/webconsole/test/unit/xpcshell.ini b/devtools/shared/webconsole/test/unit/xpcshell.ini
new file mode 100644
index 000000000..083950834
--- /dev/null
+++ b/devtools/shared/webconsole/test/unit/xpcshell.ini
@@ -0,0 +1,17 @@
+[DEFAULT]
+tags = devtools
+head =
+tail =
+firefox-appdir = browser
+skip-if = toolkit == 'android'
+support-files =
+
+[test_js_property_provider.js]
+[test_network_helper.js]
+[test_security-info-certificate.js]
+[test_security-info-parser.js]
+[test_security-info-protocol-version.js]
+[test_security-info-state.js]
+[test_security-info-static-hpkp.js]
+[test_security-info-weakness-reasons.js]
+[test_throttle.js]