| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "tokens.h" |
| |
| #include <zxtest/zxtest.h> |
| |
| namespace netdump::test { |
| |
| class TokensTester : public Tokenizer { |
| public: |
| void LiteralTest() { |
| TokenPtr token = literal("testestest"); |
| TokenPtr other = literal("testestest"); |
| |
| EXPECT_EQ("testestest", token->get_term()); |
| EXPECT_EQ("testestest", other->get_term()); |
| |
| EXPECT_FALSE(token == other); |
| } |
| |
| void KeywordTest() { |
| TokenPtr before = literal("KEYWORD"); |
| TokenPtr token = keyword("KEYWORD", 'k'); |
| TokenPtr other = keyword("KEYWORD", 'k'); |
| TokenPtr after = literal("KEYWORD"); |
| |
| EXPECT_EQ("KEYWORD", token->get_term()); |
| EXPECT_EQ('k', token->get_tag<char>()); |
| |
| // Check the behavior expected of a keyword token, and that the `literal` function has not |
| // registered anything before or after a `keyword` call. |
| EXPECT_TRUE(token == other); // Keyword token remains the same. |
| EXPECT_FALSE(token == before); // `literal` did not register new keyword. |
| EXPECT_TRUE(token == after); // `literal` did not overwrite keyword registration. |
| |
| if (ZX_DEBUG_ASSERT_IMPLEMENTED) { |
| ASSERT_DEATH([this]() { keyword("KEYWORD", 'w'); }); // Redefining `tag` not allowed. |
| } |
| } |
| |
| void SynonymTest() { |
| TokenPtr token = keyword("HELLO", "CIAO"); |
| TokenPtr keyword_syn = keyword("CIAO"); |
| TokenPtr literal_syn = literal("CIAO"); |
| TokenPtr add_syn = keyword("CIAO", "NIHAO"); |
| TokenPtr syn = keyword("NIHAO"); |
| |
| EXPECT_EQ("HELLO", token->get_term()); |
| EXPECT_TRUE(token == keyword_syn); |
| EXPECT_TRUE(token == add_syn); |
| EXPECT_TRUE(token == syn); |
| } |
| |
| static void CheckTokenVectorString(std::vector<TokenPtr> expected_tokens, |
| std::vector<TokenPtr> got_tokens) { |
| EXPECT_EQ(expected_tokens.size(), got_tokens.size()); |
| auto exp = expected_tokens.begin(); |
| for (const TokenPtr& token : got_tokens) { |
| EXPECT_EQ((*exp)->get_term(), token->get_term()); |
| ++exp; |
| } |
| } |
| |
| void BasicTokenizeTest() { |
| TokenPtr kwa = keyword("kwa"); |
| TokenPtr kwb = keyword("kwb"); |
| TokenPtr kwc = keyword("kwc", "kwd"); |
| TokenPtr lita = literal("lita"); |
| TokenPtr litb = literal("litb"); |
| std::string input = "kwa kwb\tkwa kwc\t\tlita\nkwb\t\nkwd\n\nlitb"; |
| std::vector<TokenPtr> tokens = tokenize(input); |
| std::vector<TokenPtr> expected_tokens = {kwa, kwb, kwa, kwc, lita, kwb, kwc, litb}; |
| |
| std::vector<TokenPtr> empty = tokenize(""); |
| EXPECT_TRUE(empty.empty()); |
| |
| CheckTokenVectorString(expected_tokens, tokens); |
| // Literal tokens should have unique identities. |
| EXPECT_NE(tokens[4], lita); |
| EXPECT_NE(tokens[7], litb); |
| } |
| |
| void TokenizeRealKeywordsTest() { |
| std::string input = "( ether src ) and ( ip6 or ip4 ) and ( tcp dst port )"; |
| std::vector<TokenPtr> tokens = tokenize(input); |
| std::vector<TokenPtr> expected_tokens = {L_PARENS, ETHER, SRC, R_PARENS, AND, L_PARENS, |
| IP6, OR, IP, R_PARENS, AND, L_PARENS, |
| TCP, DST, PORT, R_PARENS}; |
| EXPECT_EQ(expected_tokens, tokens); |
| } |
| |
| void TokenizeKeywordsLiteralsStringTest() { |
| TokenPtr xxx = literal("xxx"); |
| TokenPtr twentythree = literal("23"); |
| std::string input = "( ether src xxx ) and ( ip6 or ip4 ) and ( tcp dst port 23 )"; |
| std::vector<TokenPtr> tokens = tokenize(input); |
| std::vector<TokenPtr> expected_tokens = {L_PARENS, ETHER, SRC, xxx, R_PARENS, AND, |
| L_PARENS, IP6, OR, IP, R_PARENS, AND, |
| L_PARENS, TCP, DST, PORT, twentythree, R_PARENS}; |
| CheckTokenVectorString(expected_tokens, tokens); |
| } |
| |
| void VisitorTest() { |
| TokenPtr token = literal("testestest"); |
| TokenPtr port_token = port("20-30"); |
| bool token_fail = false; |
| bool port_token_fail = false; |
| std::string literal_str = ""; |
| uint16_t beg_port = 0; |
| uint16_t end_port = 0; |
| |
| auto token_visitor = |
| FunctionalTokenVisitor([&literal_str](TokenPtr t) { literal_str = t->get_term(); }, |
| [&token_fail](PortTokenPtr /*t*/) { token_fail = true; }); |
| |
| auto port_token_visitor = |
| FunctionalTokenVisitor([&port_token_fail](TokenPtr /*t*/) { port_token_fail = true; }, |
| [&beg_port, &end_port](PortTokenPtr t) { |
| beg_port = t->begin(); |
| end_port = t->end(); |
| }); |
| |
| token->accept(&token_visitor); |
| port_token->accept(&port_token_visitor); |
| |
| EXPECT_EQ("testestest", literal_str); |
| EXPECT_EQ(20, beg_port); |
| EXPECT_EQ(30, end_port); |
| EXPECT_FALSE(token_fail); |
| EXPECT_FALSE(port_token_fail); |
| } |
| |
| static inline FunctionalTokenVisitor PortTokenVisitor(bool* fail, uint16_t* beg_port, |
| uint16_t* end_port) { |
| FunctionalTokenVisitor visitor( |
| // Sets some failure `bool` to true when the visitor finds a non-port token. |
| [fail](TokenPtr /*t*/) { *fail = true; }, |
| // Sets the port range when the visitor finds a port token. |
| [beg_port, end_port](PortTokenPtr t) { |
| *beg_port = t->begin(); |
| *end_port = t->end(); |
| }); |
| return visitor; |
| } |
| |
| void NamedPortTest() { |
| TokenPtr token = named_port("FancyPort", "FANCY", 10, 1000, 42); |
| TokenPtr syn = named_port("FANCY", "FANCIER", 20, 2000); |
| TokenPtr add_syn = named_port("FANCIER", 30, 3000); |
| bool fail = false; |
| uint16_t beg_port = 0; |
| uint16_t end_port = 0; |
| FunctionalTokenVisitor visitor = PortTokenVisitor(&fail, &beg_port, &end_port); |
| token->accept(&visitor); |
| |
| EXPECT_EQ("FancyPort", token->get_term()); |
| EXPECT_EQ(42, token->get_tag<uint8_t>()); |
| EXPECT_EQ(10, beg_port); |
| EXPECT_EQ(1000, end_port); |
| EXPECT_FALSE(fail); |
| EXPECT_TRUE(token == syn); |
| EXPECT_TRUE(token == add_syn); |
| } |
| |
| static inline std::string CheckPort(const TokenPtr& token, bool* fail, uint16_t* beg_port, |
| uint16_t* end_port) { |
| *fail = false; |
| *beg_port = 0; |
| *end_port = 0; |
| FunctionalTokenVisitor visitor = PortTokenVisitor(fail, beg_port, end_port); |
| token->accept(&visitor); |
| return token->get_term(); |
| } |
| |
| using PortRanges = std::vector<std::optional<PortRange>>; |
| static void CheckPortVector(std::vector<TokenPtr> tokens, std::vector<std::string> terms, |
| PortRanges ranges) { |
| EXPECT_EQ(tokens.size(), terms.size()); |
| EXPECT_EQ(tokens.size(), ranges.size()); |
| auto term = terms.begin(); |
| auto range = ranges.begin(); |
| bool fail = false; |
| uint16_t beg_port = 0; |
| uint16_t end_port = 0; |
| for (const TokenPtr& token : tokens) { |
| CheckPort(token, &fail, &beg_port, &end_port); |
| if (*range == std::nullopt) { |
| EXPECT_TRUE(fail); |
| } else { |
| EXPECT_FALSE(fail); |
| EXPECT_EQ((*range)->first, beg_port); |
| EXPECT_EQ((*range)->second, end_port); |
| } |
| EXPECT_EQ(*term, token->get_term()); |
| ++term; |
| ++range; |
| } |
| } |
| |
| void PortTest() { |
| std::vector<TokenPtr> tokens = {named_port("MYPORT", 1, 1), |
| port("MYPORT"), |
| port("42"), |
| port("25-35"), |
| port("YOURPORT"), |
| port("42,51"), |
| port("-42"), |
| port("1--42"), |
| port("100-50"), |
| port("55-66000"), |
| port("1-ftpxfer"), |
| port("ftpxfer-ftpctl")}; |
| std::vector<std::string> terms = {"MYPORT", "MYPORT", "42", "25-35", |
| "YOURPORT", "42,51", "-42", "1--42", |
| "100-50", "55-66000", "1-ftpxfer", "ftpxfer-ftpctl"}; |
| PortRanges ranges = {std::optional(PortRange(1, 1)), |
| std::optional(PortRange(1, 1)), |
| std::optional(PortRange(42, 42)), |
| std::optional(PortRange(25, 35)), |
| std::nullopt, |
| std::nullopt, |
| std::nullopt, |
| std::nullopt, |
| std::nullopt, |
| std::nullopt, |
| std::nullopt, |
| std::nullopt}; |
| CheckPortVector(tokens, terms, ranges); |
| } |
| |
| void PortTokenizationTest() { |
| TokenPtr named1 = named_port("MYPORT", 1, 1); |
| TokenPtr named2 = named_port("THISPORT", "THATPORT", 2, 2); |
| std::string input = "6!15!10-20!MYPORT!YOURPORT!30-10! !!37!THATPORT"; |
| // In actual use we will probably use ',' as the delimiter. |
| // Here we test that '!' works too. |
| std::vector<TokenPtr> tokens = mult_ports('!', input); |
| std::vector<std::string> terms = {"6", "15", "10-20", "MYPORT", "YOURPORT", "30-10", |
| " ", "", "37", "THISPORT"}; // Careful with synonyms! |
| PortRanges ranges = {std::optional(PortRange(6, 6)), |
| std::optional(PortRange(15, 15)), |
| std::optional(PortRange(10, 20)), |
| std::optional(PortRange(1, 1)), |
| std::nullopt, |
| std::nullopt, |
| std::nullopt, |
| std::nullopt, |
| std::optional(PortRange(37, 37)), |
| std::optional(PortRange(2, 2))}; |
| |
| std::vector<TokenPtr> empty = mult_ports('!', ""); |
| EXPECT_TRUE(empty.empty()); |
| |
| CheckPortVector(tokens, terms, ranges); |
| } |
| |
| void RealNamedPortsTokenizationTest() { |
| std::string input = "75,21,10-20,ssh,http,dbglog,ftpxfer,ftpctl,badname,,SSH"; |
| std::vector<TokenPtr> tokens = mult_ports(',', input); |
| std::vector<std::string> terms = {"75", "21", "10-20", "ssh", "http", "dbglog", |
| "ftpxfer", "ftpctl", "badname", "", "SSH"}; |
| PortRanges ranges = {std::optional(PortRange(75, 75)), |
| std::optional(PortRange(21, 21)), |
| std::optional(PortRange(10, 20)), |
| std::optional(PortRange(22, 22)), |
| std::optional(PortRange(80, 80)), |
| std::optional(PortRange(DEBUGLOG_PORT, DEBUGLOG_PORT)), |
| std::optional(PortRange(20, 20)), |
| std::optional(PortRange(21, 21)), |
| std::nullopt, |
| std::nullopt, |
| std::nullopt}; |
| |
| std::vector<TokenPtr> empty = mult_ports(',', ""); |
| EXPECT_TRUE(empty.empty()); |
| |
| CheckPortVector(tokens, terms, ranges); |
| } |
| |
| void OneOfTest() { |
| TokenPtr t1 = keyword("KEYWORD"); |
| TokenPtr t2 = literal("KEYWORD"); |
| TokenPtr t3 = literal("foo"); |
| TokenPtr t4 = literal("foo"); |
| TokenPtr t5 = literal("bar"); |
| TokenPtr t6 = port("50"); |
| TokenPtr t7 = named_port("SOMEPORT", 50, 50); |
| TokenPtr t8 = port("SOMEPORT"); |
| |
| EXPECT_TRUE(t1->one_of(t3, t4, t5, t6, t1)); |
| EXPECT_TRUE(t2->one_of(t3, t4, t5, t6, t1)); |
| EXPECT_FALSE(t3->one_of(t4, t5, t6, t1)); |
| EXPECT_TRUE(t4->one_of(t1, t2, t4, t5, t6)); |
| EXPECT_TRUE(t5->one_of(t5)); |
| EXPECT_FALSE(t6->one_of(port("50"), t7, t8)); |
| EXPECT_TRUE(t7->one_of(t8)); |
| EXPECT_TRUE(t8->one_of(t1, t5, t7)); |
| } |
| }; |
| |
| } // namespace netdump::test |
| |
| #define NETDUMP_TEST(fn) \ |
| TEST(NetdumpTokensTest, fn) { netdump::test::TokensTester().fn(); } |
| NETDUMP_TEST(LiteralTest) |
| NETDUMP_TEST(KeywordTest) |
| NETDUMP_TEST(SynonymTest) |
| NETDUMP_TEST(BasicTokenizeTest) |
| NETDUMP_TEST(TokenizeRealKeywordsTest) |
| NETDUMP_TEST(TokenizeKeywordsLiteralsStringTest) |
| NETDUMP_TEST(VisitorTest) |
| NETDUMP_TEST(NamedPortTest) |
| NETDUMP_TEST(PortTest) |
| NETDUMP_TEST(PortTokenizationTest) |
| NETDUMP_TEST(RealNamedPortsTokenizationTest) |
| NETDUMP_TEST(OneOfTest) |
| #undef NETDUMP_TEST |