/* Assertions for polyfill-report-pre.js + polyfill.js.
 *
 * Reaching this point at all is half the test: the whole file is one
 * concatenation, so if the polyfill let an error escape, nothing below runs
 * and the harness reports the throw instead of a FAIL line. */

var fail = 0;

function check(what, got, want)
{
	var ok = got === want;
	if (!ok)
		fail++;
	print((ok ? "  ok   " : "  FAIL ") + what +
	      ": got " + got + (ok ? "" : ", want " + want));
}

function findWarning(re)
{
	var i;
	for (i = 0; i < WARNINGS.length; i++) {
		if (re.test(WARNINGS[i]))
			return WARNINGS[i];
	}
	return "";
}

print("polyfill error reporting:");

/* The poisoned section must have failed, and said so with its number. A
 * report that names no section is the old behaviour. */
var report = findWarning(/^polyfill section #\d+ failed: /);
check("failed section is reported", report !== "", true);

/* The old code turned this exact case into a TypeError of its own. The
 * report must describe the thrown value, not die converting it. */
check("report survives a toString-less throw",
      report.indexOf("not a function") >= 0, false);
check("report describes the unreadable value",
      report.indexOf("no readable message") >= 0, true);

/* Exactly one section may fail -- the poisoned one. A second report means
 * the new describe()/warn() path broke something else. */
var reports = 0;
for (var i = 0; i < WARNINGS.length; i++) {
	if (/^polyfill section #\d+ failed: /.test(WARNINGS[i]))
		reports++;
}
check("only the poisoned section failed", reports, 1);

/* The _DumpException fallback: present, and it names a real error instead of
 * throwing over it. */
check("_DumpException installed", typeof _DumpException, "function");

var threw = false;
try {
	_DumpException(new TypeError("boom"));
} catch (e) {
	threw = true;
}
check("_DumpException does not throw", threw, false);
check("_DumpException names the error",
      findWarning(/^_DumpException: TypeError: boom/) !== "", true);

/* The case it exists for: Google's reporter is handed whatever was caught,
 * which is not always an Error. */
threw = false;
try {
	_DumpException(Object.create(null));
} catch (e) {
	threw = true;
}
check("_DumpException survives a toString-less value", threw, false);

/* CSS.supports: the operation Google's bundle died on. */
check("CSS.supports installed", typeof CSS.supports, "function");
check("hung on the existing interface object", typeof CSS, "function");

check("two-arg form accepts a supported declaration",
      CSS.supports("display", "grid"), true);
check("two-arg form rejects an unsupported value",
      CSS.supports("display", "wibble"), false);
check("two-arg form rejects an unknown property",
      CSS.supports("no-such-prop", "1px"), false);
check("condition form accepts a supported declaration",
      CSS.supports("(display: grid)"), true);
check("condition form rejects an unsupported value",
      CSS.supports("(display: wibble)"), false);

check("bare property with no colon reports false",
      CSS.supports("(display)"), false);
check("unparenthesised condition reports false",
      CSS.supports("display: grid"), false);
check("empty value reports false", CSS.supports("display", ""), false);

/* ---- the full <supports-condition> grammar ---- */

print("CSS.supports condition grammar:");

/* and / or */
check("and, both supported",
      CSS.supports("(display: grid) and (color: red)"), true);
check("and, one unsupported",
      CSS.supports("(display: grid) and (color: wibble)"), false);
check("and chain of three",
      CSS.supports("(display: grid) and (display: flex) and (color: red)"),
      true);
check("or, first supported",
      CSS.supports("(display: grid) or (color: wibble)"), true);
check("or, neither supported",
      CSS.supports("(display: wibble) or (color: blue)"), false);
check("or chain of three",
      CSS.supports("(a: b) or (c: d) or (display: grid)"), true);

/* not */
check("not an unsupported declaration",
      CSS.supports("not (display: wibble)"), true);
check("not a supported declaration",
      CSS.supports("not (display: grid)"), false);
check("not is case-insensitive",
      CSS.supports("NOT (display: wibble)"), true);
/* `not(` is a function token, not the keyword followed by a block. */
check("not without whitespace is not the keyword",
      CSS.supports("not(display: wibble)"), false);

/* Nesting and grouping. */
check("nested group",
      CSS.supports("((display: grid))"), true);
check("or inside and",
      CSS.supports("(display: grid) and ((color: red) or (color: blue))"),
      true);
check("grouping changes the answer",
      CSS.supports("((display: wibble) or (color: red)) and (display: grid)"),
      true);
check("not applied to a group",
      CSS.supports("not ((display: wibble) and (color: red))"), true);

/* Mixing and with or at one level is a parse error, not a precedence
 * question -- the spec requires the parens. */
check("mixing and/or without parens is invalid",
      CSS.supports("(display: grid) and (color: red) or (display: flex)"),
      false);
check("mixing or/and without parens is invalid",
      CSS.supports("(display: grid) or (color: red) and (display: flex)"),
      false);

/* Values that contain parens and colons must not confuse the split. */
check("value containing a function",
      CSS.supports("(display: grid) and (color: red)"), true);
check("colon inside a nested function is not the declaration colon",
      CSS.supports("(selector(a:hover))"), true);
check("string containing an unbalanced paren",
      CSS.supports("(content: \"(\") or (display: grid)"), true);

/* selector() */
check("selector() with a parseable selector",
      CSS.supports("selector(div > .a)"), true);
check("selector() with an unparseable selector",
      CSS.supports("selector(div %%% span)"), false);
check("selector() the engine rejects",
      CSS.supports("selector(:has(a))"), false);
check("selector() combined with a declaration",
      CSS.supports("selector(div) and (display: grid)"), true);
check("not selector() of an unsupported selector",
      CSS.supports("not selector(:has(a))"), true);
check("empty selector() reports false",
      CSS.supports("selector()"), false);

/* font-tech() / font-format(): known functions, and this engine loads no
 * webfonts, so every technology and format is a definite no. */
check("font-tech() reports false",
      CSS.supports("font-tech(color-COLRv1)"), false);
check("font-format() reports false",
      CSS.supports("font-format(woff2)"), false);
check("not font-tech() is true, being a definite no rather than unknown",
      CSS.supports("not font-tech(variations)"), true);

/* <general-enclosed>: an unknown function is valid syntax of unknown
 * meaning. It is false, and -- the reason the evaluator is three-valued --
 * negating it must not turn it into true. */
check("unknown function reports false",
      CSS.supports("wibble(1)"), false);
check("not an unknown function is still false",
      CSS.supports("not wibble(1)"), false);
check("unknown function anded with a supported declaration",
      CSS.supports("wibble(1) and (display: grid)"), false);
check("unknown function ored with a supported declaration",
      CSS.supports("wibble(1) or (display: grid)"), true);
check("unknown non-function parens report false",
      CSS.supports("(wibble wobble)"), false);

/* Malformed input answers false rather than throwing. */
check("empty string reports false", CSS.supports(""), false);
check("unbalanced parens report false",
      CSS.supports("(display: grid"), false);
check("trailing junk reports false",
      CSS.supports("(display: grid) wibble"), false);
check("dangling and reports false",
      CSS.supports("(display: grid) and"), false);
check("bare keyword reports false", CSS.supports("not"), false);
check("leading and trailing whitespace is ignored",
      CSS.supports("  (display: grid)  "), true);

/* It must answer, not throw, when the document cannot produce an element --
 * this runs at boot, before a page is necessarily there. */
var realCreate = document.createElement;
document.createElement = function () { throw new Error("no document yet"); };
var threwFromSupports = false;
var answer = null;
try {
	answer = CSS.supports("display", "grid");
} catch (e) {
	threwFromSupports = true;
}
document.createElement = realCreate;
check("survives a document that cannot make elements", threwFromSupports, false);
check("and answers false there", answer, false);

print(fail === 0 ? "PASS" : "FAIL (" + fail + ")");
