// Copyright (C) 2012-2025 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config.h>

#include <util/buffer.h>

#include <dns/labelsequence.h>
#include <dns/name.h>
#include <exceptions/exceptions.h>

#include <gtest/gtest.h>

#include <boost/functional/hash.hpp>

#include <string>
#include <vector>
#include <utility>
#include <set>

using namespace isc::dns;
using namespace std;

// XXX: this is defined as class static constants, but some compilers
// seemingly cannot find the symbols when used in the EXPECT_xxx macros.
const size_t LabelSequence::MAX_SERIALIZED_LENGTH;

namespace {

// Common check that two labelsequences are equal
void check_equal(const LabelSequence& ls1, const LabelSequence& ls2) {
    NameComparisonResult result = ls1.compare(ls2);
    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
              result.getRelation()) << ls1.toText() << " != " << ls2.toText();
    EXPECT_EQ(0, result.getOrder()) << ls1.toText() << " != " << ls2.toText();
    EXPECT_EQ(ls1.getLabelCount(), result.getCommonLabels());
}

// Common check for general comparison of two labelsequences
void check_compare(const LabelSequence& ls1, const LabelSequence& ls2,
                   isc::dns::NameComparisonResult::NameRelation relation,
                   size_t common_labels, bool check_order, int order=0) {
    NameComparisonResult result = ls1.compare(ls2);
    EXPECT_EQ(relation, result.getRelation());
    EXPECT_EQ(common_labels, result.getCommonLabels());
    if (check_order) {
        EXPECT_EQ(order, result.getOrder());
    }
}

class LabelSequenceTest : public ::testing::Test {
public:
    LabelSequenceTest() : n1("example.org"), n2("example.com"),
                          n3("example.org"), n4("foo.bar.test.example"),
                          n5("example.ORG"), n6("ExAmPlE.org"),
                          n7("."), n8("foo.example.org.bar"),
                          n9("\\000xample.org"),
                          n10("\\000xample.org"),
                          n11("\\000xample.com"),
                          n12("\\000xamplE.com"),
                          n_maxlabel("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
                                     "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
                                     "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
                                     "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
                                     "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
                                     "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
                                     "0.1.2.3.4.5.6"),
                          ls1(n1), ls2(n2), ls3(n3), ls4(n4), ls5(n5),
                          ls6(n6), ls7(n7), ls8(n8),
                          ls9(n9), ls10(n10), ls11(n11), ls12(n12)
    {};
    // Need to keep names in scope for at least the lifetime of
    // the labelsequences
    Name n1, n2, n3, n4, n5, n6, n7, n8;
    Name n9, n10, n11, n12;
    const Name n_maxlabel;

    LabelSequence ls1, ls2, ls3, ls4, ls5, ls6, ls7, ls8;
    LabelSequence ls9, ls10, ls11, ls12;
};

// Check the assignment operator.
TEST_F(LabelSequenceTest, assign) {
    // Create the label sequence with example.org (n1 name).
    LabelSequence ls(n1);
    EXPECT_TRUE(ls == ls1);

    // Assign the label sequence to example.com (n2 name).
    ls = ls2;
    EXPECT_FALSE(ls == ls1);
    EXPECT_TRUE(ls == ls2);
}

// Basic equality tests
TEST_F(LabelSequenceTest, equals_sensitive) {
    EXPECT_TRUE(ls1.equals(ls1, true));
    EXPECT_FALSE(ls1.equals(ls2, true));
    EXPECT_TRUE(ls1.equals(ls3, true));
    EXPECT_FALSE(ls1.equals(ls4, true));
    EXPECT_FALSE(ls1.equals(ls5, true));
    EXPECT_FALSE(ls1.equals(ls6, true));
    EXPECT_FALSE(ls1.equals(ls7, true));
    EXPECT_FALSE(ls1.equals(ls8, true));

    EXPECT_FALSE(ls2.equals(ls1, true));
    EXPECT_TRUE(ls2.equals(ls2, true));
    EXPECT_FALSE(ls2.equals(ls3, true));
    EXPECT_FALSE(ls2.equals(ls4, true));
    EXPECT_FALSE(ls2.equals(ls5, true));
    EXPECT_FALSE(ls2.equals(ls6, true));
    EXPECT_FALSE(ls2.equals(ls7, true));
    EXPECT_FALSE(ls2.equals(ls8, true));

    EXPECT_FALSE(ls4.equals(ls1, true));
    EXPECT_FALSE(ls4.equals(ls2, true));
    EXPECT_FALSE(ls4.equals(ls3, true));
    EXPECT_TRUE(ls4.equals(ls4, true));
    EXPECT_FALSE(ls4.equals(ls5, true));
    EXPECT_FALSE(ls4.equals(ls6, true));
    EXPECT_FALSE(ls4.equals(ls7, true));
    EXPECT_FALSE(ls4.equals(ls8, true));

    EXPECT_FALSE(ls5.equals(ls1, true));
    EXPECT_FALSE(ls5.equals(ls2, true));
    EXPECT_FALSE(ls5.equals(ls3, true));
    EXPECT_FALSE(ls5.equals(ls4, true));
    EXPECT_TRUE(ls5.equals(ls5, true));
    EXPECT_FALSE(ls5.equals(ls6, true));
    EXPECT_FALSE(ls5.equals(ls7, true));
    EXPECT_FALSE(ls5.equals(ls8, true));

    EXPECT_TRUE(ls9.equals(ls10, true));
    EXPECT_FALSE(ls9.equals(ls11, true));
    EXPECT_FALSE(ls9.equals(ls12, true));
    EXPECT_FALSE(ls11.equals(ls12, true));
}

