interest: support Parameters element

Add getter/setters for Parameters, encode as packet format v0.3 if
Parameters are present, have decode03 save Parameters element, and
add encode/decode unit tests for v0.3

Refs: #4658

Change-Id: I70c1072f0003a1b757b8dc484a93ef91bac74496
diff --git a/src/interest.cpp b/src/interest.cpp
index 8fc04a3..50f5a13 100644
--- a/src/interest.cpp
+++ b/src/interest.cpp
@@ -77,14 +77,27 @@
       hasDefaultCanBePrefixWarning = true;
     }
 #ifdef NDN_CXX_HAVE_TESTS
-     if (s_errorIfCanBePrefixUnset) {
-       BOOST_THROW_EXCEPTION(std::logic_error("Interest.CanBePrefix is unset"));
-     }
+    if (s_errorIfCanBePrefixUnset) {
+      BOOST_THROW_EXCEPTION(std::logic_error("Interest.CanBePrefix is unset"));
+    }
 #endif // NDN_CXX_HAVE_TESTS
   }
 
+  if (hasParameters()) {
+    return encode03(encoder);
+  }
+  else {
+    return encode02(encoder);
+  }
+}
+
+template<encoding::Tag TAG>
+size_t
+Interest::encode02(EncodingImpl<TAG>& encoder) const
+{
   size_t totalLength = 0;
 
+  // Encode as NDN Packet Format v0.2
   // Interest ::= INTEREST-TYPE TLV-LENGTH
   //                Name
   //                Selectors?
@@ -95,8 +108,8 @@
   // (reverse encoding)
 
   // ForwardingHint
-  if (m_forwardingHint.size() > 0) {
-    totalLength += m_forwardingHint.wireEncode(encoder);
+  if (getForwardingHint().size() > 0) {
+    totalLength += getForwardingHint().wireEncode(encoder);
   }
 
   // InterestLifetime
@@ -107,10 +120,8 @@
   }
 
   // Nonce
-  uint32_t nonce = this->getNonce(); // assigns random Nonce if needed
-  totalLength += encoder.prependByteArray(reinterpret_cast<uint8_t*>(&nonce), sizeof(nonce));
-  totalLength += encoder.prependVarNumber(sizeof(nonce));
-  totalLength += encoder.prependVarNumber(tlv::Nonce);
+  uint32_t nonce = getNonce(); // if nonce was unset, getNonce generates a random nonce
+  totalLength += encoder.prependByteArrayBlock(tlv::Nonce, reinterpret_cast<uint8_t*>(&nonce), sizeof(nonce));
 
   // Selectors
   if (hasSelectors()) {
@@ -125,6 +136,66 @@
   return totalLength;
 }
 
+template<encoding::Tag TAG>
+size_t
+Interest::encode03(EncodingImpl<TAG>& encoder) const
+{
+  size_t totalLength = 0;
+
+  // Encode as NDN Packet Format v0.3
+  // Interest ::= INTEREST-TYPE TLV-LENGTH
+  //                Name
+  //                CanBePrefix?
+  //                MustBeFresh?
+  //                ForwardingHint?
+  //                Nonce?
+  //                InterestLifetime?
+  //                HopLimit?
+  //                Parameters?
+
+  // (reverse encoding)
+
+  // Parameters
+  if (hasParameters()) {
+    totalLength += encoder.prependBlock(getParameters());
+  }
+
+  // HopLimit: not yet supported
+
+  // InterestLifetime
+  if (getInterestLifetime() != DEFAULT_INTEREST_LIFETIME) {
+    totalLength += prependNonNegativeIntegerBlock(encoder,
+                                                  tlv::InterestLifetime,
+                                                  getInterestLifetime().count());
+  }
+
+  // Nonce
+  uint32_t nonce = getNonce(); // if nonce was unset, getNonce generates a random nonce
+  totalLength += encoder.prependByteArrayBlock(tlv::Nonce, reinterpret_cast<uint8_t*>(&nonce), sizeof(nonce));
+
+  // ForwardingHint
+  if (getForwardingHint().size() > 0) {
+    totalLength += getForwardingHint().wireEncode(encoder);
+  }
+
+  // MustBeFresh
+  if (getMustBeFresh()) {
+    totalLength += prependEmptyBlock(encoder, tlv::MustBeFresh);
+  }
+
+  // CanBePrefix
+  if (getCanBePrefix()) {
+    totalLength += prependEmptyBlock(encoder, tlv::CanBePrefix);
+  }
+
+  // Name
+  totalLength += getName().wireEncode(encoder);
+
+  totalLength += encoder.prependVarNumber(totalLength);
+  totalLength += encoder.prependVarNumber(tlv::Interest);
+  return totalLength;
+}
+
 NDN_CXX_DEFINE_WIRE_ENCODE_INSTANTIATIONS(Interest);
 
 const Block&
@@ -166,59 +237,59 @@
 bool
 Interest::decode02()
 {
-  auto ele = m_wire.elements_begin();
+  auto element = m_wire.elements_begin();
 
   // Name
-  if (ele != m_wire.elements_end() && ele->type() == tlv::Name) {
-    m_name.wireDecode(*ele);
-    ++ele;
+  if (element != m_wire.elements_end() && element->type() == tlv::Name) {
+    m_name.wireDecode(*element);
+    ++element;
   }
   else {
     return false;
   }
 
   // Selectors?
-  if (ele != m_wire.elements_end() && ele->type() == tlv::Selectors) {
-    m_selectors.wireDecode(*ele);
-    ++ele;
+  if (element != m_wire.elements_end() && element->type() == tlv::Selectors) {
+    m_selectors.wireDecode(*element);
+    ++element;
   }
   else {
     m_selectors = Selectors();
   }
 
   // Nonce
-  if (ele != m_wire.elements_end() && ele->type() == tlv::Nonce) {
+  if (element != m_wire.elements_end() && element->type() == tlv::Nonce) {
     uint32_t nonce = 0;
-    if (ele->value_size() != sizeof(nonce)) {
+    if (element->value_size() != sizeof(nonce)) {
       BOOST_THROW_EXCEPTION(Error("Nonce element is malformed"));
     }
-    std::memcpy(&nonce, ele->value(), sizeof(nonce));
+    std::memcpy(&nonce, element->value(), sizeof(nonce));
     m_nonce = nonce;
-    ++ele;
+    ++element;
   }
   else {
     return false;
   }
 
   // InterestLifetime?
-  if (ele != m_wire.elements_end() && ele->type() == tlv::InterestLifetime) {
-    m_interestLifetime = time::milliseconds(readNonNegativeInteger(*ele));
-    ++ele;
+  if (element != m_wire.elements_end() && element->type() == tlv::InterestLifetime) {
+    m_interestLifetime = time::milliseconds(readNonNegativeInteger(*element));
+    ++element;
   }
   else {
     m_interestLifetime = DEFAULT_INTEREST_LIFETIME;
   }
 
   // ForwardingHint?
-  if (ele != m_wire.elements_end() && ele->type() == tlv::ForwardingHint) {
-    m_forwardingHint.wireDecode(*ele, false);
-    ++ele;
+  if (element != m_wire.elements_end() && element->type() == tlv::ForwardingHint) {
+    m_forwardingHint.wireDecode(*element, false);
+    ++element;
   }
   else {
     m_forwardingHint = DelegationList();
   }
 
-  return ele == m_wire.elements_end();
+  return element == m_wire.elements_end();
 }
 
 void
@@ -239,96 +310,97 @@
   m_nonce.reset();
   m_interestLifetime = DEFAULT_INTEREST_LIFETIME;
   m_forwardingHint = DelegationList();
+  m_parameters = Block();
 
-  int lastEle = 0; // last recognized element index, in spec order
-  for (const Block& ele : m_wire.elements()) {
-    switch (ele.type()) {
+  int lastElement = 0; // last recognized element index, in spec order
+  for (const Block& element : m_wire.elements()) {
+    switch (element.type()) {
       case tlv::Name: {
-        if (lastEle >= 1) {
+        if (lastElement >= 1) {
           BOOST_THROW_EXCEPTION(Error("Name element is out of order"));
         }
         hasName = true;
-        m_name.wireDecode(ele);
+        m_name.wireDecode(element);
         if (m_name.empty()) {
           BOOST_THROW_EXCEPTION(Error("Name has zero name components"));
         }
-        lastEle = 1;
+        lastElement = 1;
         break;
       }
       case tlv::CanBePrefix: {
-        if (lastEle >= 2) {
+        if (lastElement >= 2) {
           BOOST_THROW_EXCEPTION(Error("CanBePrefix element is out of order"));
         }
-        if (ele.value_size() != 0) {
+        if (element.value_size() != 0) {
           BOOST_THROW_EXCEPTION(Error("CanBePrefix element has non-zero TLV-LENGTH"));
         }
         m_selectors.setMaxSuffixComponents(-1);
-        lastEle = 2;
+        lastElement = 2;
         break;
       }
       case tlv::MustBeFresh: {
-        if (lastEle >= 3) {
+        if (lastElement >= 3) {
           BOOST_THROW_EXCEPTION(Error("MustBeFresh element is out of order"));
         }
-        if (ele.value_size() != 0) {
+        if (element.value_size() != 0) {
           BOOST_THROW_EXCEPTION(Error("MustBeFresh element has non-zero TLV-LENGTH"));
         }
         m_selectors.setMustBeFresh(true);
-        lastEle = 3;
+        lastElement = 3;
         break;
       }
       case tlv::ForwardingHint: {
-        if (lastEle >= 4) {
+        if (lastElement >= 4) {
           BOOST_THROW_EXCEPTION(Error("ForwardingHint element is out of order"));
         }
-        m_forwardingHint.wireDecode(ele);
-        lastEle = 4;
+        m_forwardingHint.wireDecode(element);
+        lastElement = 4;
         break;
       }
       case tlv::Nonce: {
-        if (lastEle >= 5) {
+        if (lastElement >= 5) {
           BOOST_THROW_EXCEPTION(Error("Nonce element is out of order"));
         }
         uint32_t nonce = 0;
-        if (ele.value_size() != sizeof(nonce)) {
+        if (element.value_size() != sizeof(nonce)) {
           BOOST_THROW_EXCEPTION(Error("Nonce element is malformed"));
         }
-        std::memcpy(&nonce, ele.value(), sizeof(nonce));
+        std::memcpy(&nonce, element.value(), sizeof(nonce));
         m_nonce = nonce;
-        lastEle = 5;
+        lastElement = 5;
         break;
       }
       case tlv::InterestLifetime: {
-        if (lastEle >= 6) {
+        if (lastElement >= 6) {
           BOOST_THROW_EXCEPTION(Error("InterestLifetime element is out of order"));
         }
-        m_interestLifetime = time::milliseconds(readNonNegativeInteger(ele));
-        lastEle = 6;
+        m_interestLifetime = time::milliseconds(readNonNegativeInteger(element));
+        lastElement = 6;
         break;
       }
       case tlv::HopLimit: {
-        if (lastEle >= 7) {
+        if (lastElement >= 7) {
           break; // HopLimit is non-critical, ignore out-of-order appearance
         }
-        if (ele.value_size() != 1) {
+        if (element.value_size() != 1) {
           BOOST_THROW_EXCEPTION(Error("HopLimit element is malformed"));
         }
         // TLV-VALUE is ignored
-        lastEle = 7;
+        lastElement = 7;
         break;
       }
       case tlv::Parameters: {
-        if (lastEle >= 8) {
+        if (lastElement >= 8) {
           BOOST_THROW_EXCEPTION(Error("Parameters element is out of order"));
         }
-        // TLV-VALUE is ignored
-        lastEle = 8;
+        m_parameters = element;
+        lastElement = 8;
         break;
       }
       default: {
-        if (tlv::isCriticalType(ele.type())) {
+        if (tlv::isCriticalType(element.type())) {
           BOOST_THROW_EXCEPTION(Error("unrecognized element of critical type " +
-                                      to_string(ele.type())));
+                                      to_string(element.type())));
         }
         break;
       }
@@ -521,6 +593,43 @@
   return *this;
 }
 
+Interest&
+Interest::setParameters(const Block& parameters)
+{
+  if (parameters.type() == tlv::Parameters) {
+    m_parameters = parameters;
+  }
+  else {
+    m_parameters = Block(tlv::Parameters, parameters);
+  }
+  m_wire.reset();
+  return *this;
+}
+
+Interest&
+Interest::setParameters(const uint8_t* buffer, size_t bufferSize)
+{
+  m_parameters = makeBinaryBlock(tlv::Parameters, buffer, bufferSize);
+  m_wire.reset();
+  return *this;
+}
+
+Interest&
+Interest::setParameters(ConstBufferPtr buffer)
+{
+  m_parameters = Block(tlv::Parameters, std::move(buffer));
+  m_wire.reset();
+  return *this;
+}
+
+Interest&
+Interest::unsetParameters()
+{
+  m_parameters = Block();
+  m_wire.reset();
+  return *this;
+}
+
 // ---- operators ----
 
 bool
diff --git a/src/interest.hpp b/src/interest.hpp
index 86c63a3..bde5c97 100644
--- a/src/interest.hpp
+++ b/src/interest.hpp
@@ -64,7 +64,7 @@
   explicit
   Interest(const Block& wire);
 
-  /** @brief Prepend wire encoding to @p encoder in NDN Packet Format v0.2.
+  /** @brief Prepend wire encoding to @p encoder.
    */
   template<encoding::Tag TAG>
   size_t
@@ -72,9 +72,8 @@
 
   /** @brief Encode to a @c Block.
    *
-   *  Normally, this function encodes to NDN Packet Format v0.2. However, if this instance has
-   *  cached wire encoding (@c hasWire() is true), the cached encoding is returned and it might
-   *  be in v0.3 format.
+   *  Encodes into NDN Packet Format v0.3 if Parameters element is present. In this case, Selectors
+   *  are not encoded. Otherwise, encodes into NDN Packet Format v0.2.
    */
   const Block&
   wireEncode() const;
@@ -287,6 +286,51 @@
   Interest&
   setInterestLifetime(time::milliseconds lifetime);
 
+  bool
+  hasParameters() const
+  {
+    return !m_parameters.empty();
+  }
+
+  const Block&
+  getParameters() const
+  {
+    return m_parameters;
+  }
+
+  /** @brief Set parameters from a Block
+   *
+   *  If the block's TLV-TYPE is Parameters, it will be used directly as this Interest's Parameters element.
+   *  If the block's TLV-TYPE is not Parameters, it will be nested into a Parameters element.
+   *  @return a reference to this Interest
+   */
+  Interest&
+  setParameters(const Block& parameters);
+
+  /** @brief Copy parameters from raw buffer
+   *
+   *  @param buffer pointer to the first octet of parameters
+   *  @param bufferSize size of the raw buffer
+   *  @return a reference to this Interest
+   */
+  Interest&
+  setParameters(const uint8_t* buffer, size_t bufferSize);
+
+  /** @brief Set parameters from a wire buffer
+   *
+   *  @param buffer containing the Interest parameters
+   *  @return a reference to this Interest
+   */
+  Interest&
+  setParameters(ConstBufferPtr buffer);
+
+  /** @brief Remove the Parameters element from this Interest
+   *
+   *  @post hasParameters() == false
+   */
+  Interest&
+  unsetParameters();
+
 public: // Selectors (deprecated)
   /** @brief Check if Interest has any selector present.
    */
@@ -394,6 +438,18 @@
   }
 
 private:
+  /** @brief Prepend wire encoding to @p encoder in NDN Packet Format v0.2.
+   */
+  template<encoding::Tag TAG>
+  size_t
+  encode02(EncodingImpl<TAG>& encoder) const;
+
+  /** @brief Prepend wire encoding to @p encoder in NDN Packet Format v0.3.
+   */
+  template<encoding::Tag TAG>
+  size_t
+  encode03(EncodingImpl<TAG>& encoder) const;
+
   /** @brief Decode @c m_wire as NDN Packet Format v0.2.
    *  @retval true decoding successful.
    *  @retval false decoding failed due to structural error.
@@ -419,11 +475,12 @@
   static boost::logic::tribool s_defaultCanBePrefix;
 
   Name m_name;
-  Selectors m_selectors;
+  Selectors m_selectors; // NDN Packet Format v0.2 only
   mutable bool m_isCanBePrefixSet;
   mutable optional<uint32_t> m_nonce;
   time::milliseconds m_interestLifetime;
   DelegationList m_forwardingHint;
+  Block m_parameters; // NDN Packet Format v0.3 only
 
   mutable Block m_wire;
 
diff --git a/tests/unit-tests/interest.t.cpp b/tests/unit-tests/interest.t.cpp
index bf2389f..52ec461 100644
--- a/tests/unit-tests/interest.t.cpp
+++ b/tests/unit-tests/interest.t.cpp
@@ -115,6 +115,87 @@
   BOOST_CHECK_EQUAL(i1, i2);
 }
 
+BOOST_AUTO_TEST_CASE(EncodeDecode03Basic)
+{
+  const uint8_t WIRE[] = {
+    0x05, 0x22, // Interest
+          0x07, 0x14, // Name
+                0x08, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, // GenericNameComponent
+                0x08, 0x03, 0x6e, 0x64, 0x6e, // GenericNameComponent
+                0x08, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, // GenericNameComponent
+          0x0a, 0x04, // Nonce
+                0x01, 0x00, 0x00, 0x00,
+          0x23, 0x04, // Parameters
+                0xc0, 0xc1, 0xc2, 0xc3};
+
+  Interest i1;
+  i1.setName("/local/ndn/prefix");
+  i1.setCanBePrefix(false);
+  i1.setNonce(1);
+  i1.setParameters("2304C0C1C2C3"_block);
+  Block wire1 = i1.wireEncode();
+  BOOST_CHECK_EQUAL_COLLECTIONS(wire1.begin(), wire1.end(), WIRE, WIRE + sizeof(WIRE));
+
+  Interest i2(wire1);
+  BOOST_CHECK_EQUAL(i2.getName(), "/local/ndn/prefix");
+  BOOST_CHECK_EQUAL(i2.getCanBePrefix(), false);
+  BOOST_CHECK_EQUAL(i2.getMustBeFresh(), false);
+  BOOST_CHECK(i2.getForwardingHint().empty());
+  BOOST_CHECK_EQUAL(i2.getNonce(), 1);
+  BOOST_CHECK_EQUAL(i2.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
+  BOOST_CHECK(i2.hasParameters());
+  BOOST_CHECK_EQUAL(i2.getParameters(), "2304C0C1C2C3"_block);
+  BOOST_CHECK(i2.getPublisherPublicKeyLocator().empty());
+}
+
+BOOST_AUTO_TEST_CASE(EncodeDecode03Full)
+{
+  const uint8_t WIRE[] = {
+    0x05, 0x37, // Interest
+          0x07, 0x14, // Name
+                0x08, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, // GenericNameComponent
+                0x08, 0x03, 0x6e, 0x64, 0x6e, // GenericNameComponent
+                0x08, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, // GenericNameComponent
+          0x21, 0x00, // CanBePrefix
+          0x12, 0x00, // MustBeFresh
+          0x1e, 0x0b, // ForwardingHint
+                0x1f, 0x09, // Delegation List
+                      0x1e, 0x02,
+                            0x3e, 0x15,
+                      0x07, 0x03,
+                            0x08, 0x01, 0x48,
+          0x0a, 0x04, // Nonce
+                0x4a, 0xcb, 0x1e, 0x4c,
+          0x0c, 0x02, // Interest Lifetime
+                0x76, 0xa1,
+          0x23, 0x04, // Parameters
+                0xc0, 0xc1, 0xc2, 0xc3};
+  Interest i1;
+  i1.setName("/local/ndn/prefix");
+  i1.setMustBeFresh(true);
+  i1.setCanBePrefix(true);
+  i1.setForwardingHint(DelegationList({{15893, "/H"}}));
+  i1.setNonce(0x4c1ecb4a);
+  i1.setInterestLifetime(30369_ms);
+  i1.setParameters("2304C0C1C2C3"_block);
+  i1.setMinSuffixComponents(1); // v0.2-only elements will not be encoded
+  i1.setExclude(Exclude().excludeAfter(name::Component("J"))); // v0.2-only elements will not be encoded
+  Block wire1 = i1.wireEncode();
+  BOOST_CHECK_EQUAL_COLLECTIONS(wire1.begin(), wire1.end(), WIRE, WIRE + sizeof(WIRE));
+
+  Interest i2(wire1);
+  BOOST_CHECK_EQUAL(i2.getName(), "/local/ndn/prefix");
+  BOOST_CHECK_EQUAL(i2.getCanBePrefix(), true);
+  BOOST_CHECK_EQUAL(i2.getMustBeFresh(), true);
+  BOOST_CHECK_EQUAL(i2.getForwardingHint(), DelegationList({{15893, "/H"}}));
+  BOOST_CHECK(i2.hasNonce());
+  BOOST_CHECK_EQUAL(i2.getNonce(), 0x4c1ecb4a);
+  BOOST_CHECK_EQUAL(i2.getInterestLifetime(), 30369_ms);
+  BOOST_CHECK_EQUAL(i2.getParameters(), "2304C0C1C2C3"_block);
+  BOOST_CHECK_EQUAL(i2.getMinSuffixComponents(), -1); // Default because minSuffixComponents was not encoded
+  BOOST_CHECK(i2.getExclude().empty()); // Exclude was not encoded
+}
+
 class Decode03Fixture
 {
 protected:
@@ -126,6 +207,7 @@
     i.setNonce(0x03d645a8);
     i.setInterestLifetime(18554_ms);
     i.setPublisherPublicKeyLocator(Name("/K"));
+    i.setParameters("2304A0A1A2A3"_block);
   }
 
 protected:
@@ -144,6 +226,7 @@
   BOOST_CHECK(i.hasNonce()); // a random nonce is generated
   BOOST_CHECK_EQUAL(i.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
   BOOST_CHECK(i.getPublisherPublicKeyLocator().empty());
+  BOOST_CHECK(!i.hasParameters());
 
   BOOST_CHECK(!i.hasWire()); // nonce generation resets wire encoding
 
@@ -154,9 +237,9 @@
 
 BOOST_AUTO_TEST_CASE(Full)
 {
-  i.wireDecode("053B FC00 0703080149 FC00 2100 FC00 1200 "
+  i.wireDecode("0533 FC00 0703080149 FC00 2100 FC00 1200 "
                "FC00 1E0B(1F09 1E023E15 0703080148) FC00 0A044ACB1E4C "
-               "FC00 0C0276A1 FC00 2201D6 FC00 2304C0C1C2C3 FC00"_block);
+               "FC00 0C0276A1 FC00 2201D6 FC00"_block);
   BOOST_CHECK_EQUAL(i.getName(), "/I");
   BOOST_CHECK_EQUAL(i.getCanBePrefix(), true);
   BOOST_CHECK_EQUAL(i.getMustBeFresh(), true);
@@ -165,10 +248,9 @@
   BOOST_CHECK_EQUAL(i.getNonce(), 0x4c1ecb4a);
   BOOST_CHECK_EQUAL(i.getInterestLifetime(), 30369_ms);
   // HopLimit=214 is not stored
-  // Parameters="C0C1C2C3" is not stored
 
   // encode without modification: retain original wire encoding
-  BOOST_CHECK_EQUAL(i.wireEncode().value_size(), 59);
+  BOOST_CHECK_EQUAL(i.wireEncode().value_size(), 51);
 
   // modify then re-encode as v0.2 format
   i.setName("/J");
@@ -214,7 +296,7 @@
   i.wireDecode("0514 0703080149 2201D6 2200 2304C0C1C2C3 22020101"_block);
   BOOST_CHECK_EQUAL(i.getName(), "/I");
   // HopLimit=214 is not stored
-  // Parameters="C0C1C2C3" is not stored
+  BOOST_CHECK_EQUAL(i.getParameters(), "2304C0C1C2C3"_block);
 }
 
 BOOST_AUTO_TEST_CASE(NameMissing)
@@ -477,6 +559,28 @@
   BOOST_CHECK_EQUAL(i.getInterestLifetime(), 1_ms);
 }
 
+BOOST_AUTO_TEST_CASE(SetParameters)
+{
+  const uint8_t PARAMETERS1[] = {0xc1};
+  const uint8_t PARAMETERS2[] = {0xc2};
+
+  Interest i;
+  BOOST_CHECK(!i.hasParameters());
+  i.setParameters("2300"_block);
+  BOOST_CHECK(i.hasParameters());
+  i.unsetParameters();
+  BOOST_CHECK(!i.hasParameters());
+
+  i.setParameters("2301C0"_block); // Block overload
+  BOOST_CHECK_EQUAL(i.getParameters(), "2301C0"_block);
+  i.setParameters(PARAMETERS1, sizeof(PARAMETERS1)); // raw buffer overload
+  BOOST_CHECK_EQUAL(i.getParameters(), "2301C1"_block);
+  i.setParameters(make_shared<Buffer>(PARAMETERS2, sizeof(PARAMETERS2))); // ConstBufferPtr overload
+  BOOST_CHECK_EQUAL(i.getParameters(), "2301C2"_block);
+  i.setParameters("8001C1"_block); // Block of non-Parameters type
+  BOOST_CHECK_EQUAL(i.getParameters(), "23038001C1"_block);
+}
+
 // ---- operators ----
 
 BOOST_AUTO_TEST_CASE(Equality)
@@ -539,6 +643,15 @@
   b.setForwardingHint({{1, "/H"}});
   BOOST_CHECK_EQUAL(a == b, true);
   BOOST_CHECK_EQUAL(a != b, false);
+
+  // compare Parameters
+  a.setParameters("2304C0C1C2C3"_block);
+  BOOST_CHECK_EQUAL(a == b, false);
+  BOOST_CHECK_EQUAL(a != b, true);
+
+  b.setParameters("2304C0C1C2C3"_block);
+  BOOST_CHECK_EQUAL(a == b, true);
+  BOOST_CHECK_EQUAL(a != b, false);
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestInterest