Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gtest/gtest.h"
#include "mozilla/ArrayUtils.h"
#include "Preferences.h"
using namespace mozilla;
// Keep this in sync with the declaration in Preferences.cpp.
//
// It's declared here to avoid polluting Preferences.h with test-only stuff.
void TestParseError(PrefValueKind aKind, const char* aText,
nsCString& aErrorMsg);
TEST(PrefsParser, Errors)
{
nsAutoCStringN<128> actualErrorMsg;
// Use a macro rather than a function so that the line number reported by
// gtest on failure is useful.
#define P(kind_, text_, expectedErrorMsg_) \
do { \
TestParseError(kind_, text_, actualErrorMsg); \
ASSERT_STREQ(expectedErrorMsg_, actualErrorMsg.get()); \
} while (0)
#define DEFAULT(text_, expectedErrorMsg_) \
P(PrefValueKind::Default, text_, expectedErrorMsg_)
#define USER(text_, expectedErrorMsg_) \
P(PrefValueKind::User, text_, expectedErrorMsg_)
// clang-format off
//-------------------------------------------------------------------------
// Valid syntax. (Other testing of more typical valid syntax and semantics is
// done in modules/libpref/test/unit/test_parser.js.)
//-------------------------------------------------------------------------
// Normal prefs.
DEFAULT(R"(
pref("bool", true);
sticky_pref("int", 123);
user_pref("string", "value");
)",
""
);
// Totally empty input.
DEFAULT("", "");
// Whitespace-only input.
DEFAULT(R"(
)" "\v \t \v \f",
""
);
// Comment-only inputs.
DEFAULT(R"(// blah)", "");
DEFAULT(R"(# blah)", "");
DEFAULT(R"(/* blah */)", "");
//-------------------------------------------------------------------------
// All the lexing errors. (To be pedantic, some of the integer literal
// overflows are triggered in the parser, but put them all here so they're all
// in the one spot.)
//-------------------------------------------------------------------------
// Integer overflow errors.
DEFAULT(R"(
pref("int.ok", 2147483647);
pref("int.overflow", 2147483648);
pref("int.ok", +2147483647);
pref("int.overflow", +2147483648);
pref("int.ok", -2147483648);
pref("int.overflow", -2147483649);
pref("int.overflow", 4294967296);
pref("int.overflow", +4294967296);
pref("int.overflow", -4294967296);
pref("int.overflow", 4294967297);
pref("int.overflow", 1234567890987654321);
)",
"test:3: prefs parse error: integer literal overflowed\n"
"test:5: prefs parse error: integer literal overflowed\n"
"test:7: prefs parse error: integer literal overflowed\n"
"test:8: prefs parse error: integer literal overflowed\n"
"test:9: prefs parse error: integer literal overflowed\n"
"test:10: prefs parse error: integer literal overflowed\n"
"test:11: prefs parse error: integer literal overflowed\n"
"test:12: prefs parse error: integer literal overflowed\n"
);
// Other integer errors.
DEFAULT(R"(
pref("int.unexpected", 100foo);
pref("int.ok", 0);
)",
"test:2: prefs parse error: unexpected character in integer literal\n"
);
// \x00 is not allowed.
DEFAULT(R"(
pref("string.bad-x-escape", "foo\x00bar");
pref("int.ok", 0);
)",
"test:2: prefs parse error: \\x00 is not allowed\n"
);
// Various bad things after \x: end of string, punctuation, space, newline,
// EOF.
DEFAULT(R"(
pref("string.bad-x-escape", "foo\x");
pref("string.bad-x-escape", "foo\x,bar");
pref("string.bad-x-escape", "foo\x 12");
pref("string.bad-x-escape", "foo\x
12");
pref("string.bad-x-escape", "foo\x)",
"test:2: prefs parse error: malformed \\x escape sequence\n"
"test:3: prefs parse error: malformed \\x escape sequence\n"
"test:4: prefs parse error: malformed \\x escape sequence\n"
"test:5: prefs parse error: malformed \\x escape sequence\n"
"test:7: prefs parse error: malformed \\x escape sequence\n"
);
// Not enough hex digits.
DEFAULT(R"(
pref("string.bad-x-escape", "foo\x1");
pref("int.ok", 0);
)",
"test:2: prefs parse error: malformed \\x escape sequence\n"
);
// Invalid hex digit.
DEFAULT(R"(
pref("string.bad-x-escape", "foo\x1G");
pref("int.ok", 0);
)",
"test:2: prefs parse error: malformed \\x escape sequence\n"
);
// \u0000 is not allowed.
// (The string literal is broken in two so that MSVC doesn't complain about
// an invalid universal-character-name.)
DEFAULT(R"(
pref("string.bad-u-escape", "foo\)" R"(u0000 bar");
pref("int.ok", 0);
)",
"test:2: prefs parse error: \\u0000 is not allowed\n"
);
// Various bad things after \u: end of string, punctuation, space, newline,
// EOF.
DEFAULT(R"(
pref("string.bad-u-escape", "foo\u");
pref("string.bad-u-escape", "foo\u,bar");
pref("string.bad-u-escape", "foo\u 1234");
pref("string.bad-u-escape", "foo\u
1234");
pref("string.bad-u-escape", "foo\u)",
"test:2: prefs parse error: malformed \\u escape sequence\n"
"test:3: prefs parse error: malformed \\u escape sequence\n"
"test:4: prefs parse error: malformed \\u escape sequence\n"
"test:5: prefs parse error: malformed \\u escape sequence\n"
"test:7: prefs parse error: malformed \\u escape sequence\n"
);
// Not enough hex digits.
DEFAULT(R"(
pref("string.bad-u-escape", "foo\u1");
pref("string.bad-u-escape", "foo\u12");
pref("string.bad-u-escape", "foo\u123");
pref("int.ok", 0);
)",
"test:2: prefs parse error: malformed \\u escape sequence\n"
"test:3: prefs parse error: malformed \\u escape sequence\n"
"test:4: prefs parse error: malformed \\u escape sequence\n"
);
// Invalid hex digit.
DEFAULT(R"(
pref("string.bad-u-escape", "foo\u1G34");
pref("int.ok", 0);
)",
"test:2: prefs parse error: malformed \\u escape sequence\n"
);
// High surrogate not followed by low surrogate.
// (The string literal is broken in two so that MSVC doesn't complain about
// an invalid universal-character-name.)
DEFAULT(R"(
pref("string.bad-u-surrogate", "foo\)" R"(ud83c,blah");
pref("int.ok", 0);
)",
"test:2: prefs parse error: expected low surrogate after high surrogate\n"
);
// High surrogate followed by invalid low surrogate.
// (The string literal is broken in two so that MSVC doesn't complain about
// an invalid universal-character-name.)
DEFAULT(R"(
pref("string.bad-u-surrogate", "foo\)" R"(ud83c\u1234");
pref("int.ok", 0);
)",
"test:2: prefs parse error: invalid low surrogate after high surrogate\n"
);
// Low surrogate not preceded by high surrogate.
// (The string literal is broken in two so that MSVC doesn't complain about
// an invalid universal-character-name.)
DEFAULT(R"(
pref("string.bad-u-surrogate", "foo\)" R"(udc00");
pref("int.ok", 0);
)",
"test:2: prefs parse error: expected high surrogate before low surrogate\n"
);
// Unlike in JavaScript, \b, \f, \t, \v aren't allowed.
DEFAULT(R"(
pref("string.bad-escape", "foo\b");
pref("string.bad-escape", "foo\f");
pref("string.bad-escape", "foo\t");
pref("string.bad-escape", "foo\v");
pref("int.ok", 0);
)",
"test:2: prefs parse error: unexpected escape sequence character after '\\'\n"
"test:3: prefs parse error: unexpected escape sequence character after '\\'\n"
"test:4: prefs parse error: unexpected escape sequence character after '\\'\n"
"test:5: prefs parse error: unexpected escape sequence character after '\\'\n"
);
// Various bad things after \: non-special letter, number, punctuation,
// space, newline, EOF.
DEFAULT(R"(
pref("string.bad-escape", "foo\Q");
pref("string.bad-escape", "foo\1");
pref("string.bad-escape", "foo\,");
pref("string.bad-escape", "foo\ n");
pref("string.bad-escape", "foo\
n");
pref("string.bad-escape", "foo\)",
"test:2: prefs parse error: unexpected escape sequence character after '\\'\n"
"test:3: prefs parse error: unexpected escape sequence character after '\\'\n"
"test:4: prefs parse error: unexpected escape sequence character after '\\'\n"
"test:5: prefs parse error: unexpected escape sequence character after '\\'\n"
"test:6: prefs parse error: unexpected escape sequence character after '\\'\n"
"test:8: prefs parse error: unexpected escape sequence character after '\\'\n"
);
// Unterminated string literals.
// Simple case.
DEFAULT(R"(
pref("string.unterminated-string", "foo
)",
"test:3: prefs parse error: unterminated string literal\n"
);
// Alternative case; `int` comes after the string and is seen as a keyword.
// The parser then skips to the ';', so no error about the unterminated
// string is issued.
DEFAULT(R"(
pref("string.unterminated-string", "foo);
pref("int.ok", 0);
)",
"test:3: prefs parse error: unknown keyword\n"
);
// Mismatched quotes (1).
DEFAULT(R"(
pref("string.unterminated-string", "foo');
)",
"test:3: prefs parse error: unterminated string literal\n"
);
// Mismatched quotes (2).
DEFAULT(R"(
pref("string.unterminated-string", 'foo");
)",
"test:3: prefs parse error: unterminated string literal\n"
);
// Unknown keywords.
DEFAULT(R"(
foo;
preff("string.bad-keyword", true);
ticky_pref("string.bad-keyword", true);
User_pref("string.bad-keyword", true);
pref("string.bad-keyword", TRUE);
)",
"test:2: prefs parse error: unknown keyword\n"
"test:3: prefs parse error: unknown keyword\n"
"test:4: prefs parse error: unknown keyword\n"
"test:5: prefs parse error: unknown keyword\n"
"test:6: prefs parse error: unknown keyword\n"
);
// Unterminated C-style comment.
DEFAULT(R"(
/* comment
)",
"test:3: prefs parse error: unterminated /* comment\n"
);
// Malformed comments (single slashes), followed by whitespace, newline, EOF.
DEFAULT(R"(
/ comment;
/
; /)",
"test:2: prefs parse error: expected '/' or '*' after '/'\n"
"test:3: prefs parse error: expected '/' or '*' after '/'\n"
"test:4: prefs parse error: expected '/' or '*' after '/'\n"
);
// C++-style comment ending in EOF (1).
DEFAULT(R"(
// comment)",
""
);
// C++-style comment ending in EOF (2).
DEFAULT(R"(
//)",
""
);
// Various unexpected characters.
DEFAULT(R"(
pref("unexpected.chars", &true);
pref("unexpected.chars" : true);
@pref("unexpected.chars", true);
pref["unexpected.chars": true];
)",
"test:2: prefs parse error: unexpected character\n"
"test:3: prefs parse error: unexpected character\n"
"test:4: prefs parse error: unexpected character\n"
"test:5: prefs parse error: unexpected character\n"
);
//-------------------------------------------------------------------------
// All the parsing errors.
//-------------------------------------------------------------------------
DEFAULT(R"(
"pref"("parse.error": true);
pref1("parse.error": true);
pref(123: true);
pref("parse.error" true);
pref("parse.error", pref);
pref("parse.error", -true);
pref("parse.error", +"value");
pref("parse.error", true,);
pref("parse.error", true;
pref("parse.error", true, sticky, locked;
pref("parse.error", true)
pref("int.ok", 1);
pref("parse.error", true))",
"test:2: prefs parse error: expected pref specifier at start of pref definition\n"
"test:3: prefs parse error: expected '(' after pref specifier\n"
"test:4: prefs parse error: expected pref name after '('\n"
"test:5: prefs parse error: expected ',' after pref name\n"
"test:6: prefs parse error: expected pref value after ','\n"
"test:7: prefs parse error: expected integer literal after '-'\n"
"test:8: prefs parse error: expected integer literal after '+'\n"
"test:9: prefs parse error: expected pref attribute after ','\n"
"test:10: prefs parse error: expected ',' or ')' after pref value\n"
"test:11: prefs parse error: expected ',' or ')' after pref attribute\n"
"test:13: prefs parse error: expected ';' after ')'\n"
"test:14: prefs parse error: expected ';' after ')'\n"
);
USER(R"(
pref("parse.error", true);
sticky_pref("parse.error", true);
user_pref("int.ok", 1);
)",
"test:2: prefs parse error: expected 'user_pref' at start of pref definition\n"
"test:3: prefs parse error: expected 'user_pref' at start of pref definition\n"
);
USER(R"(
user_pref("parse.error", true;
user_pref("int.ok", 1);
)",
"test:2: prefs parse error: expected ')' after pref value\n"
);
// Parse errors involving unexpected EOF.
DEFAULT(R"(
pref)",
"test:2: prefs parse error: expected '(' after pref specifier\n"
);
DEFAULT(R"(
pref()",
"test:2: prefs parse error: expected pref name after '('\n"
);
DEFAULT(R"(
pref("parse.error")",
"test:2: prefs parse error: expected ',' after pref name\n"
);
DEFAULT(R"(
pref("parse.error",)",
"test:2: prefs parse error: expected pref value after ','\n"
);
DEFAULT(R"(
pref("parse.error", -)",
"test:2: prefs parse error: expected integer literal after '-'\n"
);
DEFAULT(R"(
pref("parse.error", +)",
"test:2: prefs parse error: expected integer literal after '+'\n"
);
DEFAULT(R"(
pref("parse.error", true)",
"test:2: prefs parse error: expected ',' or ')' after pref value\n"
);
USER(R"(
user_pref("parse.error", true)",
"test:2: prefs parse error: expected ')' after pref value\n"
);
DEFAULT(R"(
pref("parse.error", true,)",
"test:2: prefs parse error: expected pref attribute after ','\n"
);
DEFAULT(R"(
pref("parse.error", true, sticky)",
"test:2: prefs parse error: expected ',' or ')' after pref attribute\n"
);
DEFAULT(R"(
pref("parse.error", true))",
"test:2: prefs parse error: expected ';' after ')'\n"
);
// This is something we saw in practice with the old parser, which allowed
// repeated semicolons.
DEFAULT(R"(
pref("parse.error", true);;
pref("parse.error", true, locked);;;
pref("parse.error", true, sticky, locked);;;;
pref("int.ok", 0);
)",
"test:2: prefs parse error: expected pref specifier at start of pref definition\n"
"test:3: prefs parse error: expected pref specifier at start of pref definition\n"
"test:3: prefs parse error: expected pref specifier at start of pref definition\n"
"test:4: prefs parse error: expected pref specifier at start of pref definition\n"
"test:4: prefs parse error: expected pref specifier at start of pref definition\n"
"test:4: prefs parse error: expected pref specifier at start of pref definition\n"
);
//-------------------------------------------------------------------------
// Invalid syntax after various newline combinations, for the purpose of
// testing that line numbers are correct.
//-------------------------------------------------------------------------
// In all of the following we have a \n, a \r, a \r\n, and then an error, so
// the error is on line 4. (Note: these ones don't use raw string literals
// because MSVC somehow swallows any \r that appears in them.)
DEFAULT("\n \r \r\n bad",
"test:4: prefs parse error: unknown keyword\n"
);
DEFAULT("#\n#\r#\r\n bad",
"test:4: prefs parse error: unknown keyword\n"
);
DEFAULT("//\n//\r//\r\n bad",
"test:4: prefs parse error: unknown keyword\n"
);
DEFAULT("/*\n \r \r\n*/ bad",
"test:4: prefs parse error: unknown keyword\n"
);
// Note: the escape sequences do *not* affect the line number.
DEFAULT("pref(\"foo\\n\n foo\\r\r foo\\r\\n\r\n foo\", bad);",
"test:4: prefs parse error: unknown keyword\n"
);
// clang-format on
}