TEST_F(LabelSequenceTest, equals_insensitive) {
    EXPECT_TRUE(ls1.equals(ls1));
    EXPECT_FALSE(ls1.equals(ls2));
    EXPECT_TRUE(ls1.equals(ls3));
    EXPECT_FALSE(ls1.equals(ls4));
    EXPECT_TRUE(ls1.equals(ls5));
    EXPECT_TRUE(ls1.equals(ls6));
    EXPECT_FALSE(ls1.equals(ls7));

    EXPECT_FALSE(ls2.equals(ls1));
    EXPECT_TRUE(ls2.equals(ls2));
    EXPECT_FALSE(ls2.equals(ls3));
    EXPECT_FALSE(ls2.equals(ls4));
    EXPECT_FALSE(ls2.equals(ls5));
    EXPECT_FALSE(ls2.equals(ls6));
    EXPECT_FALSE(ls2.equals(ls7));

    EXPECT_TRUE(ls3.equals(ls1));
    EXPECT_FALSE(ls3.equals(ls2));
    EXPECT_TRUE(ls3.equals(ls3));
    EXPECT_FALSE(ls3.equals(ls4));
    EXPECT_TRUE(ls3.equals(ls5));
    EXPECT_TRUE(ls3.equals(ls6));
    EXPECT_FALSE(ls3.equals(ls7));

    EXPECT_FALSE(ls4.equals(ls1));
    EXPECT_FALSE(ls4.equals(ls2));
    EXPECT_FALSE(ls4.equals(ls3));
    EXPECT_TRUE(ls4.equals(ls4));
    EXPECT_FALSE(ls4.equals(ls5));
    EXPECT_FALSE(ls4.equals(ls6));
    EXPECT_FALSE(ls4.equals(ls7));

    EXPECT_TRUE(ls5.equals(ls1));
    EXPECT_FALSE(ls5.equals(ls2));
    EXPECT_TRUE(ls5.equals(ls3));
    EXPECT_FALSE(ls5.equals(ls4));
    EXPECT_TRUE(ls5.equals(ls5));
    EXPECT_TRUE(ls5.equals(ls6));
    EXPECT_FALSE(ls5.equals(ls7));

    EXPECT_TRUE(ls9.equals(ls10));
    EXPECT_FALSE(ls9.equals(ls11));
    EXPECT_FALSE(ls9.equals(ls12));
    EXPECT_TRUE(ls11.equals(ls12));
}

// operator==().  This is mostly trivial wrapper, so it should suffice to
// check some basic cases.
TEST_F(LabelSequenceTest, operatorEqual) {
    // cppcheck-suppress duplicateExpression
    EXPECT_TRUE(ls1 == ls1);      // self equivalence
    EXPECT_TRUE(ls1 == LabelSequence(n1)); // equivalent two different objects
    EXPECT_FALSE(ls1 == ls2);      // non equivalent objects
    EXPECT_TRUE(ls1 == ls5);       // it's always case insensitive
}

