Commit 57ebe019 authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

[2096] Test refactor: Make it type-parametric

This will help checking the various supported interfaces to decoding.
parent 4b92c8b2
......@@ -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);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment