From: Michal 'vorner' Vaner Date: Wed, 14 Nov 2012 10:01:33 +0000 (+0100) Subject: [2375] Tests for ungetting tokens X-Git-Tag: bind10-1.0.0-beta-release~84^2~3^2~20 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0287ca83ae439396e9ea478e9e4bfe2ee78308bb;p=thirdparty%2Fkea.git [2375] Tests for ungetting tokens --- diff --git a/src/lib/dns/master_lexer.cc b/src/lib/dns/master_lexer.cc index 4b02bc0f4a..bdb81ded50 100644 --- a/src/lib/dns/master_lexer.cc +++ b/src/lib/dns/master_lexer.cc @@ -418,23 +418,30 @@ namespace { class FakeState : public State { public: FakeState(const State* next, size_t eat_chars, - MasterLexer::Token* token = NULL, - const boost::function& callback = - boost::function()) : + MasterLexer::Token* token, + int paren_change, const bool* set_eol, + const boost::function& callback) : next_(next), eat_chars_(eat_chars), token_(token), + paren_change_(paren_change), + set_eol_(set_eol), callback_(callback) {} virtual const State* handle(MasterLexer& lexer) const { + std::string input; for (size_t i = 0; i < eat_chars_; ++i) { - getLexerImpl(lexer)->source_->getChar(); + input += getLexerImpl(lexer)->source_->getChar(); + } + if (!callback_.empty()) { + callback_(input); } if (token_ != NULL) { getLexerImpl(lexer)->token_ = *token_; } - if (!callback_.empty()) { - callback_(); + getLexerImpl(lexer)->paren_count_ += paren_change_; + if (set_eol_ != NULL) { + getLexerImpl(lexer)->last_was_eol_ = *set_eol_; } return (next_); } @@ -442,7 +449,9 @@ private: const State* const next_; size_t eat_chars_; MasterLexer::Token* const token_; - const boost::function callback_; + const int paren_change_; + const bool* const set_eol_; + const boost::function callback_; }; } @@ -450,10 +459,12 @@ private: State* State::getFakeState(const State* next, size_t eat_chars, MasterLexer::Token* token, - const boost::function& callback) + int paren_change, const bool* set_eol, + const boost::function& callback) { // Just allocate new FakeState with the parameters. - return (new FakeState(next, eat_chars, token, callback)); + return (new FakeState(next, eat_chars, token, paren_change, set_eol, + callback)); } } // namespace master_lexer_internal diff --git a/src/lib/dns/master_lexer_state.h b/src/lib/dns/master_lexer_state.h index cfa00e6f8a..301151bec9 100644 --- a/src/lib/dns/master_lexer_state.h +++ b/src/lib/dns/master_lexer_state.h @@ -115,7 +115,9 @@ public: /// /// The returned State will eat eat_chars from the input source, /// it'll set the given token if not NULL, call the given callback - /// and return the next state when its handle() is called. + /// and return the next state when its handle() is called. Also, the + /// parentheses count is changed accordingly a the last EOL condition + /// set if provided. /// /// This is provided only for testing purposes. MasterLexer shouldn't /// need this method. @@ -123,8 +125,11 @@ public: /// The caller is responsible for deleting the State. static State* getFakeState(const State* next, size_t eat_chars, MasterLexer::Token* token = NULL, - const boost::function& callback = - boost::function()); + int paren_change = 0, + const bool* set_eol = NULL, + const boost::function& callback = + boost::function()); /// \name Read-only accessors for testing purposes. /// diff --git a/src/lib/dns/tests/master_lexer_unittest.cc b/src/lib/dns/tests/master_lexer_unittest.cc index a36d0a8679..c649fc8596 100644 --- a/src/lib/dns/tests/master_lexer_unittest.cc +++ b/src/lib/dns/tests/master_lexer_unittest.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -269,4 +270,103 @@ TEST_F(MasterLexerTest, eof) { EXPECT_THROW(lexer.getNextToken(), isc::InvalidOperation); } +void +checkInput(const std::string& expected, const std::string& received) { + EXPECT_EQ(expected, received); +} + +// Check ungetting a token, which should get to the previous state. We do +// so with changing the state a little bit. +TEST_F(MasterLexerTest, ungetSimple) { + ss << "12345"; + lexer.pushSource(ss); + + const bool true_value = true, false_value = false; + // Make sure we change the state to non-default, so we return to previous + // not default state. + MasterLexer::Token t0(MasterLexer::Token::INITIAL_WS); + scoped_ptr s0(State::getFakeState(NULL, 1, &t0, 1, &true_value)); + lexer.pushFakeStart(s0.get()); + EXPECT_EQ(MasterLexer::Token::INITIAL_WS, lexer.getNextToken().getType()); + + // Prepare the token to get and return + const std::string expected = "234"; + MasterLexer::Token token(MasterLexer::Token::END_OF_LINE); + // Change the internal state with it too. So we can check it is retured. + scoped_ptr state(State::getFakeState(NULL, 3, &token, 1, + &false_value, + boost::bind(&checkInput, + expected, _1))); + lexer.pushFakeStart(state.get()); + + // Check the internal state before getting the token + // We access the lexer through any state, so use the one we have. + EXPECT_EQ(1, state->getParenCount(lexer)); + EXPECT_TRUE(state->wasLastEOL(lexer)); + EXPECT_EQ(MasterLexer::Token::INITIAL_WS, + state->getToken(lexer).getType()); + + // Now get the token and check the state changed + EXPECT_EQ(MasterLexer::Token::END_OF_LINE, lexer.getNextToken().getType()); + EXPECT_EQ(2, state->getParenCount(lexer)); + EXPECT_FALSE(state->wasLastEOL(lexer)); + EXPECT_EQ(MasterLexer::Token::END_OF_LINE, + state->getToken(lexer).getType()); + + // Return the token back. Check the state is as it was before. + lexer.ungetToken(); + EXPECT_EQ(1, state->getParenCount(lexer)); + EXPECT_TRUE(state->wasLastEOL(lexer)); + EXPECT_EQ(MasterLexer::Token::INITIAL_WS, + state->getToken(lexer).getType()); + // By calling getToken again, we verify even the source got back to + // original. We must push it as a fake start again so it is picked. + lexer.pushFakeStart(state.get()); + EXPECT_EQ(MasterLexer::Token::END_OF_LINE, lexer.getNextToken().getType()); + EXPECT_EQ(2, state->getParenCount(lexer)); + EXPECT_FALSE(state->wasLastEOL(lexer)); + EXPECT_EQ(MasterLexer::Token::END_OF_LINE, + state->getToken(lexer).getType()); +} + +// Check ungetting token without overriding the start method. We also +// check it works well with changing options between the calls. +TEST_F(MasterLexerTest, ungetRealOptions) { + ss << " \n"; + lexer.pushSource(ss); + + // If we call it the usual way, it skips up to the newline and returns + // it + EXPECT_EQ(MasterLexer::Token::END_OF_LINE, lexer.getNextToken().getType()); + + // Now we return it. If we call it again, but with different options, + // we get the initial whitespace. + EXPECT_EQ(MasterLexer::Token::INITIAL_WS, + lexer.getNextToken(MasterLexer::INITIAL_WS).getType()); +} + +// Test we can't unget a token before we get one +TEST_F(MasterLexerTest, ungetBeforeGet) { + lexer.pushSource(ss); // Just to eliminate the missing source problem + EXPECT_THROW(lexer.ungetToken(), isc::InvalidOperation); +} + +// Test we can't unget a token after a source switch, even when we got +// something before. +TEST_F(MasterLexerTest, ungetAfterSwitch) { + ss << "\n\n"; + lexer.pushSource(ss); + EXPECT_EQ(MasterLexer::Token::END_OF_LINE, lexer.getNextToken().getType()); + // Switch the source + std::stringstream ss2; + ss2 << "\n\n"; + lexer.pushSource(ss2); + EXPECT_THROW(lexer.ungetToken(), isc::InvalidOperation); + // We can get from the new source + EXPECT_EQ(MasterLexer::Token::END_OF_LINE, lexer.getNextToken().getType()); + // And when we drop the current source, we can't unget again + lexer.popSource(); + EXPECT_THROW(lexer.ungetToken(), isc::InvalidOperation); +} + }