// Compare tests
TEST_F(LabelSequenceTest, compare) {
    // "example.org." and "example.org.", case sensitive
    NameComparisonResult result = ls1.compare(ls3, true);
    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
              result.getRelation());
    EXPECT_EQ(0, result.getOrder());
    EXPECT_EQ(3, result.getCommonLabels());

    // "example.org." and "example.ORG.", case sensitive
    result = ls3.compare(ls5, true);
    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
              result.getRelation());
    EXPECT_LT(0, result.getOrder());
    EXPECT_EQ(1, result.getCommonLabels());

    // "example.org." and "example.ORG.", case in-sensitive
    result = ls3.compare(ls5);
    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
              result.getRelation());
    EXPECT_EQ(0, result.getOrder());
    EXPECT_EQ(3, result.getCommonLabels());

    Name na("a.example.org");
    Name nb("b.example.org");
    LabelSequence lsa(na);
    LabelSequence lsb(nb);

    // "a.example.org." and "b.example.org.", case in-sensitive
    result = lsa.compare(lsb);
    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
              result.getRelation());
    EXPECT_GT(0, result.getOrder());
    EXPECT_EQ(3, result.getCommonLabels());

    // "example.org." and "b.example.org.", case in-sensitive
    lsa.stripLeft(1);
    result = lsa.compare(lsb);
    EXPECT_EQ(isc::dns::NameComparisonResult::SUPERDOMAIN,
              result.getRelation());
    EXPECT_GT(0, result.getOrder());
    EXPECT_EQ(3, result.getCommonLabels());

    Name nc("g.f.e.d.c.example.org");
    LabelSequence lsc(nc);

    // "g.f.e.d.c.example.org." and "b.example.org" (not absolute), case
    // in-sensitive; the absolute one is always smaller.
    lsb.stripRight(1);
    result = lsc.compare(lsb);
    EXPECT_EQ(isc::dns::NameComparisonResult::NONE, result.getRelation());
    EXPECT_GT(0, result.getOrder());
    EXPECT_EQ(0, result.getCommonLabels());

    // "g.f.e.d.c.example.org." and "example.org.", case in-sensitive
    result = lsc.compare(ls1);
    EXPECT_EQ(isc::dns::NameComparisonResult::SUBDOMAIN,
              result.getRelation());
    EXPECT_LT(0, result.getOrder());
    EXPECT_EQ(3, result.getCommonLabels());

    // "e.d.c.example.org." and "example.org.", case in-sensitive
    lsc.stripLeft(2);
    result = lsc.compare(ls1);
    EXPECT_EQ(isc::dns::NameComparisonResult::SUBDOMAIN,
              result.getRelation());
    EXPECT_LT(0, result.getOrder());
    EXPECT_EQ(3, result.getCommonLabels());

    // "example.org." and "example.org.", case in-sensitive
    lsc.stripLeft(3);
    result = lsc.compare(ls1);
    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
              result.getRelation());
    EXPECT_EQ(0, result.getOrder());
    EXPECT_EQ(3, result.getCommonLabels());

    // "." and "example.org.", case in-sensitive
    lsc.stripLeft(2);
    result = lsc.compare(ls1);
    EXPECT_EQ(isc::dns::NameComparisonResult::SUPERDOMAIN,
              result.getRelation());
    EXPECT_GT(0, result.getOrder());
    EXPECT_EQ(1, result.getCommonLabels());

    Name nd("a.b.c.isc.example.org");
    LabelSequence lsd(nd);
    Name ne("w.x.y.isc.EXAMPLE.org");
    LabelSequence lse(ne);

    // "a.b.c.isc.example.org." and "w.x.y.isc.EXAMPLE.org.",
    // case sensitive
    result = lsd.compare(lse, true);
    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
              result.getRelation());
    EXPECT_LT(0, result.getOrder());
    EXPECT_EQ(2, result.getCommonLabels());

    // "a.b.c.isc.example.org." and "w.x.y.isc.EXAMPLE.org.",
    // case in-sensitive
    result = lsd.compare(lse);
    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
              result.getRelation());
    EXPECT_GT(0, result.getOrder());
    EXPECT_EQ(4, result.getCommonLabels());

    // "isc.example.org." and "isc.EXAMPLE.org.", case sensitive
    lsd.stripLeft(3);
    lse.stripLeft(3);
    result = lsd.compare(lse, true);
    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
              result.getRelation());
    EXPECT_LT(0, result.getOrder());
    EXPECT_EQ(2, result.getCommonLabels());

    // "isc.example.org." and "isc.EXAMPLE.org.", case in-sensitive
    result = lsd.compare(lse);
    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
              result.getRelation());
    EXPECT_EQ(0, result.getOrder());
    EXPECT_EQ(4, result.getCommonLabels());

    Name nf("a.b.c.isc.example.org");
    LabelSequence lsf(nf);
    Name ng("w.x.y.isc.EXAMPLE.org");
    LabelSequence lsg(ng);

    // lsf: "a.b.c.isc.example.org."
    // lsg: "w.x.y.isc.EXAMPLE.org" (not absolute), case in-sensitive.
    // the absolute one is always smaller.
    lsg.stripRight(1);
    result = lsg.compare(lsf);  // lsg > lsf
    EXPECT_EQ(isc::dns::NameComparisonResult::NONE, result.getRelation());
    EXPECT_LT(0, result.getOrder());
    EXPECT_EQ(0, result.getCommonLabels());

    // "a.b.c.isc.example.org" (not absolute) and
    // "w.x.y.isc.EXAMPLE.org" (not absolute), case in-sensitive
    lsf.stripRight(1);
    result = lsg.compare(lsf);
    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
              result.getRelation());
    EXPECT_LT(0, result.getOrder());
    EXPECT_EQ(3, result.getCommonLabels());

    // "a.b.c.isc.example" (not absolute) and
    // "w.x.y.isc.EXAMPLE" (not absolute), case in-sensitive
    lsf.stripRight(1);
    lsg.stripRight(1);
    result = lsg.compare(lsf);
    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
              result.getRelation());
    EXPECT_LT(0, result.getOrder());
    EXPECT_EQ(2, result.getCommonLabels());

    // lsf: "a.b.c" (not absolute) and
    // lsg: "w.x.y" (not absolute), case in-sensitive; a.b.c < w.x.y;
    // no common labels.
    lsf.stripRight(2);
    lsg.stripRight(2);
    result = lsf.compare(lsg);
    EXPECT_EQ(isc::dns::NameComparisonResult::NONE, result.getRelation());
    EXPECT_GT(0, result.getOrder());
    EXPECT_EQ(0, result.getCommonLabels());

    // lsf2: a.b.cc (not absolute); a.b.c < a.b.cc, no common labels.
    const Name nf2("a.b.cc");
    LabelSequence lsf2(nf2);
    lsf2.stripRight(1);
    result = lsf.compare(lsf2);
    EXPECT_EQ(isc::dns::NameComparisonResult::NONE, result.getRelation());
    EXPECT_GT(0, result.getOrder());
    EXPECT_EQ(0, result.getCommonLabels());

    Name nh("aexample.org");
    LabelSequence lsh(nh);
    Name ni("bexample.org");
    LabelSequence lsi(ni);

    // "aexample.org" (not absolute) and
    // "bexample.org" (not absolute), case in-sensitive
    lsh.stripRight(1);
    lsi.stripRight(1);
    result = lsh.compare(lsi);
    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
              result.getRelation());
    EXPECT_GT(0, result.getOrder());
    EXPECT_EQ(1, result.getCommonLabels());

    // "aexample" (not absolute) and
    // "bexample" (not absolute), case in-sensitive;
    // aexample < bexample; no common labels.
    lsh.stripRight(1);
    lsi.stripRight(1);
    result = lsh.compare(lsi);
    EXPECT_EQ(isc::dns::NameComparisonResult::NONE, result.getRelation());
    EXPECT_GT(0, result.getOrder());
    EXPECT_EQ(0, result.getCommonLabels());

    Name nj("example.org");
    LabelSequence lsj(nj);
    Name nk("example.org");
    LabelSequence lsk(nk);

    // "example.org" (not absolute) and
    // "example.org" (not absolute), case in-sensitive
    lsj.stripRight(1);
    lsk.stripRight(1);
    result = lsj.compare(lsk);
    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
              result.getRelation());
    EXPECT_EQ(0, result.getOrder());
    EXPECT_EQ(2, result.getCommonLabels());

    // "example" (not absolute) and
    // "example" (not absolute), case in-sensitive
    lsj.stripRight(1);
    lsk.stripRight(1);
    result = lsj.compare(lsk);
    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
              result.getRelation());
    EXPECT_EQ(0, result.getOrder());
    EXPECT_EQ(1, result.getCommonLabels());
}

void
getDataCheck(const uint8_t* expected_data, size_t expected_len,
             const LabelSequence& ls)
{
    size_t len;
    const uint8_t* data = ls.getData(&len);
    ASSERT_EQ(expected_len, len) << "Expected data: " << expected_data <<
                                    ", label sequence: " << ls;
    EXPECT_EQ(expected_len, ls.getDataLength()) <<
        "Expected data: " << expected_data <<
        ", label sequence: " << ls;
    for (size_t i = 0; i < len; ++i) {
        EXPECT_EQ(expected_data[i], data[i]) <<
          "Difference at pos " << i << ": Expected data: " << expected_data <<
          ", label sequence: " << ls;
    }
}

// Convenient data converter for expected data.  Label data must be of
// uint8_t*, while it's convenient if we can specify some test data in
// plain string (which is of char*).  This wrapper converts the latter to
// the former in a safer way.
void
getDataCheck(const char* expected_char_data, size_t expected_len,
             const LabelSequence& ls)
{
    const vector<uint8_t> expected_data(expected_char_data,
                                        expected_char_data + expected_len);
    getDataCheck(&expected_data[0], expected_len, ls);
}

TEST_F(LabelSequenceTest, getData) {
    getDataCheck("\007example\003org\000", 13, ls1);
    getDataCheck("\007example\003com\000", 13, ls2);
    getDataCheck("\007example\003org\000", 13, ls3);
    getDataCheck("\003foo\003bar\004test\007example\000", 22, ls4);
    getDataCheck("\007example\003ORG\000", 13, ls5);
    getDataCheck("\007ExAmPlE\003org\000", 13, ls6);
    getDataCheck("\000", 1, ls7);
};

