Browse Source

[2096] Test refactor: Make it type-parametric

This will help checking the various supported interfaces to decoding.
Michal 'vorner' Vaner 13 years ago
parent
commit
57ebe0199a
1 changed files with 129 additions and 85 deletions
  1. 129 85
      src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc

+ 129 - 85
src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc

@@ -12,6 +12,10 @@
 // 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>
@@ -111,9 +115,9 @@ renderDataField(MessageRenderer* renderer, const uint8_t* data,
     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")),
@@ -123,6 +127,29 @@ protected:
                                           "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
@@ -132,27 +159,87 @@ protected:
                      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
@@ -167,12 +254,13 @@ RdataEncoderTest::encodeWrapper(size_t data_len) {
     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
@@ -181,22 +269,11 @@ RdataEncoderTest::checkEncode(RRClass rrclass, RRType rrtype,
     // 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) {
@@ -221,54 +298,19 @@ RdataEncoderTest::checkEncode(RRClass rrclass, RRType rrtype,
     }
     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) {
@@ -285,18 +327,20 @@ RdataEncoderTest::addRdataCommon(const vector<ConstRdataPtr>& rrsigs) {
     }
 }
 
-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),
@@ -338,7 +382,7 @@ RdataEncoderTest::addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs) {
     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.
 
@@ -357,19 +401,19 @@ TEST_F(RdataEncoderTest, encodeLargeRdata) {
     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);
@@ -447,7 +491,7 @@ TEST_F(RdataEncoderTest, badAddRdata) {
                  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());
@@ -461,7 +505,7 @@ TEST_F(RdataEncoderTest, addSIGRdataOnly) {
     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);