/// handled in \c loadIncremental().
MasterToken handleInitialToken();
- /// \brief Helper method for \c doGenerate().
- ///
- /// This is a helper method for \c doGenerate() that processes the
- /// LHS or RHS for a single iteration in the range that is requested
- /// by the $GENERATE directive and returns a generated string (that
- /// is used to build a name (LHS) or RDATA (RHS) for an RR). See the
- /// commented implementation for details.
- std::string generateForIter(const std::string& str, const int it);
-
- /// \brief Process the $GENERATE directive.
- ///
- /// See the commented implementation for details.
- void doGenerate();
-
/// \brief Process the $ORIGIN directive.
void doOrigin(bool is_optional) {
// Parse and create the new origin. It is relative to the previous
} else if (iequals(directive, "ORIGIN")) {
doOrigin(false);
eatUntilEOL(true);
- } else if (iequals(directive, "GENERATE")) {
- doGenerate();
- eatUntilEOL(true);
} else if (iequals(directive, "TTL")) {
setDefaultTTL(RRTTL(getString()), false);
eatUntilEOL(true);
size_t rr_count_; // number of RRs successfully loaded
};
-namespace { // begin unnamed namespace
-
-/// \brief Generate a dotted nibble sequence.
-///
-/// This method generates a dotted nibble sequence and returns it as a
-/// string. The nibbles are appended from the least significant digit
-/// (in hex representation of \c num) to the most significant digit with
-/// dots ('.') to separate the digits. If \c width is non-zero and the
-/// dotted nibble sequence has not filled the requested width, the rest
-/// of the width is filled with a dotted nibble sequence of 0 nibbles.
-///
-/// Some sample representations:
-///
-/// num = 0x1234, width = 0
-/// "4.3.2.1"
-///
-/// num = 0x1234, width = 1
-/// "4.3.2.1"
-///
-/// num = 0x1234, width = 8
-/// "4.3.2.1"
-///
-/// num = 0x1234, width = 9
-/// "4.3.2.1."
-///
-/// num = 0x1234, width = 10
-/// "4.3.2.1.0"
-///
-/// num = 0x1234, width = 11
-/// "4.3.2.1.0."
-///
-/// num = 0xabcd, width = 0, uppercase = true
-/// "D.C.B.A"
-///
-/// num = 0, width = 0
-/// "0"
-///
-/// num = 0, width = 1
-/// "0"
-///
-/// num = 0, width = 2
-/// "0."
-///
-/// num = 0, width = 3
-/// "0.0"
-///
-/// \param num The number for which the dotted nibble sequence should be
-/// generated.
-/// \param width The width of the generated string. This is only
-/// meaningful when it is larger than the dotted nibble sequence
-/// representation of \c num.
-/// \param uppercase Whether to use uppercase characters in nibble
-/// sequence.
-/// \return A string containing the dotted nibble sequence.
-std::string
-genNibbles(int num, unsigned int width, bool uppercase) {
- static const char *hex = "0123456789abcdef0123456789ABCDEF";
- std::string rstr;
-
- do {
- char ch = hex[(num & 0x0f) + (uppercase ? 16 : 0)];
- num >>= 4;
- rstr.push_back(ch);
-
- if (width > 0) {
- --width;
- }
-
- // If width is non zero then we need to add a label separator.
- // If value is non zero then we need to add another label and
- // that requires a label separator.
- if (width > 0 || num != 0) {
- rstr.push_back('.');
-
- if (width > 0) {
- --width;
- }
- }
- } while ((num != 0) || (width > 0));
-
- return (rstr);
-}
-
-} // end unnamed namespace
-
-std::string
-MasterLoader::MasterLoaderImpl::generateForIter(const std::string& str,
- const int num) {
- std::string rstr;
-
- for (auto it = str.begin(); it != str.end();) {
- switch (*it) {
- case '$':
- // This is the case when the '$' character is encountered in
- // the LHS or RHS. A computed value is added in its place in
- // the generated string.
- ++it;
- if ((it != str.end()) && (*it == '$')) {
- rstr.push_back('$');
- ++it;
- continue;
- }
-
- // The str.end() check is required.
- if ((it == str.end()) || (*it != '{')) {
- // There is no modifier (between {}), so just copy the
- // passed number into the generated string.
- rstr += boost::str(boost::format("%d") % num);
- } else {
- // There is a modifier (between {}). Parse it and handle
- // the various cases below.
- const char* scan_str =
- str.c_str() + std::distance(str.begin(), it);
- int offset = 0;
- unsigned int width;
- char base[2] = {'d', 0}; // char plus null byte
- // cppcheck-suppress invalidscanf_libc
- const int n = sscanf(scan_str, "{%d,%u,%1[doxXnN]}",
- &offset, &width, base);
- switch (n) {
- case 1:
- // Only 1 item was matched (the offset). Copy (num +
- // offset) into the generated string.
- rstr += boost::str(boost::format("%d") % (num + offset));
- break;
-
- case 2: {
- // 2 items were matched (the offset and width). Copy
- // (num + offset) and format it according to the width
- // into the generated string.
- const std::string fmt =
- boost::str(boost::format("%%0%ud") % width);
- rstr += boost::str(boost::format(fmt) % (num + offset));
- break;
- }
-
- case 3:
- // 3 items were matched (offset, width and base).
- if ((base[0] == 'n') || (base[0] == 'N')) {
- // The base is requesting nibbles. Format it
- // specially (see genNibbles() documentation).
- rstr += genNibbles(num + offset, width, (base[0] == 'N'));
- } else {
- // The base is not requesting nibbles. Copy (num +
- // offset) and format it according to the width
- // and base into the generated string.
- const std::string fmt =
- boost::str(boost::format("%%0%u%c") % width % base[0]);
- rstr += boost::str(boost::format(fmt) % (num + offset));
- }
- break;
-
- default:
- // Any other case in the modifiers is an error.
- reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
- "Invalid $GENERATE format modifiers");
- return ("");
- }
-
- // Find the closing brace. Careful that 'it' can be equal
- // to str.end() here.
- while ((it != str.end()) && (*it != '}')) {
- ++it;
- }
- // Skip past the closing brace (if there is one).
- if (it != str.end()) {
- ++it;
- }
- }
- break;
-
- case '\\':
- // This is the case when the '\' character is encountered in
- // the LHS or RHS. The '\' and the following character are
- // copied as-is into the generated string. This is usually
- // used for escaping the $ character.
- rstr.push_back(*it);
- ++it;
- if (it == str.end()) {
- continue;
- }
- rstr.push_back(*it);
- ++it;
- break;
-
- default:
- // This is the default case that handles all other
- // characters. They are copied as-is into the generated
- // string.
- rstr.push_back(*it);
- ++it;
- break;
- }
- }
-
- return (rstr);
-}
-
-void
-MasterLoader::MasterLoaderImpl::doGenerate() {
- // Parse the range token
- const MasterToken& range_token = lexer_.getNextToken(MasterToken::STRING);
- if (range_token.getType() != MasterToken::STRING) {
- reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
- "Invalid $GENERATE syntax");
- return;
- }
- const std::string range = range_token.getString();
-
- // Parse the LHS token
- const MasterToken& lhs_token = lexer_.getNextToken(MasterToken::STRING);
- if (lhs_token.getType() != MasterToken::STRING) {
- reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
- "Invalid $GENERATE syntax");
- return;
- }
- const std::string lhs = lhs_token.getString();
-
- // Parse the TTL, RR class and RR type tokens. Note that TTL and RR
- // class may come in any order, or may be missing (either or
- // both). If TTL is missing, we expect that it was either specified
- // explicitly using $TTL, or is implicitly known from a previous RR,
- // or that this is the SOA RR from which the MINIMUM field is
- // used. It's unlikely that $GENERATE will be used with an SOA RR,
- // but it's possible. The parsing happens within the parseRRParams()
- // helper method which is called below.
- const MasterToken& param_token = lexer_.getNextToken(MasterToken::STRING);
- if (param_token.getType() != MasterToken::STRING) {
- reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
- "Invalid $GENERATE syntax");
- return;
- }
-
- bool explicit_ttl = false;
- const RRType rrtype = parseRRParams(explicit_ttl, param_token);
-
- // Parse the RHS token. It can be a quoted string.
- const MasterToken& rhs_token = lexer_.getNextToken(MasterToken::QSTRING);
- if ((rhs_token.getType() != MasterToken::QSTRING) &&
- (rhs_token.getType() != MasterToken::STRING))
- {
- reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
- "Invalid $GENERATE syntax");
- return;
- }
- const std::string rhs = rhs_token.getString();
-
- // Range can be one of two forms: start-stop or start-stop/step. If
- // the first form is used, then step is set to 1. All of start, stop
- // and step must be positive.
- unsigned int start;
- unsigned int stop;
- unsigned int step;
- // cppcheck-suppress invalidscanf_libc
- const int n = sscanf(range.c_str(), "%u-%u/%u", &start, &stop, &step);
- if ((n < 2) || (stop < start)) {
- reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
- "$GENERATE: invalid range: " + range);
- return;
- }
-
- if (n == 2) {
- step = 1;
- }
-
- // Generate and add the records.
- for (unsigned int i = start; i <= stop; i += step) {
- // Get generated strings for LHS and RHS. LHS goes to form the
- // name, RHS goes to form the RDATA of the RR.
- const std::string generated_name = generateForIter(lhs, i);
- const std::string generated_rdata = generateForIter(rhs, i);
- if (generated_name.empty() || generated_rdata.empty()) {
- // The error should have been sent to the callbacks already
- // by generateForIter().
- reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
- "$GENERATE error");
- return;
- }
-
- // generateForIter() can return a string with a trailing '.' in
- // case of a nibble representation. So we cannot use the
- // relative Name constructor. We use concatenate() which is
- // expensive, but keeps the generated LHS-based Name within the
- // active origin.
- last_name_.reset
- (new Name(Name(generated_name).concatenate(active_origin_)));
- previous_name_ = true;
-
- const rdata::RdataPtr rdata =
- rdata::createRdata(rrtype, zone_class_, generated_rdata);
- // In case we get null, it means there was error creating the
- // Rdata. The errors should have been reported by callbacks_
- // already. We need to decide if we want to continue or not.
- if (rdata) {
- add_callback_(*last_name_, zone_class_, rrtype,
- getCurrentTTL(explicit_ttl, rrtype, rdata),
- rdata);
- // Good, we added another one
- ++rr_count_;
- } else {
- seen_error_ = true;
- if (!many_errors_) {
- ok_ = false;
- complete_ = true;
- // We don't have the exact error here, but it was
- // reported by the error callback.
- isc_throw(MasterLoaderError, "Invalid RR data");
- }
- }
- }
-}
-
MasterToken
MasterLoader::MasterLoaderImpl::handleInitialToken() {
const MasterToken& initial_token =
}
}
-TEST_F(MasterLoaderTest, generate) {
- // Various forms of the directive
- const char* generates[] = {
- "$generate",
- "$GENERATE",
- "$Generate",
- "$GeneratE",
- "\"$GENERATE\"",
- 0
- };
- for (const char** generate = generates; *generate != 0; ++generate) {
- SCOPED_TRACE(*generate);
-
- clear();
- const string directive = *generate;
- const string input =
- "$ORIGIN example.org.\n"
- "before.example.org. 3600 IN A 192.0.2.0\n" +
- directive + " 3-5 host$ A 192.0.2.$\n" +
- "after.example.org. 3600 IN A 192.0.2.255\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_TRUE(loader_->loadedSuccessfully());
- EXPECT_TRUE(errors_.empty());
-
- // The "before" and "after" scaffolding below checks that no
- // extra records are added by $GENERATE outside the requested
- // range.
- checkRR("before.example.org", RRType::A(), "192.0.2.0");
- checkRR("host3.example.org", RRType::A(), "192.0.2.3");
- checkRR("host4.example.org", RRType::A(), "192.0.2.4");
- checkRR("host5.example.org", RRType::A(), "192.0.2.5");
- checkRR("after.example.org", RRType::A(), "192.0.2.255");
- }
-}
-
-TEST_F(MasterLoaderTest, generateRelativeLHS) {
- const string input =
- "$ORIGIN example.org.\n"
- "$GENERATE 1-2 @ 3600 NS ns$.example.org.\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_TRUE(loader_->loadedSuccessfully());
- EXPECT_TRUE(errors_.empty());
-
- checkRR("example.org", RRType::NS(), "ns1.example.org.");
- checkRR("example.org", RRType::NS(), "ns2.example.org.");
-}
-
-TEST_F(MasterLoaderTest, generateInFront) {
- // $ is in the front
- const string input =
- "$ORIGIN example.org.\n"
- "$GENERATE 9-10 $host 3600 TXT \"$ pomegranate\"\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_TRUE(loader_->loadedSuccessfully());
- EXPECT_TRUE(errors_.empty());
-
- checkRR("9host.example.org", RRType::TXT(), "9 pomegranate");
- checkRR("10host.example.org", RRType::TXT(), "10 pomegranate");
-}
-
-TEST_F(MasterLoaderTest, generateInMiddle) {
- // $ is in the middle
- const string input =
- "$ORIGIN example.org.\n"
- "$GENERATE 9-10 num$-host 3600 TXT \"This is $ pomegranate\"\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_TRUE(loader_->loadedSuccessfully());
- EXPECT_TRUE(errors_.empty());
-
- checkRR("num9-host.example.org", RRType::TXT(), "This is 9 pomegranate");
- checkRR("num10-host.example.org", RRType::TXT(), "This is 10 pomegranate");
-}
-
-TEST_F(MasterLoaderTest, generateAtEnd) {
- // $ is at the end
- const string input =
- "$ORIGIN example.org.\n"
- "$GENERATE 9-10 num$-host 3600 TXT Pomegranate$\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_TRUE(loader_->loadedSuccessfully());
- EXPECT_TRUE(errors_.empty());
-
- checkRR("num9-host.example.org", RRType::TXT(), "Pomegranate9");
- checkRR("num10-host.example.org", RRType::TXT(), "Pomegranate10");
-}
-
-TEST_F(MasterLoaderTest, generateWithDoublePlaceholder) {
- const string input =
- "$ORIGIN example.org.\n"
- "$GENERATE 9-10 host$ 3600 TXT \"This is $$ pomegranate\"\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_TRUE(loader_->loadedSuccessfully());
- EXPECT_TRUE(errors_.empty());
-
- checkRR("host9.example.org", RRType::TXT(), "This is $ pomegranate");
- checkRR("host10.example.org", RRType::TXT(), "This is $ pomegranate");
-}
-
-TEST_F(MasterLoaderTest, generateWithEscape) {
- const string input =
- "$ORIGIN example.org.\n"
- "$GENERATE 9-10 host$ 3600 TXT \"This is \\$\\pomegranate\"\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_TRUE(loader_->loadedSuccessfully());
- EXPECT_TRUE(errors_.empty());
-
- checkRR("host9.example.org", RRType::TXT(), "This is \\$\\pomegranate");
- checkRR("host10.example.org", RRType::TXT(), "This is \\$\\pomegranate");
-}
-
-TEST_F(MasterLoaderTest, generateWithParams) {
- const string input =
- "$ORIGIN example.org.\n"
- "$TTL 3600\n"
- "$GENERATE 2-3 host$ A 192.0.2.$\n"
- "$GENERATE 5-6 host$ 3600 A 192.0.2.$\n"
- "$GENERATE 8-9 host$ IN A 192.0.2.$\n"
- "$GENERATE 11-12 host$ IN 3600 A 192.0.2.$\n"
- "$GENERATE 14-15 host$ 3600 IN A 192.0.2.$\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_TRUE(loader_->loadedSuccessfully());
- EXPECT_TRUE(errors_.empty());
-
- checkRR("host2.example.org", RRType::A(), "192.0.2.2");
- checkRR("host3.example.org", RRType::A(), "192.0.2.3");
-
- checkRR("host5.example.org", RRType::A(), "192.0.2.5");
- checkRR("host6.example.org", RRType::A(), "192.0.2.6");
-
- checkRR("host8.example.org", RRType::A(), "192.0.2.8");
- checkRR("host9.example.org", RRType::A(), "192.0.2.9");
-
- checkRR("host11.example.org", RRType::A(), "192.0.2.11");
- checkRR("host12.example.org", RRType::A(), "192.0.2.12");
-
- checkRR("host14.example.org", RRType::A(), "192.0.2.14");
- checkRR("host15.example.org", RRType::A(), "192.0.2.15");
-}
-
-TEST_F(MasterLoaderTest, generateWithStep) {
- const string input =
- "$ORIGIN example.org.\n"
- "$GENERATE 2-9/2 host$ 3600 A 192.0.2.$\n"
- "$GENERATE 12-21/3 host$ 3600 A 192.0.2.$\n"
- "$GENERATE 30-31/1 host$ 3600 A 192.0.2.$\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_TRUE(loader_->loadedSuccessfully());
- EXPECT_TRUE(errors_.empty());
-
- checkRR("host2.example.org", RRType::A(), "192.0.2.2");
- checkRR("host4.example.org", RRType::A(), "192.0.2.4");
- checkRR("host6.example.org", RRType::A(), "192.0.2.6");
- checkRR("host8.example.org", RRType::A(), "192.0.2.8");
-
- checkRR("host12.example.org", RRType::A(), "192.0.2.12");
- checkRR("host15.example.org", RRType::A(), "192.0.2.15");
- checkRR("host18.example.org", RRType::A(), "192.0.2.18");
- checkRR("host21.example.org", RRType::A(), "192.0.2.21");
-
- checkRR("host30.example.org", RRType::A(), "192.0.2.30");
- checkRR("host31.example.org", RRType::A(), "192.0.2.31");
-}
-
-TEST_F(MasterLoaderTest, generateWithModifiers) {
- const string input =
- "$ORIGIN example.org.\n"
- "$TTL 3600\n"
-
- // Use a positive delta of 1 in the LHS and a negative delta of
- // -1 in the RHS
- "$GENERATE 2-9/2 host${1} A 192.0.2.${-1}\n"
-
- "$GENERATE 10-12 host${0,4} A 192.0.2.$\n"
- "$GENERATE 14-15 host${0,4,d} A 192.0.2.$\n"
-
- // Names are case-insensitive, so we use TXT's RDATA to check
- // case with hex representation.
- "$GENERATE 30-31 host$ TXT \"Value ${0,4,x}\"\n"
- "$GENERATE 42-43 host$ TXT \"Value ${0,4,X}\"\n"
-
- // Octal does not use any alphabets
- "$GENERATE 45-46 host${0,4,o} A 192.0.2.$\n"
-
- // Here, the LHS has a trailing dot (which would result in an
- // out-of-zone name), but that should be handled as a relative
- // name.
- "$GENERATE 90-92 ${0,8,n} A 192.0.2.$\n"
-
- // Here, the LHS has no trailing dot, and results in the same
- // number of labels as width=8 above.
- "$GENERATE 94-96 ${0,7,n} A 192.0.2.$\n"
-
- // Names are case-insensitive, so we use TXT's RDATA to check
- // case with nibble representation.
- "$GENERATE 106-107 host$ TXT \"Value ${0,9,n}\"\n"
- "$GENERATE 109-110 host$ TXT \"Value ${0,9,N}\"\n"
-
- // Junk type will not parse and 'd' is assumed. No error is
- // generated (this is to match BIND 9 behavior).
- "$GENERATE 200-201 host${0,4,j} A 192.0.2.$\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_TRUE(loader_->loadedSuccessfully());
- EXPECT_TRUE(errors_.empty());
-
- checkRR("host3.example.org", RRType::A(), "192.0.2.1");
- checkRR("host5.example.org", RRType::A(), "192.0.2.3");
- checkRR("host7.example.org", RRType::A(), "192.0.2.5");
- checkRR("host9.example.org", RRType::A(), "192.0.2.7");
-
- checkRR("host0010.example.org", RRType::A(), "192.0.2.10");
- checkRR("host0011.example.org", RRType::A(), "192.0.2.11");
- checkRR("host0012.example.org", RRType::A(), "192.0.2.12");
-
- checkRR("host0014.example.org", RRType::A(), "192.0.2.14");
- checkRR("host0015.example.org", RRType::A(), "192.0.2.15");
-
- checkRR("host30.example.org", RRType::TXT(), "Value 001e");
- checkRR("host31.example.org", RRType::TXT(), "Value 001f");
-
- checkRR("host42.example.org", RRType::TXT(), "Value 002A");
- checkRR("host43.example.org", RRType::TXT(), "Value 002B");
-
- checkRR("host0055.example.org", RRType::A(), "192.0.2.45");
- checkRR("host0056.example.org", RRType::A(), "192.0.2.46");
-
- checkRR("a.5.0.0.example.org", RRType::A(), "192.0.2.90");
- checkRR("b.5.0.0.example.org", RRType::A(), "192.0.2.91");
- checkRR("c.5.0.0.example.org", RRType::A(), "192.0.2.92");
-
- checkRR("e.5.0.0.example.org", RRType::A(), "192.0.2.94");
- checkRR("f.5.0.0.example.org", RRType::A(), "192.0.2.95");
- checkRR("0.6.0.0.example.org", RRType::A(), "192.0.2.96");
-
- checkRR("host106.example.org", RRType::TXT(), "Value a.6.0.0.0");
- checkRR("host107.example.org", RRType::TXT(), "Value b.6.0.0.0");
- checkRR("host109.example.org", RRType::TXT(), "Value D.6.0.0.0");
- checkRR("host110.example.org", RRType::TXT(), "Value E.6.0.0.0");
-
- checkRR("host0200.example.org", RRType::A(), "192.0.2.200");
- checkRR("host0201.example.org", RRType::A(), "192.0.2.201");
-}
-
-TEST_F(MasterLoaderTest, generateWithNoModifiers) {
- const string input =
- "$ORIGIN example.org.\n"
- "$TTL 3600\n"
- "$GENERATE 10-12 host${} A 192.0.2.$\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_FALSE(loader_->loadedSuccessfully());
- ASSERT_EQ(2, errors_.size()); // For the broken GENERATE
- EXPECT_TRUE(warnings_.empty());
-
- checkCallbackMessage(errors_.at(0),
- "Invalid $GENERATE format modifiers", 3);
- checkCallbackMessage(errors_.at(1),
- "$GENERATE error", 3);
-}
-
-TEST_F(MasterLoaderTest, generateWithBadModifiers) {
- const string input =
- "$ORIGIN example.org.\n"
- "$TTL 3600\n"
- "$GENERATE 10-12 host${GARBAGE} A 192.0.2.$\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_FALSE(loader_->loadedSuccessfully());
- ASSERT_EQ(2, errors_.size()); // For the broken GENERATE
- EXPECT_TRUE(warnings_.empty());
-
- checkCallbackMessage(errors_.at(0),
- "Invalid $GENERATE format modifiers", 3);
- checkCallbackMessage(errors_.at(1),
- "$GENERATE error", 3);
-}
-
-TEST_F(MasterLoaderTest, generateMissingRange) {
- const string input =
- "$ORIGIN example.org.\n"
- "$GENERATE\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_FALSE(loader_->loadedSuccessfully());
- EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
- EXPECT_TRUE(warnings_.empty());
-
- checkCallbackMessage(errors_.at(0),
- "unexpected end of input", 2);
-}
-
-TEST_F(MasterLoaderTest, generateMissingLHS) {
- const string input =
- "$ORIGIN example.org.\n"
- "$GENERATE 2-4\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_FALSE(loader_->loadedSuccessfully());
- EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
- EXPECT_TRUE(warnings_.empty());
-
- checkCallbackMessage(errors_.at(0),
- "unexpected end of input", 2);
-}
-
-TEST_F(MasterLoaderTest, generateMissingType) {
- const string input =
- "$ORIGIN example.org.\n"
- "$GENERATE 2-4 host$\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_FALSE(loader_->loadedSuccessfully());
- EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
- EXPECT_TRUE(warnings_.empty());
-
- checkCallbackMessage(errors_.at(0),
- "unexpected end of input", 2);
-}
-
-TEST_F(MasterLoaderTest, generateMissingRHS) {
- const string input =
- "$ORIGIN example.org.\n"
- "$GENERATE 2-4 host$ A\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_FALSE(loader_->loadedSuccessfully());
- EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
- EXPECT_TRUE(warnings_.empty());
-
- checkCallbackMessage(errors_.at(0),
- "unexpected end of input", 2);
-}
-
-TEST_F(MasterLoaderTest, generateWithBadRangeSyntax) {
- const string input =
- "$ORIGIN example.org.\n"
- "$GENERATE ABCD host$ 3600 A 192.0.2.$\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_FALSE(loader_->loadedSuccessfully());
- EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
- EXPECT_TRUE(warnings_.empty());
-
- checkCallbackMessage(errors_.at(0),
- "$GENERATE: invalid range: ABCD", 2);
-}
-
-TEST_F(MasterLoaderTest, generateWithInvalidRange) {
- // start > stop
- const string input =
- "$ORIGIN example.org.\n"
- "$GENERATE 2-1 host$ 3600 A 192.0.2.$\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_FALSE(loader_->loadedSuccessfully());
- EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
- EXPECT_TRUE(warnings_.empty());
-
- checkCallbackMessage(errors_.at(0),
- "$GENERATE: invalid range: 2-1", 2);
-}
-
-TEST_F(MasterLoaderTest, generateWithInvalidClass) {
- const string input =
- "$ORIGIN example.org.\n"
- "$GENERATE 1-2 host$ 3600 CH A 192.0.2.$\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_FALSE(loader_->loadedSuccessfully());
- EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
- EXPECT_TRUE(warnings_.empty());
-
- checkCallbackMessage(errors_.at(0),
- "Class mismatch: CH vs. IN", 2);
-}
-
-TEST_F(MasterLoaderTest, generateWithNoAvailableTTL) {
- const string input =
- "$ORIGIN example.org.\n"
- "$GENERATE 1-2 host$ A 192.0.2.$\n";
- stringstream ss(input);
- setLoader(ss, Name("example.org."), RRClass::IN(),
- MasterLoader::MANY_ERRORS);
-
- loader_->load();
- EXPECT_FALSE(loader_->loadedSuccessfully());
- EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
- EXPECT_TRUE(warnings_.empty());
-
- checkCallbackMessage(errors_.at(0),
- "no TTL specified; load rejected", 2);
-}
-
// Test the source is correctly popped even after error
TEST_F(MasterLoaderTest, popAfterError) {
const string include_str = "$include " TEST_DATA_SRCDIR