TEST_F(LabelSequenceTest, stripLeft) {
    EXPECT_TRUE(ls1.equals(ls3));
    ls1.stripLeft(0);
    getDataCheck("\007example\003org\000", 13, ls1);
    EXPECT_TRUE(ls1.equals(ls3));
    ls1.stripLeft(1);
    getDataCheck("\003org\000", 5, ls1);
    EXPECT_FALSE(ls1.equals(ls3));
    ls1.stripLeft(1);
    getDataCheck("\000", 1, ls1);
    EXPECT_TRUE(ls1.equals(ls7));

    ls2.stripLeft(2);
    getDataCheck("\000", 1, ls2);
    EXPECT_TRUE(ls2.equals(ls7));
}

TEST_F(LabelSequenceTest, stripRight) {
    EXPECT_TRUE(ls1.equals(ls3));
    ls1.stripRight(1);
    getDataCheck("\007example\003org", 12, ls1);
    EXPECT_FALSE(ls1.equals(ls3));
    ls1.stripRight(1);
    getDataCheck("\007example", 8, ls1);
    EXPECT_FALSE(ls1.equals(ls3));

    ASSERT_FALSE(ls1.equals(ls2));
    ls2.stripRight(2);
    getDataCheck("\007example", 8, ls2);
    EXPECT_TRUE(ls1.equals(ls2));
}

TEST_F(LabelSequenceTest, stripOutOfRange) {
    EXPECT_THROW(ls1.stripLeft(100), isc::OutOfRange);
    EXPECT_THROW(ls1.stripLeft(5), isc::OutOfRange);
    EXPECT_THROW(ls1.stripLeft(4), isc::OutOfRange);
    EXPECT_THROW(ls1.stripLeft(3), isc::OutOfRange);
    getDataCheck("\007example\003org\000", 13, ls1);

    EXPECT_THROW(ls1.stripRight(100), isc::OutOfRange);
    EXPECT_THROW(ls1.stripRight(5), isc::OutOfRange);
    EXPECT_THROW(ls1.stripRight(4), isc::OutOfRange);
    EXPECT_THROW(ls1.stripRight(3), isc::OutOfRange);
    getDataCheck("\007example\003org\000", 13, ls1);
}

TEST_F(LabelSequenceTest, getLabelCount) {
    EXPECT_EQ(3, ls1.getLabelCount());
    ls1.stripLeft(0);
    EXPECT_EQ(3, ls1.getLabelCount());
    ls1.stripLeft(1);
    EXPECT_EQ(2, ls1.getLabelCount());
    ls1.stripLeft(1);
    EXPECT_EQ(1, ls1.getLabelCount());

    EXPECT_EQ(3, ls2.getLabelCount());
    ls2.stripRight(1);
    EXPECT_EQ(2, ls2.getLabelCount());
    ls2.stripRight(1);
    EXPECT_EQ(1, ls2.getLabelCount());

    EXPECT_EQ(3, ls3.getLabelCount());
    ls3.stripRight(2);
    EXPECT_EQ(1, ls3.getLabelCount());

    EXPECT_EQ(5, ls4.getLabelCount());
    ls4.stripRight(3);
    EXPECT_EQ(2, ls4.getLabelCount());

    EXPECT_EQ(3, ls5.getLabelCount());
    ls5.stripLeft(2);
    EXPECT_EQ(1, ls5.getLabelCount());
}

TEST_F(LabelSequenceTest, comparePart) {
    EXPECT_FALSE(ls1.equals(ls8));

    // strip root label from example.org.
    ls1.stripRight(1);
    // strip foo from foo.example.org.bar.
    ls8.stripLeft(1);
    // strip bar. (i.e. bar and root) too
    ls8.stripRight(2);

    EXPECT_TRUE(ls1.equals(ls8));

    // Data comparison
    size_t len;
    const uint8_t* data = ls1.getData(&len);
    getDataCheck(data, len, ls8);
}

TEST_F(LabelSequenceTest, isAbsolute) {
    ASSERT_TRUE(ls1.isAbsolute());

    ls1.stripLeft(1);
    ASSERT_TRUE(ls1.isAbsolute());
    ls1.stripRight(1);
    ASSERT_FALSE(ls1.isAbsolute());

    ASSERT_TRUE(ls2.isAbsolute());
    ls2.stripRight(1);
    ASSERT_FALSE(ls2.isAbsolute());

    ASSERT_TRUE(ls3.isAbsolute());
    ls3.stripLeft(2);
    ASSERT_TRUE(ls3.isAbsolute());
}

TEST_F(LabelSequenceTest, toText) {
    EXPECT_EQ(".", ls7.toText());

    EXPECT_EQ("example.org.", ls1.toText());
    ls1.stripLeft(1);
    EXPECT_EQ("org.", ls1.toText());
    ls1.stripLeft(1);
    EXPECT_EQ(".", ls1.toText());

    EXPECT_EQ("example.com.", ls2.toText());
    ls2.stripRight(1);
    EXPECT_EQ("example.com", ls2.toText());
    ls2.stripRight(1);
    EXPECT_EQ("example", ls2.toText());

    EXPECT_EQ("foo.example.org.bar.", ls8.toText());
    ls8.stripRight(2);
    EXPECT_EQ("foo.example.org", ls8.toText());

    EXPECT_EQ(".", ls7.toText());
    EXPECT_THROW(ls7.stripLeft(1), isc::OutOfRange);

    Name n_long1("012345678901234567890123456789"
                 "012345678901234567890123456789012."
                 "012345678901234567890123456789"
                 "012345678901234567890123456789012."
                 "012345678901234567890123456789"
                 "012345678901234567890123456789012."
                 "012345678901234567890123456789"
                 "0123456789012345678901234567890");
    LabelSequence ls_long1(n_long1);

    EXPECT_EQ("012345678901234567890123456789"
              "012345678901234567890123456789012."
              "012345678901234567890123456789"
              "012345678901234567890123456789012."
              "012345678901234567890123456789"
              "012345678901234567890123456789012."
              "012345678901234567890123456789"
              "0123456789012345678901234567890.", ls_long1.toText());
    ls_long1.stripRight(1);
    EXPECT_EQ("012345678901234567890123456789"
              "012345678901234567890123456789012."
              "012345678901234567890123456789"
              "012345678901234567890123456789012."
              "012345678901234567890123456789"
              "012345678901234567890123456789012."
              "012345678901234567890123456789"
              "0123456789012345678901234567890", ls_long1.toText());

    LabelSequence ls_long2(n_maxlabel);

    EXPECT_EQ("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
              "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
              "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
              "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
              "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
              "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
              "0.1.2.3.4.5.6.", ls_long2.toText());
    ls_long2.stripRight(1);
    EXPECT_EQ("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
              "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
              "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
              "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
              "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
              "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9."
              "0.1.2.3.4.5.6", ls_long2.toText());
    ls_long2.stripRight(125);
    EXPECT_EQ("0.1", ls_long2.toText());
}

