// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+// Note: This file tests both the rdata_encoder and rdata_reader. They are
+// tested together because they form kind the oposite sides of the same
+// functionality.
+
#include <exceptions/exceptions.h>
#include <util/buffer.h>
renderer->writeData(data, data_len);
}
-class RdataEncoderTest : public ::testing::Test {
+class RdataSerializationTest : public ::testing::Test {
protected:
- RdataEncoderTest() : a_rdata_(createRdata(RRType::A(), RRClass::IN(),
+ RdataSerializationTest() : a_rdata_(createRdata(RRType::A(), RRClass::IN(),
"192.0.2.53")),
aaaa_rdata_(createRdata(RRType::AAAA(), RRClass::IN(),
"2001:db8::53")),
"20120715220826 12345 com. FAKE"))
{}
+ // A wraper for RdataEncoder::encode() with buffer overrun check.
+ void encodeWrapper(size_t data_len);
+
+ // Some commonly used RDATA
+ const ConstRdataPtr a_rdata_;
+ const ConstRdataPtr aaaa_rdata_;
+ const ConstRdataPtr rrsig_rdata_;
+
+ RdataEncoder encoder_;
+ vector<uint8_t> encoded_data_;
+ MessageRenderer expected_renderer_;
+ MessageRenderer actual_renderer_;
+ vector<ConstRdataPtr> rdata_list_;
+};
+
+// There are several ways to decode the data. For one, there are
+// more interfaces uses for RdataReader, and we use our own decoder,
+// to check the actual encoded data.
+//
+// These decoding ways are provided by the template parameter.
+template<class DecoderStyle>
+class RdataEncodeDecodeTest : public RdataSerializationTest {
+public:
// This helper test method constructs encodes the given list of RDATAs
// (in rdata_list), and then iterates over the data, rendering the fields
// in the wire format. It then compares the wire data with the one
const vector<ConstRdataPtr>& rdata_list,
size_t expected_varlen_fields,
const vector<ConstRdataPtr>& rrsig_list);
- // A wraper for RdataEncoder::encode() with buffer overrun check.
- void encodeWrapper(size_t data_len);
void addRdataCommon(const vector<ConstRdataPtr>& rrsigs);
void addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs);
+};
- // Some commonly used RDATA
- const ConstRdataPtr a_rdata_;
- const ConstRdataPtr aaaa_rdata_;
- const ConstRdataPtr rrsig_rdata_;
+// Used across more classes and scopes. But it's just uninteresting
+// constant.
+const Name dummy_name2("example.com");
+
+// A decoder that does not use RdataReader. Not recommended for use,
+// but it allows the tests to check the internals of the data.
+class ManualDecoderStyle {
+public:
+ static void decode(const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype,
+ size_t rdata_count,
+ size_t rrsig_count,
+ size_t expected_varlen_fields,
+ // Warning: this test actualy might change the
+ // encoded_data !
+ vector<uint8_t>& encoded_data,
+ MessageRenderer& renderer)
+ {
+ // If this type of RDATA is expected to contain variable-length fields,
+ // we brute force the encoded data, exploiting our knowledge of actual
+ // encoding, then adjust the encoded data excluding the list of length
+ // fields. This is ugly, but for tests only.
+ vector<uint16_t> varlen_list;
+ if (expected_varlen_fields > 0) {
+ const size_t varlen_list_size =
+ rdata_count * expected_varlen_fields * sizeof(uint16_t);
+ ASSERT_LE(varlen_list_size, encoded_data.size());
+ varlen_list.resize(rdata_count * expected_varlen_fields);
+ std::memcpy(&varlen_list[0], &encoded_data[0], varlen_list_size);
+ encoded_data.assign(encoded_data.begin() + varlen_list_size,
+ encoded_data.end());
+ }
+
+ // If RRSIGs are given, we need to extract the list of the RRSIG lengths
+ // and adjust encoded_data_ further.
+ vector<uint16_t> rrsiglen_list;
+ if (rrsig_count > 0) {
+ const size_t rrsig_len_size = rrsig_count * sizeof(uint16_t);
+ ASSERT_LE(rrsig_len_size, encoded_data.size());
+ rrsiglen_list.resize(rrsig_count * rrsig_len_size);
+ std::memcpy(&rrsiglen_list[0], &encoded_data[0], rrsig_len_size);
+ encoded_data.assign(encoded_data.begin() + rrsig_len_size,
+ encoded_data.end());
+ }
+
+ // The set of RR types that require additional section processing.
+ // We'll pass it to renderNameField to check the stored attribute matches
+ // our expectation.
+ std::set<RRType> need_additionals;
+ need_additionals.insert(RRType::NS());
+ need_additionals.insert(RRType::MX());
+ need_additionals.insert(RRType::SRV());
+
+ const bool additional_required =
+ (need_additionals.find(rrtype) != need_additionals.end());
+
+ // Create wire-format data from the encoded data
+ foreachRdataField(rrclass, rrtype, rdata_count, encoded_data,
+ varlen_list,
+ boost::bind(renderNameField, &renderer,
+ additional_required, _1, _2),
+ boost::bind(renderDataField, &renderer, _1, _2));
- RdataEncoder encoder_;
- vector<uint8_t> encoded_data_;
- MessageRenderer expected_renderer_;
- MessageRenderer actual_renderer_;
- vector<ConstRdataPtr> rdata_list_;
+ // 2nd dummy name
+ renderer.writeName(dummy_name2);
+ // Finally, dump any RRSIGs in wire format.
+ foreachRRSig(encoded_data, rrsiglen_list,
+ boost::bind(renderDataField, &renderer, _1, _2));
+ }
};
+typedef ::testing::Types<ManualDecoderStyle> DecoderStyles;
+TYPED_TEST_CASE(RdataEncodeDecodeTest, DecoderStyles);
void
-RdataEncoderTest::encodeWrapper(size_t data_len) {
+RdataSerializationTest::encodeWrapper(size_t data_len) {
// make sure the data buffer is large enough for the canary
encoded_data_.resize(data_len + 2);
// set the canary data
encoded_data_.resize(data_len);
}
+template<class DecoderStyle>
void
-RdataEncoderTest::checkEncode(RRClass rrclass, RRType rrtype,
- const vector<ConstRdataPtr>& rdata_list,
- size_t expected_varlen_fields,
- const vector<ConstRdataPtr>& rrsig_list =
- vector<ConstRdataPtr>())
+RdataEncodeDecodeTest<DecoderStyle>::
+checkEncode(RRClass rrclass, RRType rrtype,
+ const vector<ConstRdataPtr>& rdata_list,
+ size_t expected_varlen_fields,
+ const vector<ConstRdataPtr>& rrsig_list = vector<ConstRdataPtr>())
{
// These two names will be rendered before and after the test RDATA,
// to check in case the RDATA contain a domain name whether it's
// Likewise, dummy_name2 should be able to be fully compressed due to
// the name in the RDATA.
const Name dummy_name("com");
- const Name dummy_name2("example.com");
-
- // The set of RR types that require additional section processing.
- // We'll pass it to renderNameField to check the stored attribute matches
- // our expectation.
- std::set<RRType> need_additionals;
- need_additionals.insert(RRType::NS());
- need_additionals.insert(RRType::MX());
- need_additionals.insert(RRType::SRV());
+
expected_renderer_.clear();
actual_renderer_.clear();
encoded_data_.clear();
- const bool additional_required =
- (need_additionals.find(rrtype) != need_additionals.end());
-
// Build expected wire-format data
expected_renderer_.writeName(dummy_name);
BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
}
encodeWrapper(encoder_.getStorageLength());
- // If this type of RDATA is expected to contain variable-length fields,
- // we brute force the encoded data, exploiting our knowledge of actual
- // encoding, then adjust the encoded data excluding the list of length
- // fields. This is ugly, but we should be able to eliminate this hack
- // at #2096.
- vector<uint16_t> varlen_list;
- if (expected_varlen_fields > 0) {
- const size_t varlen_list_size =
- rdata_list.size() * expected_varlen_fields * sizeof(uint16_t);
- ASSERT_LE(varlen_list_size, encoded_data_.size());
- varlen_list.resize(rdata_list.size() * expected_varlen_fields);
- std::memcpy(&varlen_list[0], &encoded_data_[0], varlen_list_size);
- encoded_data_.assign(encoded_data_.begin() + varlen_list_size,
- encoded_data_.end());
- }
-
- // If RRSIGs are given, we need to extract the list of the RRSIG lengths
- // and adjust encoded_data_ further (this will be unnecessary at #2096,
- // too).
- vector<uint16_t> rrsiglen_list;
- if (rrsig_list.size() > 0) {
- const size_t rrsig_len_size = rrsig_list.size() * sizeof(uint16_t);
- ASSERT_LE(rrsig_len_size, encoded_data_.size());
- rrsiglen_list.resize(rrsig_list.size() * rrsig_len_size);
- std::memcpy(&rrsiglen_list[0], &encoded_data_[0], rrsig_len_size);
- encoded_data_.assign(encoded_data_.begin() + rrsig_len_size,
- encoded_data_.end());
- }
-
- // Create wire-format data from the encoded data
- foreachRdataField(rrclass, rrtype, rdata_list.size(), encoded_data_,
- varlen_list,
- boost::bind(renderNameField, &actual_renderer_,
- additional_required, _1, _2),
- boost::bind(renderDataField, &actual_renderer_, _1, _2));
- // 2nd dummy name
- actual_renderer_.writeName(dummy_name2);
- // Finally, dump any RRSIGs in wire format.
- foreachRRSig(encoded_data_, rrsiglen_list,
- boost::bind(renderDataField, &actual_renderer_, _1, _2));
+ DecoderStyle::decode(rrclass, rrtype, rdata_list.size(), rrsig_list.size(),
+ expected_varlen_fields, encoded_data_,
+ actual_renderer_);
// Two sets of wire-format data should be identical.
matchWireData(expected_renderer_.getData(), expected_renderer_.getLength(),
actual_renderer_.getData(), actual_renderer_.getLength());
}
+template<class DecoderStyle>
void
-RdataEncoderTest::addRdataCommon(const vector<ConstRdataPtr>& rrsigs) {
+RdataEncodeDecodeTest<DecoderStyle>::
+addRdataCommon(const vector<ConstRdataPtr>& rrsigs) {
// Basic check on the encoded data for (most of) all supported RR types,
// in a comprehensive manner.
for (size_t i = 0; test_rdata_list[i].rrclass != NULL; ++i) {
}
}
-TEST_F(RdataEncoderTest, addRdata) {
+TYPED_TEST(RdataEncodeDecodeTest, addRdata) {
vector<ConstRdataPtr> rrsigs;
- addRdataCommon(rrsigs); // basic tests without RRSIGs (empty vector)
+ this->addRdataCommon(rrsigs); // basic tests without RRSIGs (empty vector)
// Test with RRSIGs (covered type doesn't always match, but the encoder
// doesn't check that)
- rrsigs.push_back(rrsig_rdata_);
- addRdataCommon(rrsigs);
+ rrsigs.push_back(this->rrsig_rdata_);
+ this->addRdataCommon(rrsigs);
}
+template<class DecoderStyle>
void
-RdataEncoderTest::addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs) {
+RdataEncodeDecodeTest<DecoderStyle>::
+addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs) {
// Similar to addRdata(), but test with multiple RDATAs.
// Four different cases are tested: a single fixed-len RDATA (A),
// fixed-len data + domain name (MX), variable-len data only (TXT),
checkEncode(RRClass::IN(), RRType::NAPTR(), rdata_list_, 1, rrsigs);
}
-TEST_F(RdataEncoderTest, encodeLargeRdata) {
+TEST_F(RdataSerializationTest, encodeLargeRdata) {
// There should be no reason for a large RDATA to fail in encoding,
// but we check such a case explicitly.
EXPECT_EQ(0, encoded_dhcid.compare(large_dhcid));
}
-TEST_F(RdataEncoderTest, addRdataMulti) {
+TYPED_TEST(RdataEncodeDecodeTest, addRdataMulti) {
vector<ConstRdataPtr> rrsigs;
- addRdataMultiCommon(rrsigs); // test without RRSIGs (empty vector)
+ this->addRdataMultiCommon(rrsigs); // test without RRSIGs (empty vector)
// Tests with two RRSIGs
- rrsigs.push_back(rrsig_rdata_);
+ rrsigs.push_back(this->rrsig_rdata_);
rrsigs.push_back(createRdata(RRType::RRSIG(), RRClass::IN(),
"A 5 2 3600 20120814220826 "
"20120715220826 54321 com. FAKE"));
- addRdataMultiCommon(rrsigs);
+ this->addRdataMultiCommon(rrsigs);
}
-TEST_F(RdataEncoderTest, badAddRdata) {
+TEST_F(RdataSerializationTest, badAddRdata) {
// Some operations must follow start().
EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::InvalidOperation);
EXPECT_THROW(encoder_.getStorageLength(), isc::InvalidOperation);
isc::BadValue);
}
-TEST_F(RdataEncoderTest, addSIGRdataOnly) {
+TEST_F(RdataSerializationTest, addSIGRdataOnly) {
// Encoded data that only contain RRSIGs. Mostly useless, but can happen
// (in a partially broken zone) and it's accepted.
encoder_.start(RRClass::IN(), RRType::A());
EXPECT_EQ(0, encoded_sig.compare(*rrsig_rdata_));
}
-TEST_F(RdataEncoderTest, badAddSIGRdata) {
+TEST_F(RdataSerializationTest, badAddSIGRdata) {
// try adding SIG before start
EXPECT_THROW(encoder_.addSIGRdata(*rrsig_rdata_), isc::InvalidOperation);