/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 "mozilla/Tokenizer.h" #include "mozilla/IncrementalTokenizer.h" #include "mozilla/Unused.h" #include "gtest/gtest.h" using namespace mozilla; static bool IsOperator(char const c) { return c == '+' || c == '*'; } static bool HttpHeaderCharacter(char const c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '-'); } TEST(Tokenizer, HTTPResponse) { Tokenizer::Token t; // Real life test, HTTP response Tokenizer p(NS_LITERAL_CSTRING( "HTTP/1.0 304 Not modified\r\n" "ETag: hallo\r\n" "Content-Length: 16\r\n" "\r\n" "This is the body")); EXPECT_TRUE(p.CheckWord("HTTP")); EXPECT_TRUE(p.CheckChar('/')); EXPECT_TRUE(p.Check(Tokenizer::TOKEN_INTEGER, t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_INTEGER); EXPECT_TRUE(t.AsInteger() == 1); EXPECT_TRUE(p.CheckChar('.')); EXPECT_TRUE(p.Check(Tokenizer::TOKEN_INTEGER, t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_INTEGER); EXPECT_TRUE(t.AsInteger() == 0); p.SkipWhites(); EXPECT_TRUE(p.Check(Tokenizer::TOKEN_INTEGER, t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_INTEGER); EXPECT_TRUE(t.AsInteger() == 304); p.SkipWhites(); p.Record(); while (p.Next(t) && t.Type() != Tokenizer::TOKEN_EOL); EXPECT_FALSE(p.HasFailed()); nsAutoCString h; p.Claim(h); EXPECT_TRUE(h == "Not modified"); p.Record(); while (p.CheckChar(HttpHeaderCharacter)); p.Claim(h, Tokenizer::INCLUDE_LAST); EXPECT_TRUE(h == "ETag"); p.SkipWhites(); EXPECT_TRUE(p.CheckChar(':')); p.SkipWhites(); p.Record(); while (p.Next(t) && t.Type() != Tokenizer::TOKEN_EOL); EXPECT_FALSE(p.HasFailed()); p.Claim(h); EXPECT_TRUE(h == "hallo"); p.Record(); while (p.CheckChar(HttpHeaderCharacter)); p.Claim(h, Tokenizer::INCLUDE_LAST); EXPECT_TRUE(h == "Content-Length"); p.SkipWhites(); EXPECT_TRUE(p.CheckChar(':')); p.SkipWhites(); EXPECT_TRUE(p.Check(Tokenizer::TOKEN_INTEGER, t)); EXPECT_TRUE(t.AsInteger() == 16); EXPECT_TRUE(p.CheckEOL()); EXPECT_TRUE(p.CheckEOL()); p.Record(); while (p.Next(t) && t.Type() != Tokenizer::TOKEN_EOF); nsAutoCString b; p.Claim(b); EXPECT_TRUE(b == "This is the body"); } TEST(Tokenizer, Main) { Tokenizer::Token t; // Synthetic code-specific test Tokenizer p(NS_LITERAL_CSTRING("test123 ,15 \t*\r\n%xx,-15\r\r")); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_WORD); EXPECT_TRUE(t.AsString() == "test123"); Tokenizer::Token u; EXPECT_FALSE(p.Check(u)); EXPECT_FALSE(p.CheckChar('!')); EXPECT_FALSE(p.Check(Tokenizer::Token::Number(123))); EXPECT_TRUE(p.CheckWhite()); EXPECT_TRUE(p.CheckChar(',')); EXPECT_TRUE(p.Check(Tokenizer::Token::Number(15))); p.Rollback(); EXPECT_TRUE(p.Check(Tokenizer::Token::Number(15))); p.Rollback(); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_INTEGER); EXPECT_TRUE(t.AsInteger() == 15); EXPECT_FALSE(p.CheckChar(IsOperator)); EXPECT_TRUE(p.CheckWhite()); p.SkipWhites(); EXPECT_FALSE(p.CheckWhite()); p.Rollback(); EXPECT_TRUE(p.CheckWhite()); EXPECT_TRUE(p.CheckWhite()); p.Record(Tokenizer::EXCLUDE_LAST); EXPECT_TRUE(p.CheckChar(IsOperator)); p.Rollback(); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_CHAR); EXPECT_TRUE(t.AsChar() == '*'); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_EOL); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_CHAR); EXPECT_TRUE(t.AsChar() == '%'); nsAutoCString claim; p.Claim(claim, Tokenizer::EXCLUDE_LAST); EXPECT_TRUE(claim == "*\r\n"); p.Claim(claim, Tokenizer::INCLUDE_LAST); EXPECT_TRUE(claim == "*\r\n%"); p.Rollback(); EXPECT_TRUE(p.CheckChar('%')); p.Record(Tokenizer::INCLUDE_LAST); EXPECT_FALSE(p.CheckWord("xy")); EXPECT_TRUE(p.CheckWord("xx")); p.Claim(claim, Tokenizer::INCLUDE_LAST); EXPECT_TRUE(claim == "%xx"); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_CHAR); EXPECT_TRUE(t.AsChar() == ','); EXPECT_TRUE(p.CheckChar('-')); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_INTEGER); EXPECT_TRUE(t.AsInteger() == 15); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_EOL); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_EOL); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_EOF); EXPECT_FALSE(p.Next(t)); p.Rollback(); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_EOF); EXPECT_FALSE(p.Next(t)); p.Rollback(); EXPECT_TRUE(p.CheckEOF()); EXPECT_FALSE(p.CheckEOF()); } TEST(Tokenizer, SingleWord) { // Single word with numbers in it test Tokenizer p(NS_LITERAL_CSTRING("test123")); EXPECT_TRUE(p.CheckWord("test123")); EXPECT_TRUE(p.CheckEOF()); } TEST(Tokenizer, EndingAfterNumber) { // An end handling after a number Tokenizer p(NS_LITERAL_CSTRING("123")); EXPECT_FALSE(p.CheckWord("123")); EXPECT_TRUE(p.Check(Tokenizer::Token::Number(123))); EXPECT_TRUE(p.CheckEOF()); } TEST(Tokenizer, BadInteger) { Tokenizer::Token t; // A bad integer test Tokenizer p(NS_LITERAL_CSTRING("189234891274981758617846178651647620587135")); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_ERROR); EXPECT_TRUE(p.CheckEOF()); } TEST(Tokenizer, CheckExpectedTokenValue) { Tokenizer::Token t; // Check expected token value test Tokenizer p(NS_LITERAL_CSTRING("blue velvet")); EXPECT_FALSE(p.Check(Tokenizer::TOKEN_INTEGER, t)); EXPECT_TRUE(p.Check(Tokenizer::TOKEN_WORD, t)); EXPECT_TRUE(t.AsString() == "blue"); EXPECT_FALSE(p.Check(Tokenizer::TOKEN_WORD, t)); EXPECT_TRUE(p.CheckWhite()); EXPECT_TRUE(p.Check(Tokenizer::TOKEN_WORD, t)); EXPECT_TRUE(t.AsString() == "velvet"); EXPECT_TRUE(p.CheckEOF()); EXPECT_FALSE(p.Next(t)); } TEST(Tokenizer, HasFailed) { Tokenizer::Token t; // HasFailed test Tokenizer p1(NS_LITERAL_CSTRING("a b")); while (p1.Next(t) && t.Type() != Tokenizer::TOKEN_CHAR); EXPECT_TRUE(p1.HasFailed()); Tokenizer p2(NS_LITERAL_CSTRING("a b ?!c")); EXPECT_FALSE(p2.CheckChar('c')); EXPECT_TRUE(p2.HasFailed()); EXPECT_TRUE(p2.CheckChar(HttpHeaderCharacter)); EXPECT_FALSE(p2.HasFailed()); p2.SkipWhites(); EXPECT_FALSE(p2.HasFailed()); EXPECT_FALSE(p2.CheckChar('c')); EXPECT_TRUE(p2.HasFailed()); EXPECT_TRUE(p2.Next(t)); EXPECT_FALSE(p2.HasFailed()); EXPECT_TRUE(p2.Next(t)); EXPECT_FALSE(p2.HasFailed()); EXPECT_FALSE(p2.CheckChar('c')); EXPECT_TRUE(p2.HasFailed()); EXPECT_TRUE(p2.Check(Tokenizer::TOKEN_CHAR, t)); EXPECT_FALSE(p2.HasFailed()); EXPECT_FALSE(p2.CheckChar('#')); EXPECT_TRUE(p2.HasFailed()); t = Tokenizer::Token::Char('!'); EXPECT_TRUE(p2.Check(t)); EXPECT_FALSE(p2.HasFailed()); while (p2.Next(t) && t.Type() != Tokenizer::TOKEN_CHAR); EXPECT_TRUE(p2.HasFailed()); } TEST(Tokenizer, Construction) { { nsCString a("test"); Tokenizer p1(a); EXPECT_TRUE(p1.CheckWord("test")); EXPECT_TRUE(p1.CheckEOF()); } { nsAutoCString a("test"); Tokenizer p1(a); EXPECT_TRUE(p1.CheckWord("test")); EXPECT_TRUE(p1.CheckEOF()); } { static const char _a[] = "test"; nsDependentCString a(_a); Tokenizer p1(a); EXPECT_TRUE(p1.CheckWord("test")); EXPECT_TRUE(p1.CheckEOF()); } { static const char* _a = "test"; nsDependentCString a(_a); Tokenizer p1(a); EXPECT_TRUE(p1.CheckWord("test")); EXPECT_TRUE(p1.CheckEOF()); } { Tokenizer p1(nsDependentCString("test")); EXPECT_TRUE(p1.CheckWord("test")); EXPECT_TRUE(p1.CheckEOF()); } { Tokenizer p1(NS_LITERAL_CSTRING("test")); EXPECT_TRUE(p1.CheckWord("test")); EXPECT_TRUE(p1.CheckEOF()); } { Tokenizer p1("test"); EXPECT_TRUE(p1.CheckWord("test")); EXPECT_TRUE(p1.CheckEOF()); } } TEST(Tokenizer, Customization) { Tokenizer p1(NS_LITERAL_CSTRING("test-custom*words and\tdefault-whites"), nullptr, "-*"); EXPECT_TRUE(p1.CheckWord("test-custom*words")); EXPECT_TRUE(p1.CheckWhite()); EXPECT_TRUE(p1.CheckWord("and")); EXPECT_TRUE(p1.CheckWhite()); EXPECT_TRUE(p1.CheckWord("default-whites")); Tokenizer p2(NS_LITERAL_CSTRING("test, custom,whites"), ", "); EXPECT_TRUE(p2.CheckWord("test")); EXPECT_TRUE(p2.CheckWhite()); EXPECT_TRUE(p2.CheckWhite()); EXPECT_TRUE(p2.CheckWord("custom")); EXPECT_TRUE(p2.CheckWhite()); EXPECT_TRUE(p2.CheckWord("whites")); Tokenizer p3(NS_LITERAL_CSTRING("test, custom, whites-and#word-chars"), ",", "-#"); EXPECT_TRUE(p3.CheckWord("test")); EXPECT_TRUE(p3.CheckWhite()); EXPECT_FALSE(p3.CheckWhite()); EXPECT_TRUE(p3.CheckChar(' ')); EXPECT_TRUE(p3.CheckWord("custom")); EXPECT_TRUE(p3.CheckWhite()); EXPECT_FALSE(p3.CheckWhite()); EXPECT_TRUE(p3.CheckChar(' ')); EXPECT_TRUE(p3.CheckWord("whites-and#word-chars")); } TEST(Tokenizer, ShortcutChecks) { Tokenizer p("test1 test2,123"); nsAutoCString test1; nsDependentCSubstring test2; char comma; uint32_t integer; EXPECT_TRUE(p.ReadWord(test1)); EXPECT_TRUE(test1 == "test1"); p.SkipWhites(); EXPECT_TRUE(p.ReadWord(test2)); EXPECT_TRUE(test2 == "test2"); EXPECT_TRUE(p.ReadChar(&comma)); EXPECT_TRUE(comma == ','); EXPECT_TRUE(p.ReadInteger(&integer)); EXPECT_TRUE(integer == 123); EXPECT_TRUE(p.CheckEOF()); } static bool ABChar(const char aChar) { return aChar == 'a' || aChar == 'b'; } TEST(Tokenizer, ReadCharClassified) { Tokenizer p("abc"); char c; EXPECT_TRUE(p.ReadChar(ABChar, &c)); EXPECT_TRUE(c == 'a'); EXPECT_TRUE(p.ReadChar(ABChar, &c)); EXPECT_TRUE(c == 'b'); EXPECT_FALSE(p.ReadChar(ABChar, &c)); nsDependentCSubstring w; EXPECT_TRUE(p.ReadWord(w)); EXPECT_TRUE(w == "c"); EXPECT_TRUE(p.CheckEOF()); } TEST(Tokenizer, ClaimSubstring) { Tokenizer p(" abc "); EXPECT_TRUE(p.CheckWhite()); p.Record(); EXPECT_TRUE(p.CheckWord("abc")); nsDependentCSubstring v; p.Claim(v, Tokenizer::INCLUDE_LAST); EXPECT_TRUE(v == "abc"); EXPECT_TRUE(p.CheckWhite()); EXPECT_TRUE(p.CheckEOF()); } TEST(Tokenizer, Fragment) { const char str[] = "ab;cd:10 "; Tokenizer p(str); nsDependentCSubstring f; Tokenizer::Token t1, t2; EXPECT_TRUE(p.Next(t1)); EXPECT_TRUE(t1.Type() == Tokenizer::TOKEN_WORD); EXPECT_TRUE(t1.Fragment() == "ab"); EXPECT_TRUE(t1.Fragment().BeginReading() == &str[0]); p.Rollback(); EXPECT_TRUE(p.Check(Tokenizer::TOKEN_WORD, t2)); EXPECT_TRUE(t2.Fragment() == "ab"); EXPECT_TRUE(t2.Fragment().BeginReading() == &str[0]); EXPECT_TRUE(p.Next(t1)); EXPECT_TRUE(t1.Type() == Tokenizer::TOKEN_CHAR); EXPECT_TRUE(t1.Fragment() == ";"); EXPECT_TRUE(t1.Fragment().BeginReading() == &str[2]); p.Rollback(); EXPECT_TRUE(p.Check(Tokenizer::TOKEN_CHAR, t2)); EXPECT_TRUE(t2.Fragment() == ";"); EXPECT_TRUE(t2.Fragment().BeginReading() == &str[2]); EXPECT_TRUE(p.Check(Tokenizer::TOKEN_WORD, t2)); EXPECT_TRUE(t2.Fragment() == "cd"); EXPECT_TRUE(t2.Fragment().BeginReading() == &str[3]); p.Rollback(); EXPECT_TRUE(p.Next(t1)); EXPECT_TRUE(t1.Type() == Tokenizer::TOKEN_WORD); EXPECT_TRUE(t1.Fragment() == "cd"); EXPECT_TRUE(t1.Fragment().BeginReading() == &str[3]); EXPECT_TRUE(p.Check(Tokenizer::TOKEN_CHAR, t2)); EXPECT_TRUE(t2.Fragment() == ":"); EXPECT_TRUE(t2.Fragment().BeginReading() == &str[5]); p.Rollback(); EXPECT_TRUE(p.Next(t1)); EXPECT_TRUE(t1.Type() == Tokenizer::TOKEN_CHAR); EXPECT_TRUE(t1.Fragment() == ":"); EXPECT_TRUE(t1.Fragment().BeginReading() == &str[5]); EXPECT_TRUE(p.Next(t1)); EXPECT_TRUE(t1.Type() == Tokenizer::TOKEN_INTEGER); EXPECT_TRUE(t1.Fragment() == "10"); EXPECT_TRUE(t1.Fragment().BeginReading() == &str[6]); EXPECT_TRUE(p.Check(Tokenizer::TOKEN_WS, t2)); EXPECT_TRUE(t2.Fragment() == " "); EXPECT_TRUE(t2.Fragment().BeginReading() == &str[8]); EXPECT_TRUE(p.Check(Tokenizer::TOKEN_EOF, t1)); EXPECT_TRUE(t1.Fragment() == ""); EXPECT_TRUE(t1.Fragment().BeginReading() == &str[9]); } TEST(Tokenizer, SkipWhites) { Tokenizer p("Text1 \nText2 \nText3\n Text4\n "); EXPECT_TRUE(p.CheckWord("Text1")); p.SkipWhites(); EXPECT_TRUE(p.CheckEOL()); EXPECT_TRUE(p.CheckWord("Text2")); p.SkipWhites(Tokenizer::INCLUDE_NEW_LINE); EXPECT_TRUE(p.CheckWord("Text3")); p.SkipWhites(); EXPECT_TRUE(p.CheckEOL()); p.SkipWhites(); EXPECT_TRUE(p.CheckWord("Text4")); p.SkipWhites(Tokenizer::INCLUDE_NEW_LINE); EXPECT_TRUE(p.CheckEOF()); } TEST(Tokenizer, SkipCustomWhites) { Tokenizer p("Text1 \n\r\t.Text2 \n\r\t.", " \n\r\t."); EXPECT_TRUE(p.CheckWord("Text1")); p.SkipWhites(); EXPECT_TRUE(p.CheckWord("Text2")); EXPECT_TRUE(p.CheckWhite()); EXPECT_TRUE(p.CheckWhite()); EXPECT_TRUE(p.CheckWhite()); EXPECT_TRUE(p.CheckWhite()); EXPECT_TRUE(p.CheckWhite()); EXPECT_TRUE(p.CheckEOF()); } TEST(Tokenizer, IntegerReading) { #define INT_6_BITS 64U #define INT_30_BITS 1073741824UL #define INT_32_BITS 4294967295UL #define INT_50_BITS 1125899906842624ULL #define STR_INT_MORE_THAN_64_BITS "922337203685477580899" { Tokenizer p(NS_STRINGIFY(INT_6_BITS)); uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; EXPECT_TRUE(p.ReadInteger(&u8)); EXPECT_TRUE(u8 == INT_6_BITS); p.Rollback(); EXPECT_TRUE(p.ReadInteger(&u16)); EXPECT_TRUE(u16 == INT_6_BITS); p.Rollback(); EXPECT_TRUE(p.ReadInteger(&u32)); EXPECT_TRUE(u32 == INT_6_BITS); p.Rollback(); EXPECT_TRUE(p.ReadInteger(&u64)); EXPECT_TRUE(u64 == INT_6_BITS); p.Rollback(); int8_t s8; int16_t s16; int32_t s32; int64_t s64; EXPECT_TRUE(p.ReadInteger(&s8)); EXPECT_TRUE(s8 == INT_6_BITS); p.Rollback(); EXPECT_TRUE(p.ReadInteger(&s16)); EXPECT_TRUE(s16 == INT_6_BITS); p.Rollback(); EXPECT_TRUE(p.ReadInteger(&s32)); EXPECT_TRUE(s32 == INT_6_BITS); p.Rollback(); EXPECT_TRUE(p.ReadInteger(&s64)); EXPECT_TRUE(s64 == INT_6_BITS); EXPECT_TRUE(p.CheckWord("U")); EXPECT_TRUE(p.CheckEOF()); } { Tokenizer p(NS_STRINGIFY(INT_30_BITS)); uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; EXPECT_FALSE(p.ReadInteger(&u8)); EXPECT_FALSE(p.ReadInteger(&u16)); EXPECT_TRUE(p.ReadInteger(&u32)); EXPECT_TRUE(u32 == INT_30_BITS); p.Rollback(); EXPECT_TRUE(p.ReadInteger(&u64)); EXPECT_TRUE(u64 == INT_30_BITS); p.Rollback(); int8_t s8; int16_t s16; int32_t s32; int64_t s64; EXPECT_FALSE(p.ReadInteger(&s8)); EXPECT_FALSE(p.ReadInteger(&s16)); EXPECT_TRUE(p.ReadInteger(&s32)); EXPECT_TRUE(s32 == INT_30_BITS); p.Rollback(); EXPECT_TRUE(p.ReadInteger(&s64)); EXPECT_TRUE(s64 == INT_30_BITS); EXPECT_TRUE(p.CheckWord("UL")); EXPECT_TRUE(p.CheckEOF()); } { Tokenizer p(NS_STRINGIFY(INT_32_BITS)); uint32_t u32; int32_t s32; EXPECT_FALSE(p.ReadInteger(&s32)); EXPECT_TRUE(p.ReadInteger(&u32)); EXPECT_TRUE(u32 == INT_32_BITS); EXPECT_TRUE(p.CheckWord("UL")); EXPECT_TRUE(p.CheckEOF()); } { Tokenizer p(NS_STRINGIFY(INT_50_BITS)); uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; EXPECT_FALSE(p.ReadInteger(&u8)); EXPECT_FALSE(p.ReadInteger(&u16)); EXPECT_FALSE(p.ReadInteger(&u32)); EXPECT_TRUE(p.ReadInteger(&u64)); EXPECT_TRUE(u64 == INT_50_BITS); EXPECT_TRUE(p.CheckWord("ULL")); EXPECT_TRUE(p.CheckEOF()); } { Tokenizer p(STR_INT_MORE_THAN_64_BITS); int64_t i; EXPECT_FALSE(p.ReadInteger(&i)); uint64_t u; EXPECT_FALSE(p.ReadInteger(&u)); EXPECT_FALSE(p.CheckEOF()); } } TEST(Tokenizer, ReadUntil) { Tokenizer p("Hello;test 4,"); nsDependentCSubstring f; EXPECT_TRUE(p.ReadUntil(Tokenizer::Token::Char(';'), f)); EXPECT_TRUE(f == "Hello"); p.Rollback(); EXPECT_TRUE(p.ReadUntil(Tokenizer::Token::Char(';'), f, Tokenizer::INCLUDE_LAST)); EXPECT_TRUE(f == "Hello;"); p.Rollback(); EXPECT_FALSE(p.ReadUntil(Tokenizer::Token::Char('!'), f)); EXPECT_TRUE(f == "Hello;test 4,"); p.Rollback(); EXPECT_TRUE(p.ReadUntil(Tokenizer::Token::Word(NS_LITERAL_CSTRING("test")), f)); EXPECT_TRUE(f == "Hello;"); p.Rollback(); EXPECT_TRUE(p.ReadUntil(Tokenizer::Token::Word(NS_LITERAL_CSTRING("test")), f, Tokenizer::INCLUDE_LAST)); EXPECT_TRUE(f == "Hello;test"); EXPECT_TRUE(p.ReadUntil(Tokenizer::Token::Char(','), f)); EXPECT_TRUE(f == " 4"); } TEST(Tokenizer, SkipUntil) { { Tokenizer p("test1,test2,,,test3"); p.SkipUntil(Tokenizer::Token::Char(',')); EXPECT_TRUE(p.CheckChar(',')); EXPECT_TRUE(p.CheckWord("test2")); p.SkipUntil(Tokenizer::Token::Char(',')); // must not move EXPECT_TRUE(p.CheckChar(',')); // check the first comma of the ',,,' string p.Rollback(); // moves cursor back to the first comma of the ',,,' string p.SkipUntil(Tokenizer::Token::Char(',')); // must not move, we are on the ',' char EXPECT_TRUE(p.CheckChar(',')); EXPECT_TRUE(p.CheckChar(',')); EXPECT_TRUE(p.CheckChar(',')); EXPECT_TRUE(p.CheckWord("test3")); p.Rollback(); p.SkipUntil(Tokenizer::Token::Char(',')); EXPECT_TRUE(p.CheckEOF()); } { Tokenizer p("test0,test1,test2"); p.SkipUntil(Tokenizer::Token::Char(',')); EXPECT_TRUE(p.CheckChar(',')); p.SkipUntil(Tokenizer::Token::Char(',')); p.Rollback(); EXPECT_TRUE(p.CheckWord("test1")); EXPECT_TRUE(p.CheckChar(',')); p.SkipUntil(Tokenizer::Token::Char(',')); p.Rollback(); EXPECT_TRUE(p.CheckWord("test2")); EXPECT_TRUE(p.CheckEOF()); } } TEST(Tokenizer, Custom) { Tokenizer p("aaaaaacustom-1\r,custom-1,Custom-1,Custom-1,00custom-2xxxx,CUSTOM-2"); Tokenizer::Token c1 = p.AddCustomToken("custom-1", Tokenizer::CASE_INSENSITIVE); Tokenizer::Token c2 = p.AddCustomToken("custom-2", Tokenizer::CASE_SENSITIVE); // It's expected to NOT FIND the custom token if it's not on an edge // between other recognizable tokens. EXPECT_TRUE(p.CheckWord("aaaaaacustom")); EXPECT_TRUE(p.CheckChar('-')); EXPECT_TRUE(p.Check(Tokenizer::Token::Number(1))); EXPECT_TRUE(p.CheckEOL()); EXPECT_TRUE(p.CheckChar(',')); EXPECT_TRUE(p.Check(c1)); EXPECT_TRUE(p.CheckChar(',')); EXPECT_TRUE(p.Check(c1)); EXPECT_TRUE(p.CheckChar(',')); p.EnableCustomToken(c1, false); EXPECT_TRUE(p.CheckWord("Custom")); EXPECT_TRUE(p.CheckChar('-')); EXPECT_TRUE(p.Check(Tokenizer::Token::Number(1))); EXPECT_TRUE(p.CheckChar(',')); EXPECT_TRUE(p.Check(Tokenizer::Token::Number(0))); EXPECT_TRUE(p.Check(c2)); EXPECT_TRUE(p.CheckWord("xxxx")); EXPECT_TRUE(p.CheckChar(',')); EXPECT_TRUE(p.CheckWord("CUSTOM")); EXPECT_TRUE(p.CheckChar('-')); EXPECT_TRUE(p.Check(Tokenizer::Token::Number(2))); EXPECT_TRUE(p.CheckEOF()); } TEST(Tokenizer, CustomRaw) { Tokenizer p("aaaaaacustom-1\r,custom-1,Custom-1,Custom-1,00custom-2xxxx,CUSTOM-2"); Tokenizer::Token c1 = p.AddCustomToken("custom-1", Tokenizer::CASE_INSENSITIVE); Tokenizer::Token c2 = p.AddCustomToken("custom-2", Tokenizer::CASE_SENSITIVE); // In this mode it's expected to find all custom tokens among any kind of input. p.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY); Tokenizer::Token t; EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW); EXPECT_TRUE(t.Fragment().EqualsLiteral("aaaaaa")); EXPECT_TRUE(p.Check(c1)); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW); EXPECT_TRUE(t.Fragment().EqualsLiteral("\r,")); EXPECT_TRUE(p.Check(c1)); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW); EXPECT_TRUE(t.Fragment().EqualsLiteral(",")); EXPECT_TRUE(p.Check(c1)); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW); EXPECT_TRUE(t.Fragment().EqualsLiteral(",")); EXPECT_TRUE(p.Check(c1)); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW); EXPECT_TRUE(t.Fragment().EqualsLiteral(",00")); EXPECT_TRUE(p.Check(c2)); EXPECT_TRUE(p.Next(t)); EXPECT_TRUE(t.Type() == Tokenizer::TOKEN_RAW); EXPECT_TRUE(t.Fragment().EqualsLiteral("xxxx,CUSTOM-2")); EXPECT_TRUE(p.CheckEOF()); } TEST(Tokenizer, Incremental) { typedef TokenizerBase::Token Token; int test = 0; IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult { switch (++test) { case 1: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test1")))); break; case 2: EXPECT_TRUE(t.Equals(Token::Char(','))); break; case 3: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test2")))); break; case 4: EXPECT_TRUE(t.Equals(Token::Char(','))); break; case 5: EXPECT_TRUE(t.Equals(Token::Char(','))); break; case 6: EXPECT_TRUE(t.Equals(Token::Char(','))); break; case 7: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test3")))); break; case 8: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break; } return NS_OK; }); NS_NAMED_LITERAL_CSTRING(input, "test1,test2,,,test3"); auto cur = input.BeginReading(); auto end = input.EndReading(); for (; cur < end; ++cur) { i.FeedInput(nsDependentCSubstring(cur, 1)); } EXPECT_TRUE(test == 6); i.FinishInput(); EXPECT_TRUE(test == 8); } TEST(Tokenizer, IncrementalRollback) { typedef TokenizerBase::Token Token; int test = 0; IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult { switch (++test) { case 1: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test1")))); break; case 2: EXPECT_TRUE(t.Equals(Token::Char(','))); break; case 3: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test2")))); i.Rollback(); // so that we get the token again break; case 4: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test2")))); break; case 5: EXPECT_TRUE(t.Equals(Token::Char(','))); break; case 6: EXPECT_TRUE(t.Equals(Token::Char(','))); break; case 7: EXPECT_TRUE(t.Equals(Token::Char(','))); break; case 8: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("test3")))); break; case 9: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break; } return NS_OK; }); NS_NAMED_LITERAL_CSTRING(input, "test1,test2,,,test3"); auto cur = input.BeginReading(); auto end = input.EndReading(); for (; cur < end; ++cur) { i.FeedInput(nsDependentCSubstring(cur, 1)); } EXPECT_TRUE(test == 7); i.FinishInput(); EXPECT_TRUE(test == 9); } TEST(Tokenizer, IncrementalNeedMoreInput) { typedef TokenizerBase::Token Token; int test = 0; IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult { Token t2; switch (++test) { case 1: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("a")))); break; case 2: case 3: case 4: case 5: EXPECT_TRUE(t.Equals(Token::Whitespace())); if (i.Next(t2)) { EXPECT_TRUE(test == 5); EXPECT_TRUE(t2.Equals(Token::Word(NS_LITERAL_CSTRING("bb")))); } else { EXPECT_TRUE(test < 5); i.NeedMoreInput(); } break; case 6: EXPECT_TRUE(t.Equals(Token::Char(','))); break; case 7: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("c")))); return NS_ERROR_FAILURE; default: EXPECT_TRUE(false); break; } return NS_OK; }); NS_NAMED_LITERAL_CSTRING(input, "a bb,c"); auto cur = input.BeginReading(); auto end = input.EndReading(); nsresult rv; for (; cur < end; ++cur) { rv = i.FeedInput(nsDependentCSubstring(cur, 1)); if (NS_FAILED(rv)) { break; } } EXPECT_TRUE(rv == NS_OK); EXPECT_TRUE(test == 6); rv = i.FinishInput(); EXPECT_TRUE(rv == NS_ERROR_FAILURE); EXPECT_TRUE(test == 7); } TEST(Tokenizer, IncrementalCustom) { typedef TokenizerBase::Token Token; int test = 0; Token custom; IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult { switch (++test) { case 1: EXPECT_TRUE(t.Equals(custom)); break; case 2: EXPECT_TRUE(t.Equals(Token::Word(NS_LITERAL_CSTRING("bla")))); break; case 3: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break; } return NS_OK; }, nullptr, "-"); custom = i.AddCustomToken("some-test", Tokenizer::CASE_SENSITIVE); i.FeedInput(NS_LITERAL_CSTRING("some-")); EXPECT_TRUE(test == 0); i.FeedInput(NS_LITERAL_CSTRING("tes")); EXPECT_TRUE(test == 0); i.FeedInput(NS_LITERAL_CSTRING("tbla")); EXPECT_TRUE(test == 1); i.FinishInput(); EXPECT_TRUE(test == 3); } TEST(Tokenizer, IncrementalCustomRaw) { typedef TokenizerBase::Token Token; int test = 0; Token custom; IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult { switch (++test) { case 1: EXPECT_TRUE(t.Fragment().EqualsLiteral("test1,")); break; case 2: EXPECT_TRUE(t.Equals(custom)); break; case 3: EXPECT_TRUE(t.Fragment().EqualsLiteral("!,,test3")); i.Rollback(); i.SetTokenizingMode(Tokenizer::Mode::FULL); break; case 4: EXPECT_TRUE(t.Equals(Token::Char('!'))); i.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY); break; case 5: EXPECT_TRUE(t.Fragment().EqualsLiteral(",,test3")); break; case 6: EXPECT_TRUE(t.Equals(custom)); break; case 7: EXPECT_TRUE(t.Fragment().EqualsLiteral("tes")); break; case 8: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break; } return NS_OK; }); custom = i.AddCustomToken("test2", Tokenizer::CASE_SENSITIVE); i.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY); NS_NAMED_LITERAL_CSTRING(input, "test1,test2!,,test3test2tes"); auto cur = input.BeginReading(); auto end = input.EndReading(); for (; cur < end; ++cur) { i.FeedInput(nsDependentCSubstring(cur, 1)); } EXPECT_TRUE(test == 6); i.FinishInput(); EXPECT_TRUE(test == 8); } TEST(Tokenizer, IncrementalCustomRemove) { typedef TokenizerBase::Token Token; int test = 0; Token custom; IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult { switch (++test) { case 1: EXPECT_TRUE(t.Equals(custom)); i.RemoveCustomToken(custom); break; case 2: EXPECT_FALSE(t.Equals(custom)); break; case 3: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break; } return NS_OK; }); custom = i.AddCustomToken("custom1", Tokenizer::CASE_SENSITIVE); NS_NAMED_LITERAL_CSTRING(input, "custom1custom1"); i.FeedInput(input); EXPECT_TRUE(test == 1); i.FinishInput(); EXPECT_TRUE(test == 3); } TEST(Tokenizer, IncrementalBuffering1) { typedef TokenizerBase::Token Token; int test = 0; Token custom; nsDependentCSubstring observedFragment; IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult { switch (++test) { case 1: EXPECT_TRUE(t.Fragment().EqualsLiteral("012")); break; case 2: EXPECT_TRUE(t.Fragment().EqualsLiteral("3456789")); break; case 3: EXPECT_TRUE(t.Equals(custom)); break; case 4: EXPECT_TRUE(t.Fragment().EqualsLiteral("qwe")); break; case 5: EXPECT_TRUE(t.Fragment().EqualsLiteral("rt")); break; case 6: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break; } observedFragment.Rebind(t.Fragment().BeginReading(), t.Fragment().Length()); return NS_OK; }, nullptr, nullptr, 3); custom = i.AddCustomToken("aaa", Tokenizer::CASE_SENSITIVE); // This externally unused token is added only to check the internal algorithm // does work correctly as expected when there are two different length tokens. Unused << i.AddCustomToken("bb", Tokenizer::CASE_SENSITIVE); i.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY); i.FeedInput(NS_LITERAL_CSTRING("01234")); EXPECT_TRUE(test == 1); EXPECT_TRUE(observedFragment.EqualsLiteral("012")); i.FeedInput(NS_LITERAL_CSTRING("5")); EXPECT_TRUE(test == 1); i.FeedInput(NS_LITERAL_CSTRING("6789aa")); EXPECT_TRUE(test == 2); EXPECT_TRUE(observedFragment.EqualsLiteral("3456789")); i.FeedInput(NS_LITERAL_CSTRING("aqwert")); EXPECT_TRUE(test == 4); EXPECT_TRUE(observedFragment.EqualsLiteral("qwe")); i.FinishInput(); EXPECT_TRUE(test == 6); } TEST(Tokenizer, IncrementalBuffering2) { typedef TokenizerBase::Token Token; int test = 0; Token custom; IncrementalTokenizer i([&](Token const& t, IncrementalTokenizer& i) -> nsresult { switch (++test) { case 1: EXPECT_TRUE(t.Fragment().EqualsLiteral("01")); break; case 2: EXPECT_TRUE(t.Fragment().EqualsLiteral("234567")); break; case 3: EXPECT_TRUE(t.Fragment().EqualsLiteral("89")); break; case 4: EXPECT_TRUE(t.Equals(custom)); break; case 5: EXPECT_TRUE(t.Fragment().EqualsLiteral("qwert")); break; case 6: EXPECT_TRUE(t.Equals(Token::EndOfFile())); break; } return NS_OK; }, nullptr, nullptr, 3); custom = i.AddCustomToken("aaa", Tokenizer::CASE_SENSITIVE); // This externally unused token is added only to check the internal algorithm // does work correctly as expected when there are two different length tokens. Unused << i.AddCustomToken("bbbbb", Tokenizer::CASE_SENSITIVE); i.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY); i.FeedInput(NS_LITERAL_CSTRING("01234")); EXPECT_TRUE(test == 0); i.FeedInput(NS_LITERAL_CSTRING("5")); EXPECT_TRUE(test == 1); i.FeedInput(NS_LITERAL_CSTRING("6789aa")); EXPECT_TRUE(test == 2); i.FeedInput(NS_LITERAL_CSTRING("aqwert")); EXPECT_TRUE(test == 4); i.FinishInput(); EXPECT_TRUE(test == 6); }