// The following verifies that toRawText() returns a string
// actual characters in place of escape sequences.  We do not
// bother with an exhaustive set of tests here as this is
// not a primary use case.
TEST_F(LabelSequenceTest, toRawText) {
    Name n("a bc.$exa(m)ple.@org");
    LabelSequence l(n);
    EXPECT_EQ("a bc.$exa(m)ple.@org", l.toRawText(true));
    EXPECT_EQ("a bc.$exa(m)ple.@org.", l.toRawText(false));

    // toRawText is not supposed to do any sanity checks.
    // Let's try with a very weird name.
    Name name2("xtra\tchars\n.in.name");
    LabelSequence l2(name2);
    EXPECT_EQ("xtra\tchars\n.in.name.", l2.toRawText(false));
}

// The following are test data used in the getHash test below.  Normally
// we use example/documentation domain names for testing, but in this case
// we'd specifically like to use more realistic data, and are intentionally
// using real-world samples: They are the NS names of root and some top level
// domains as of this test.
const char* const root_servers[] = {
    "a.root-servers.net", "b.root-servers.net", "c.root-servers.net",
    "d.root-servers.net", "e.root-servers.net", "f.root-servers.net",
    "g.root-servers.net", "h.root-servers.net", "i.root-servers.net",
    "j.root-servers.net", "k.root-servers.net", "l.root-servers.net",
    "m.root-servers.net", 0
};

const char* const jp_servers[] = {
    "a.dns.jp", "b.dns.jp", "c.dns.jp", "d.dns.jp", "e.dns.jp",
    "f.dns.jp", "g.dns.jp", 0
};
const char* const cn_servers[] = {
    "a.dns.cn", "b.dns.cn", "c.dns.cn", "d.dns.cn", "e.dns.cn",
    "ns.cernet.net", 0
};
const char* const ca_servers[] = {
    "k.ca-servers.ca", "e.ca-servers.ca", "a.ca-servers.ca", "z.ca-servers.ca",
    "tld.isc-sns.net", "c.ca-servers.ca", "j.ca-servers.ca", "l.ca-servers.ca",
    "sns-pb.isc.org", "f.ca-servers.ca", 0
};

// A helper function used in the getHash test below.
void
hashDistributionCheck(const char* const* servers) {
    const size_t BUCKETS = 64;  // constant used in the MessageRenderer
    set<Name> names;
    vector<size_t> hash_counts(BUCKETS);

    // Store all test names and their super domain names (excluding the
    // "root" label) in the set, calculates their hash values, and increments
    // the counter for the corresponding hash "bucket".
    for (size_t i = 0; servers[i]; ++i) {
        const Name name(servers[i]);
        for (size_t l = 0; l < name.getLabelCount() - 1; ++l) {
            pair<set<Name>::const_iterator, bool> ret =
                names.insert(name.split(l));
            if (ret.second) {
                hash_counts[LabelSequence((*ret.first)).getHash(false) %
                            BUCKETS]++;
            }
        }
    }

    // See how many conflicts we have in the buckets.  For the testing purpose
    // we expect there's at most 2 conflicts in each set, which is an
    // arbitrary choice (it should happen to succeed with the hash function
    // and data we are using; if it's not the case, maybe with an update to
    // the hash implementation, we should revise the test).
    for (size_t i = 0; i < BUCKETS; ++i) {
        EXPECT_GE(3, hash_counts[i]);
    }
}

TEST_F(LabelSequenceTest, getHash) {
    // Trivial case.  The same sequence should have the same hash.
    EXPECT_EQ(ls1.getHash(true), ls1.getHash(true));

    // Check the case-insensitive mode behavior.
    EXPECT_EQ(ls1.getHash(false), ls5.getHash(false));

    // Check that the distribution of hash values is "not too bad" (such as
    // everything has the same hash value due to a stupid bug).  It's
    // difficult to check such things reliably.  We do some ad hoc tests here.
    hashDistributionCheck(root_servers);
    hashDistributionCheck(jp_servers);
    hashDistributionCheck(cn_servers);
    hashDistributionCheck(ca_servers);
}

// test operator<<.  We simply confirm it appends the result of toText().
TEST_F(LabelSequenceTest, LeftShiftOperator) {
    ostringstream oss;
    oss << ls1;
    EXPECT_EQ(ls1.toText(), oss.str());
}

