diff options
Diffstat (limited to 'modules/libpref/test/gtest/Parser.cpp')
-rw-r--r-- | modules/libpref/test/gtest/Parser.cpp | 496 |
1 files changed, 496 insertions, 0 deletions
diff --git a/modules/libpref/test/gtest/Parser.cpp b/modules/libpref/test/gtest/Parser.cpp new file mode 100644 index 0000000000..972d32cfca --- /dev/null +++ b/modules/libpref/test/gtest/Parser.cpp @@ -0,0 +1,496 @@ +/* -*- 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 +} |