/* -*- 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 }