TEST_F(LabelSequenceTest, serialize) {
    // placeholder for serialized data.  We use a sufficiently large space
    // for testing the overwrapping cases below.
    uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH * 3];

    // vector to store expected and actual data
    vector<LabelSequence> actual_labelseqs;
    typedef pair<size_t, const uint8_t*> DataPair;
    vector<DataPair> expected;

    // An absolute sequence directly constructed from a valid name.
    // labels = 3, offset sequence = 0, 8, 12, data = "example.com."
    actual_labelseqs.push_back(ls1);
    const uint8_t expected_data1[] = {
        3, 0, 8, 12, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
        3, 'o', 'r', 'g', 0 };
    expected.push_back(DataPair(sizeof(expected_data1), expected_data1));

    // Strip the original one from right.
    // labels = 2, offset sequence = 0, 8, data = "example.com" (non absolute)
    LabelSequence ls_rstripped = ls1;
    ls_rstripped.stripRight(1);
    actual_labelseqs.push_back(ls_rstripped);
    const uint8_t expected_data2[] = {
        2, 0, 8, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
        3, 'o', 'r', 'g'};
    expected.push_back(DataPair(sizeof(expected_data2), expected_data2));

    // Strip the original one from left.
    // labels = 2, offset sequence = 0, 4, data = "com."
    // Note that offsets are adjusted so that they begin with 0.
    LabelSequence ls_lstripped = ls1;
    ls_lstripped.stripLeft(1);
    actual_labelseqs.push_back(ls_lstripped);
    const uint8_t expected_data3[] = { 2, 0, 4, 3, 'o', 'r', 'g', 0 };
    expected.push_back(DataPair(sizeof(expected_data3), expected_data3));

    // Root label.
    LabelSequence ls_root(Name::ROOT_NAME());
    actual_labelseqs.push_back(ls_root);
    const uint8_t expected_data4[] = { 1, 0, 0 };
    expected.push_back(DataPair(sizeof(expected_data4), expected_data4));

    // Non absolute single-label.
    LabelSequence ls_single = ls_rstripped;
    ls_single.stripRight(1);
    actual_labelseqs.push_back(ls_single);
    const uint8_t expected_data5[] = {
        1, 0, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e' };
    expected.push_back(DataPair(sizeof(expected_data5), expected_data5));

    // Labels containing a longest possible label
    const Name name_longlabel(std::string(63, 'x')); // 63 'x's
    LabelSequence ls_longlabel(name_longlabel);
    actual_labelseqs.push_back(ls_longlabel);
    vector<uint8_t> expected_data6;
    expected_data6.push_back(2); // 2 labels
    expected_data6.push_back(0); // 1st offset
    expected_data6.push_back(64); // 2nd offset
    expected_data6.push_back(63); // 1st label length
    expected_data6.insert(expected_data6.end(), 63, 'x'); // 1st label: 63 'x's
    expected_data6.push_back(0); // 2nd label: trailing 0
    expected.push_back(DataPair(expected_data6.size(), &expected_data6[0]));

    // Max number of labels and longest possible name
    EXPECT_EQ(Name::MAX_WIRE, n_maxlabel.getLength());
    LabelSequence ls_maxlabel(n_maxlabel);
    actual_labelseqs.push_back(ls_maxlabel);
    vector<uint8_t> expected_data7;
    expected_data7.push_back(Name::MAX_LABELS); // number of labels
    for (size_t i = 0; i < Name::MAX_LABELS; ++i) {
        expected_data7.push_back(i * 2); // each label has length and 1 byte
    }
    // Copy wire data of the name
    isc::util::OutputBuffer ob(0);
    n_maxlabel.toWire(ob);
    expected_data7.insert(expected_data7.end(),
                          ob.getVector().cbegin(),
                          ob.getVector().cend());
    expected.push_back(DataPair(expected_data7.size(), &expected_data7[0]));

    // For each data set, serialize the labels and compare the data to the
    // expected one.
    vector<DataPair>::const_iterator it = expected.begin();
    vector<LabelSequence>::const_iterator itl = actual_labelseqs.begin();
    for (; it != expected.end(); ++it, ++itl) {
        SCOPED_TRACE(itl->toText());

        const size_t serialized_len = itl->getSerializedLength();

        ASSERT_GE(LabelSequence::MAX_SERIALIZED_LENGTH, serialized_len);
        itl->serialize(labels_buf, serialized_len);
        EXPECT_EQ(it->first, serialized_len);
        EXPECT_EQ(0, memcmp(it->second, labels_buf, serialized_len));

        EXPECT_EQ(NameComparisonResult::EQUAL,
                  LabelSequence(labels_buf).compare(*itl).getRelation());

        // Shift the data to the middle of the buffer for overwrap check
        uint8_t* const bp = labels_buf;
        std::memcpy(bp + serialized_len, bp, serialized_len);
        // Memory layout is now as follows:
        //   <- ser_len ->          <- ser_len ------>
        // bp             bp+ser_len                  bp+(ser_len*2)
        //                           olen,odata,ndata

        // end of buffer would be the first byte of offsets: invalid.
        EXPECT_THROW(LabelSequence(bp + serialized_len).
                     serialize(bp + 2, serialized_len),
                     isc::BadValue);
        // begin of buffer would be the last byte of ndata: invalid.
        EXPECT_THROW(LabelSequence(bp + serialized_len).
                     serialize(bp + (2 * serialized_len) - 1, serialized_len),
                     isc::BadValue);
        // A boundary safe case: buffer is placed after the sequence data.
        // should cause no disruption.
        LabelSequence(bp + serialized_len).
                     serialize(bp + 2 * serialized_len, serialized_len);
        // A boundary safe case: buffer is placed before the sequence data
        // should cause no disruption. (but the original serialized data will
        // be overridden, so it can't be used any more)
        LabelSequence(bp + serialized_len).
                     serialize(bp + 1, serialized_len);
    }

    EXPECT_THROW(ls1.serialize(labels_buf, ls1.getSerializedLength() - 1),
                 isc::BadValue);
}

#ifdef ENABLE_DEBUG

// These checks are enabled only in debug mode in the LabelSequence
// class.
TEST_F(LabelSequenceTest, badDeserialize) {
    EXPECT_THROW(LabelSequence(0), isc::BadValue);
    const uint8_t zero_offsets[] = { 0 };
    EXPECT_THROW(LabelSequence ls(zero_offsets), isc::BadValue);
    const uint8_t toomany_offsets[] = { Name::MAX_LABELS + 1 };
    EXPECT_THROW(LabelSequence ls(toomany_offsets), isc::BadValue);

    // (second) offset does not match actual label length
    const uint8_t offsets_wrongoffset[] = { 2, 0, 64, 1 };
    EXPECT_THROW(LabelSequence ls(offsets_wrongoffset), isc::BadValue);

    // offset matches, but exceeds MAX_LABEL_LEN
    const uint8_t offsets_toolonglabel[] = { 2, 0, 64, 64 };
    EXPECT_THROW(LabelSequence ls(offsets_toolonglabel), isc::BadValue);

    // Inconsistent data: an offset is lower than the previous offset
    const uint8_t offsets_lower[] = { 3, // # of offsets
                                      0, 2, 1, // offsets
                                      1, 'a', 1, 'b', 0};
    EXPECT_THROW(LabelSequence ls(offsets_lower), isc::BadValue);

    // Inconsistent data: an offset is equal to the previous offset
    const uint8_t offsets_noincrease[] = { 2, 0, 0, 0, 0 };
    EXPECT_THROW(LabelSequence ls(offsets_noincrease), isc::BadValue);
}

#endif

namespace {

// Helper function; repeatedly calls
// - Initially, all three labelsequences should be the same
// - repeatedly performs:
//   - checks all three are equal
//   - stripLeft on ls1
//   - checks ls1 and ls2 are different, and ls2 and ls3 are equal
//   - stripLeft on ls2
//   - checks ls1 and ls2 are equal, and ls2 and ls3 are different
//   - stripLeft on ls3
//
// (this test makes sure the stripLeft of one has no effect on the other
// two, and that the strip properties hold regardless of how they were
// constructed)
//
void stripLeftCheck(LabelSequence ls1, LabelSequence ls2, LabelSequence ls3) {
    ASSERT_LT(1, ls1.getLabelCount());
    while (ls1.getLabelCount() > 1) {
        check_equal(ls1, ls2);
        check_equal(ls2, ls3);

        ls1.stripLeft(1);
        check_compare(ls1, ls2, isc::dns::NameComparisonResult::SUPERDOMAIN,
                      ls1.getLabelCount(), true, -1);
        check_equal(ls2, ls3);

        ls2.stripLeft(1);
        check_equal(ls1, ls2);
        check_compare(ls2, ls3, isc::dns::NameComparisonResult::SUPERDOMAIN,
                      ls1.getLabelCount(), true, -1);

        ls3.stripLeft(1);
    }
}

// Similar to stripLeftCheck, but using stripRight()
void stripRightCheck(LabelSequence ls1, LabelSequence ls2, LabelSequence ls3) {
    ASSERT_LT(1, ls1.getLabelCount());
    while (ls1.getLabelCount() > 1) {
        check_equal(ls1, ls2);
        check_equal(ls2, ls3);

        ls1.stripRight(1);
        check_compare(ls1, ls2, isc::dns::NameComparisonResult::NONE, 0,
                      false);
        check_equal(ls2, ls3);

        ls2.stripRight(1);
        check_equal(ls1, ls2);
        check_compare(ls2, ls3, isc::dns::NameComparisonResult::NONE, 0,
                      false);

        ls3.stripRight(1);
    }
}

} // end anonymous namespace

class ExtendableLabelSequenceTest : public ::testing::Test {
public:
    ExtendableLabelSequenceTest() : bar("bar."),
                                    example_org("example.org"),
                                    foo("foo."),
                                    foo_bar("foo.bar."),
                                    foo_bar_example_org("foo.bar.example.org."),
                                    foo_bar_foo_bar("foo.bar.foo.bar."),
                                    foo_example("foo.example."),
                                    org("org")
    {
        // explicitly set to non-zero data, to make sure
        // we don't try to use data we don't set
        memset(buf, 0xff, LabelSequence::MAX_SERIALIZED_LENGTH);
    }

    Name bar;
    Name example_org;
    Name foo;
    Name foo_bar;
    Name foo_bar_example_org;
    Name foo_bar_foo_bar;
    Name foo_example;
    Name org;

    uint8_t buf[LabelSequence::MAX_SERIALIZED_LENGTH];
};

// Test that 'extendable' labelsequences behave correctly when using
// stripLeft() and stripRight()
TEST_F(ExtendableLabelSequenceTest, extendableLabelSequence) {
    LabelSequence ls1(example_org);
    LabelSequence ls2(example_org);

    LabelSequence els(ls1, buf);
    // ls1 is absolute, so els should be too
    EXPECT_TRUE(els.isAbsolute());
    check_equal(ls1, els);

    ASSERT_EQ(ls1.getDataLength(), els.getDataLength());
    stripLeftCheck(ls1, els, ls2);
    stripRightCheck(ls1, els, ls2);

    // Creating an extendable labelsequence from a non-absolute
    // label sequence should result in a non-absolute label sequence
    ls1.stripRight(1);
    els = LabelSequence(ls1, buf);
    EXPECT_FALSE(els.isAbsolute());
    check_equal(ls1, els);

    // and extending with the root label should make it absolute again
    els.extend(LabelSequence(Name(".")), buf);
    EXPECT_TRUE(els.isAbsolute());
    check_equal(ls2, els);
}

// Test that 'extendable' LabelSequences behave correctly when initialized
// with a stripped source LabelSequence
TEST_F(ExtendableLabelSequenceTest, extendableLabelSequenceLeftStrippedSource) {
    LabelSequence ls1(foo_bar_example_org);
    LabelSequence ls2(foo_bar_example_org);

    while (ls1.getLabelCount() > 2) {
        ls1.stripLeft(1);
        ls2.stripLeft(1);

        LabelSequence els(ls1, buf);

        ASSERT_EQ(ls1.getDataLength(), els.getDataLength());
        stripLeftCheck(ls1, els, ls2);
        stripRightCheck(ls1, els, ls2);
    }
}

TEST_F(ExtendableLabelSequenceTest, extendableLabelSequenceRightStrippedSource) {
    LabelSequence ls1(foo_bar_example_org);
    LabelSequence ls2(foo_bar_example_org);

    while (ls1.getLabelCount() > 2) {
        ls1.stripRight(1);
        ls2.stripRight(1);

        LabelSequence els(ls1, buf);

        ASSERT_EQ(ls1.getDataLength(), els.getDataLength());
        stripLeftCheck(ls1, els, ls2);
        stripRightCheck(ls1, els, ls2);
    }
}

// Check some basic 'extend' functionality
TEST_F(ExtendableLabelSequenceTest, extend) {
    LabelSequence ls1(foo_bar);
    LabelSequence ls2(foo);
    LabelSequence ls3(bar);
    LabelSequence ls4(foo_bar);

    LabelSequence els(ls2, buf);

    check_compare(ls1, els, isc::dns::NameComparisonResult::COMMONANCESTOR, 1,
                  true, -4);
    els.extend(ls3, buf);
    EXPECT_TRUE(els.isAbsolute());

    check_equal(ls1, els);
    stripLeftCheck(ls1, els, ls4);
    stripRightCheck(ls1, els, ls4);

    // strip, then extend again
    els.stripRight(2); // (2, 1 for root label, 1 for last label)
    els.extend(ls3, buf);
    EXPECT_TRUE(els.isAbsolute());
    check_equal(ls1, els);

    // Extending again should make it different
    els.extend(ls3, buf);
    EXPECT_TRUE(els.isAbsolute());
    check_compare(ls1, els, isc::dns::NameComparisonResult::COMMONANCESTOR, 2,
                  true, 4);

    // Extending with a non-absolute name should make it non-absolute as well
    ls3.stripRight(1);
    els.extend(ls3, buf);
    EXPECT_FALSE(els.isAbsolute());

    Name check_name("foo.bar.bar.bar");
    LabelSequence check_ls(check_name);
    check_ls.stripRight(1);
    check_equal(check_ls, els);

    // And try extending when both are not absolute
    els.stripRight(3);
    ls1.stripRight(1);
    EXPECT_FALSE(els.isAbsolute());
    els.extend(ls3, buf);
    EXPECT_FALSE(els.isAbsolute());
    check_equal(ls1, els);

    // Extending non-absolute with absolute should make it absolute again
    EXPECT_FALSE(els.isAbsolute());
    els.extend(LabelSequence(Name("absolute.")), buf);
    EXPECT_TRUE(els.isAbsolute());
    check_equal(LabelSequence(Name("foo.bar.absolute")), els);
}

TEST_F(ExtendableLabelSequenceTest, extendLeftStripped) {
    LabelSequence ls1(foo_example);
    LabelSequence ls2(example_org);
    LabelSequence ls3(org);

    LabelSequence els(ls1, buf);

    els.stripLeft(1);
    els.extend(ls3, buf);
    EXPECT_TRUE(els.isAbsolute());
    check_equal(ls2, els);
}

// Check that when extending with itself, it does not cause horrible failures
TEST_F(ExtendableLabelSequenceTest, extendWithItself) {
    LabelSequence ls1(foo_bar);
    LabelSequence ls2(foo_bar_foo_bar);

    LabelSequence els(ls1, buf);

    els.extend(els, buf);
    EXPECT_TRUE(els.isAbsolute());
    check_equal(ls2, els);

    // Also try for non-absolute names
    ls2.stripRight(1);
    els = LabelSequence(ls1, buf);
    els.stripRight(1);
    els.extend(els, buf);
    EXPECT_FALSE(els.isAbsolute());
    check_equal(ls2, els);

    // Once more, now start out with non-absolute labelsequence
    ls1.stripRight(1);
    els = LabelSequence(ls1, buf);
    els.extend(els, buf);
    EXPECT_FALSE(els.isAbsolute());
    check_equal(ls2, els);
}

// Test that 'extending' with just a root label is a no-op, iff the original
// was already absolute
TEST_F(ExtendableLabelSequenceTest, extendWithRoot) {
    LabelSequence ls1(example_org);

    LabelSequence els(LabelSequence(ls1, buf));
    check_equal(ls1, els);
    els.extend(LabelSequence(Name(".")), buf);
    EXPECT_TRUE(els.isAbsolute());
    check_equal(ls1, els);

    // but not if the original was not absolute (it will be equal to
    // the original labelsequence used above, but not the one it was based
    // on).
    LabelSequence ls2(example_org);
    ls2.stripRight(1);
    els = LabelSequence(ls2, buf);
    EXPECT_FALSE(els.isAbsolute());
    els.extend(LabelSequence(Name(".")), buf);
    EXPECT_TRUE(els.isAbsolute());
    check_equal(ls1, els);
    check_compare(ls2, els, isc::dns::NameComparisonResult::NONE, 0, true, 3);
}

// Check possible failure modes of extend()
TEST_F(ExtendableLabelSequenceTest, extendBadData) {
    LabelSequence ls1(example_org);

    LabelSequence els(ls1, buf);

    // try use with unrelated labelsequence
    EXPECT_THROW(ls1.extend(ls1, buf), isc::BadValue);

    // Create a long name, but so that we can still extend once
    Name longlabel("1234567890123456789012345678901234567890"
                   "12345678901234567890");
    LabelSequence long_ls(longlabel);
    els = LabelSequence(long_ls, buf);
    els.extend(els, buf);
    els.extend(long_ls, buf);
    els.extend(long_ls, buf);
    ASSERT_EQ(245, els.getDataLength());
    // Extending once more with 10 bytes should still work
    els.extend(LabelSequence(Name("123456789")), buf);
    EXPECT_TRUE(els.isAbsolute());

    // Extended label sequence should now look like
    const Name full_name(
        "123456789012345678901234567890123456789012345678901234567890."
        "123456789012345678901234567890123456789012345678901234567890."
        "123456789012345678901234567890123456789012345678901234567890."
        "123456789012345678901234567890123456789012345678901234567890."
        "123456789.");
    const LabelSequence full_ls(full_name);
    check_equal(full_ls, els);

    // But now, even the shortest extension should fail
    EXPECT_THROW(els.extend(LabelSequence(Name("1")), buf), isc::BadValue);

    // Check it hasn't been changed
    EXPECT_TRUE(els.isAbsolute());
    check_equal(full_ls, els);

    // Also check that extending past MAX_LABELS is not possible
    Name shortname("1.");
    LabelSequence short_ls(shortname);
    els = LabelSequence(short_ls, buf);
    for (size_t i=0; i < 126; ++i) {
        els.extend(short_ls, buf);
    }

    // Should now look like this
    const Name full_name2(
        "1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1."
        "1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1."
        "1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1."
        "1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1."
        "1.1.1.1.1.1.1.");
    const LabelSequence full_ls2(full_name2);
    EXPECT_TRUE(els.isAbsolute());
    check_equal(full_ls2, els);

    EXPECT_THROW(els.extend(short_ls, buf), isc::BadValue);

    EXPECT_TRUE(els.isAbsolute());
    check_equal(full_ls2, els);
}

// Check the static fixed 'wildcard' LabelSequence
TEST(WildCardLabelSequence, wildcard) {
    ASSERT_FALSE(LabelSequence::WILDCARD().isAbsolute());
    ASSERT_EQ("*", LabelSequence::WILDCARD().toText());
}

}
