data: avoid unnecessarily invalidating cached wire encoding

Also, improve unit testing of Interest and Data classes and
cleanup some doxygen comments.

Change-Id: Ia7ef0af88fc84976ae72f9b99a281300717ced3e
diff --git a/ndn-cxx/data.cpp b/ndn-cxx/data.cpp
index a4331a8..598002f 100644
--- a/ndn-cxx/data.cpp
+++ b/ndn-cxx/data.cpp
@@ -47,12 +47,13 @@
 size_t
 Data::wireEncode(EncodingImpl<TAG>& encoder, bool wantUnsignedPortionOnly) const
 {
-  // Data ::= DATA-TLV TLV-LENGTH
-  //            Name
-  //            MetaInfo?
-  //            Content?
-  //            SignatureInfo
-  //            SignatureValue
+  // Data = DATA-TYPE TLV-LENGTH
+  //          Name
+  //          [MetaInfo]
+  //          [Content]
+  //          SignatureInfo
+  //          SignatureValue
+  // (elements are encoded in reverse order)
 
   size_t totalLength = 0;
 
@@ -121,29 +122,32 @@
 void
 Data::wireDecode(const Block& wire)
 {
-  // Data ::= DATA-TLV TLV-LENGTH
-  //            Name
-  //            MetaInfo?
-  //            Content?
-  //            SignatureInfo
-  //            SignatureValue
-
+  if (wire.type() != tlv::Data) {
+    NDN_THROW(Error("Data", wire.type()));
+  }
   m_wire = wire;
   m_wire.parse();
 
+  // Data = DATA-TYPE TLV-LENGTH
+  //          Name
+  //          [MetaInfo]
+  //          [Content]
+  //          SignatureInfo
+  //          SignatureValue
+
   auto element = m_wire.elements_begin();
   if (element == m_wire.elements_end() || element->type() != tlv::Name) {
     NDN_THROW(Error("Name element is missing or out of order"));
   }
   m_name.wireDecode(*element);
-  int lastElement = 1; // last recognized element index, in spec order
 
-  m_metaInfo = MetaInfo();
+  m_metaInfo = {};
   m_content = Block(tlv::Content);
-  m_signatureInfo = SignatureInfo();
-  m_signatureValue = Block();
+  m_signatureInfo = {};
+  m_signatureValue = {};
   m_fullName.clear();
 
+  int lastElement = 1; // last recognized element index, in spec order
   for (++element; element != m_wire.elements_end(); ++element) {
     switch (element->type()) {
       case tlv::MetaInfo: {
@@ -174,17 +178,16 @@
         if (lastElement >= 5) {
           NDN_THROW(Error("SignatureValue element is out of order"));
         }
-        if (element->type() != tlv::SignatureValue) {
-          NDN_THROW(Error("SignatureValue", element->type()));
-        }
         m_signatureValue = *element;
         lastElement = 5;
         break;
       }
-      default: {
+      default: { // unrecognized element
+        // if the TLV-TYPE is critical, abort decoding
         if (tlv::isCriticalType(element->type())) {
-          NDN_THROW(Error("unrecognized element of critical type " + to_string(element->type())));
+          NDN_THROW(Error("Unrecognized element of critical type " + to_string(element->type())));
         }
+        // otherwise, ignore it
         break;
       }
     }
@@ -193,7 +196,6 @@
   if (!m_signatureInfo) {
     NDN_THROW(Error("SignatureInfo element is missing"));
   }
-
   if (!m_signatureValue.isValid()) {
     NDN_THROW(Error("SignatureValue element is missing"));
   }
@@ -223,16 +225,18 @@
 Data&
 Data::setName(const Name& name)
 {
-  resetWire();
-  m_name = name;
+  if (name != m_name) {
+    m_name = name;
+    resetWire();
+  }
   return *this;
 }
 
 Data&
 Data::setMetaInfo(const MetaInfo& metaInfo)
 {
-  resetWire();
   m_metaInfo = metaInfo;
+  resetWire();
   return *this;
 }
 
@@ -248,23 +252,21 @@
 Data&
 Data::setContent(const Block& block)
 {
-  resetWire();
-
   if (block.type() == tlv::Content) {
     m_content = block;
   }
   else {
     m_content = Block(tlv::Content, block);
   }
-
+  resetWire();
   return *this;
 }
 
 Data&
 Data::setContent(const uint8_t* value, size_t valueSize)
 {
-  resetWire();
   m_content = makeBinaryBlock(tlv::Content, value, valueSize);
+  resetWire();
   return *this;
 }
 
@@ -274,8 +276,8 @@
   if (value == nullptr) {
     NDN_THROW(std::invalid_argument("Content buffer cannot be nullptr"));
   }
-  resetWire();
   m_content = Block(tlv::Content, std::move(value));
+  resetWire();
   return *this;
 }
 
@@ -288,17 +290,17 @@
 Data&
 Data::setSignature(const Signature& signature)
 {
-  resetWire();
   m_signatureInfo = signature.getSignatureInfo();
   m_signatureValue = signature.getValue();
+  resetWire();
   return *this;
 }
 
 Data&
 Data::setSignatureInfo(const SignatureInfo& info)
 {
-  resetWire();
   m_signatureInfo = info;
+  resetWire();
   return *this;
 }
 
@@ -308,32 +310,38 @@
   if (value == nullptr) {
     NDN_THROW(std::invalid_argument("SignatureValue buffer cannot be nullptr"));
   }
-  resetWire();
   m_signatureValue = Block(tlv::SignatureValue, std::move(value));
+  resetWire();
   return *this;
 }
 
 Data&
 Data::setContentType(uint32_t type)
 {
-  resetWire();
-  m_metaInfo.setType(type);
+  if (type != m_metaInfo.getType()) {
+    m_metaInfo.setType(type);
+    resetWire();
+  }
   return *this;
 }
 
 Data&
 Data::setFreshnessPeriod(time::milliseconds freshnessPeriod)
 {
-  resetWire();
-  m_metaInfo.setFreshnessPeriod(freshnessPeriod);
+  if (freshnessPeriod != m_metaInfo.getFreshnessPeriod()) {
+    m_metaInfo.setFreshnessPeriod(freshnessPeriod);
+    resetWire();
+  }
   return *this;
 }
 
 Data&
 Data::setFinalBlock(optional<name::Component> finalBlockId)
 {
-  resetWire();
-  m_metaInfo.setFinalBlock(std::move(finalBlockId));
+  if (finalBlockId != m_metaInfo.getFinalBlock()) {
+    m_metaInfo.setFinalBlock(std::move(finalBlockId));
+    resetWire();
+  }
   return *this;
 }
 
diff --git a/ndn-cxx/data.hpp b/ndn-cxx/data.hpp
index 88c37b1..26a64b3 100644
--- a/ndn-cxx/data.hpp
+++ b/ndn-cxx/data.hpp
@@ -19,8 +19,8 @@
  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
  */
 
-#ifndef NDN_DATA_HPP
-#define NDN_DATA_HPP
+#ifndef NDN_CXX_DATA_HPP
+#define NDN_CXX_DATA_HPP
 
 #include "ndn-cxx/detail/packet-base.hpp"
 #include "ndn-cxx/encoding/block.hpp"
@@ -30,7 +30,8 @@
 
 namespace ndn {
 
-/** @brief Represents a Data packet.
+/** @brief Represents a %Data packet.
+ *  @sa https://named-data.net/doc/NDN-packet-spec/0.3/data.html
  */
 class Data : public PacketBase, public std::enable_shared_from_this<Data>
 {
@@ -42,27 +43,29 @@
   };
 
   /** @brief Construct an unsigned Data packet with given @p name and empty Content.
-   *  @warning In certain contexts that use `Data::shared_from_this()`, Data must be created
-   *           using `make_shared`. Otherwise, `shared_from_this()` will trigger undefined behavior.
+   *  @warning In certain contexts that use `Data::shared_from_this()`, Data must be created using
+   *           `std::make_shared`. Otherwise, `shared_from_this()` may trigger undefined behavior.
+   *           One example where this is necessary is storing Data into a subclass of InMemoryStorage.
    */
   explicit
   Data(const Name& name = Name());
 
   /** @brief Construct a Data packet by decoding from @p wire.
-   *  @param wire @c tlv::Data element as defined in NDN Packet Format v0.2 or v0.3.
-   *              It may be signed or unsigned.
-   *  @warning In certain contexts that use `Data::shared_from_this()`, Data must be created
-   *           using `make_shared`. Otherwise, `shared_from_this()` will trigger undefined behavior.
+   *  @param wire TLV block of type tlv::Data; may be signed or unsigned.
+   *  @warning In certain contexts that use `Data::shared_from_this()`, Data must be created using
+   *           `std::make_shared`. Otherwise, `shared_from_this()` may trigger undefined behavior.
+   *           One example where this is necessary is storing Data into a subclass of InMemoryStorage.
    */
   explicit
   Data(const Block& wire);
 
-  /** @brief Prepend wire encoding to @p encoder in NDN Packet Format v0.2.
-   *  @param encoder EncodingEstimator or EncodingBuffer instance
-   *  @param wantUnsignedPortionOnly If true, only prepends Name, MetaInfo, Content, and
-   *         SignatureInfo to @p encoder, but omit SignatureValue and outmost Type-Length of Data
-   *         element. This is intended to be used with wireEncode(encoder, signatureValue).
-   *  @throw Error SignatureBits are not provided and wantUnsignedPortionOnly is false.
+  /** @brief Prepend wire encoding to @p encoder
+   *  @param encoder EncodingEstimator or EncodingBuffer instance.
+   *  @param wantUnsignedPortionOnly If true, prepend only Name, MetaInfo, Content, and
+   *         SignatureInfo to @p encoder, but omit SignatureValue and the outermost TLV
+   *         Type and Length of the Data element. This is intended to be used with
+   *         wireEncode(EncodingBuffer&, const Block&) const.
+   *  @throw Error %Signature is not present and @p wantUnsignedPortionOnly is false.
    */
   template<encoding::Tag TAG>
   size_t
@@ -70,10 +73,10 @@
 
   /** @brief Finalize Data packet encoding with the specified SignatureValue
    *  @param encoder EncodingBuffer containing Name, MetaInfo, Content, and SignatureInfo, but
-   *                 without SignatureValue or outmost Type-Length of Data element
-   *  @param signatureValue SignatureValue element
+   *                 without SignatureValue and the outermost Type-Length of the Data element.
+   *  @param signatureValue SignatureValue element.
    *
-   *  This method is intended to be used in concert with Data::wireEncode(encoder, true)
+   *  This method is intended to be used in concert with `wireEncode(encoder, true)`, e.g.:
    *  @code
    *     Data data;
    *     ...
@@ -87,17 +90,13 @@
   const Block&
   wireEncode(EncodingBuffer& encoder, const Block& signatureValue) const;
 
-  /** @brief Encode to a @c Block.
-   *  @pre Data is signed.
-   *
-   *  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.
+  /** @brief Encode into a Block.
+   *  @pre Data must be signed.
    */
   const Block&
   wireEncode() const;
 
-  /** @brief Decode from @p wire in NDN Packet Format v0.2 or v0.3.
+  /** @brief Decode from @p wire.
    */
   void
   wireDecode(const Block& wire);
@@ -320,4 +319,4 @@
 
 } // namespace ndn
 
-#endif // NDN_DATA_HPP
+#endif // NDN_CXX_DATA_HPP
diff --git a/ndn-cxx/delegation.hpp b/ndn-cxx/delegation.hpp
index 84c8e7b..c9ac24d 100644
--- a/ndn-cxx/delegation.hpp
+++ b/ndn-cxx/delegation.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2019 Regents of the University of California.
+ * Copyright (c) 2013-2020 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -26,8 +26,8 @@
 
 namespace ndn {
 
-/** \brief Represents a Delegation.
- *  \sa https://named-data.net/doc/NDN-packet-spec/current/link.html
+/** \brief Represents a %Delegation.
+ *  \sa https://named-data.net/doc/NDN-packet-spec/0.3/link.html
  */
 class Delegation
 {
diff --git a/ndn-cxx/encoding/block.hpp b/ndn-cxx/encoding/block.hpp
index b91e90a..41a1b30 100644
--- a/ndn-cxx/encoding/block.hpp
+++ b/ndn-cxx/encoding/block.hpp
@@ -21,8 +21,8 @@
  * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
  */
 
-#ifndef NDN_ENCODING_BLOCK_HPP
-#define NDN_ENCODING_BLOCK_HPP
+#ifndef NDN_CXX_ENCODING_BLOCK_HPP
+#define NDN_CXX_ENCODING_BLOCK_HPP
 
 #include "ndn-cxx/encoding/buffer.hpp"
 #include "ndn-cxx/encoding/encoding-buffer-fwd.hpp"
@@ -36,8 +36,8 @@
 
 namespace ndn {
 
-/** @brief Represents a TLV element of NDN packet format
- *  @sa https://named-data.net/doc/NDN-packet-spec/current/
+/** @brief Represents a TLV element of the NDN packet format.
+ *  @sa https://named-data.net/doc/NDN-packet-spec/0.3/tlv.html#tlv-encoding
  */
 class Block
 {
@@ -505,4 +505,4 @@
 
 } // namespace ndn
 
-#endif // NDN_ENCODING_BLOCK_HPP
+#endif // NDN_CXX_ENCODING_BLOCK_HPP
diff --git a/ndn-cxx/interest.cpp b/ndn-cxx/interest.cpp
index 8f69fcb..6969de5 100644
--- a/ndn-cxx/interest.cpp
+++ b/ndn-cxx/interest.cpp
@@ -97,7 +97,6 @@
 #endif // NDN_CXX_HAVE_TESTS
   }
 
-  // Encode as NDN Packet Format v0.3
   // Interest = INTEREST-TYPE TLV-LENGTH
   //              Name
   //              [CanBePrefix]
diff --git a/ndn-cxx/interest.hpp b/ndn-cxx/interest.hpp
index 701c1de..a49bf78 100644
--- a/ndn-cxx/interest.hpp
+++ b/ndn-cxx/interest.hpp
@@ -19,8 +19,8 @@
  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
  */
 
-#ifndef NDN_INTEREST_HPP
-#define NDN_INTEREST_HPP
+#ifndef NDN_CXX_INTEREST_HPP
+#define NDN_CXX_INTEREST_HPP
 
 #include "ndn-cxx/delegation-list.hpp"
 #include "ndn-cxx/detail/packet-base.hpp"
@@ -43,7 +43,8 @@
  */
 const time::milliseconds DEFAULT_INTEREST_LIFETIME = 4_s;
 
-/** @brief Represents an Interest packet.
+/** @brief Represents an %Interest packet.
+ *  @sa https://named-data.net/doc/NDN-packet-spec/0.3/interest.html
  */
 class Interest : public PacketBase, public std::enable_shared_from_this<Interest>
 {
@@ -117,18 +118,18 @@
   explicit
   Interest(const Block& wire);
 
-  /** @brief Prepend wire encoding to @p encoder according to NDN Packet Format v0.3.
+  /** @brief Prepend wire encoding to @p encoder.
    */
   template<encoding::Tag TAG>
   size_t
   wireEncode(EncodingImpl<TAG>& encoder) const;
 
-  /** @brief Encode into a Block according to NDN Packet Format v0.3.
+  /** @brief Encode into a Block.
    */
   const Block&
   wireEncode() const;
 
-  /** @brief Decode from @p wire according to NDN Packet Format v0.3.
+  /** @brief Decode from @p wire.
    */
   void
   wireDecode(const Block& wire);
@@ -136,7 +137,7 @@
   /** @brief Check if this instance has cached wire encoding.
    */
   bool
-  hasWire() const
+  hasWire() const noexcept
   {
     return m_wire.hasWire();
   }
@@ -512,4 +513,4 @@
 
 } // namespace ndn
 
-#endif // NDN_INTEREST_HPP
+#endif // NDN_CXX_INTEREST_HPP
diff --git a/ndn-cxx/name.hpp b/ndn-cxx/name.hpp
index 5b9268a..fde6ebe 100644
--- a/ndn-cxx/name.hpp
+++ b/ndn-cxx/name.hpp
@@ -23,8 +23,8 @@
  * @author Zhenkai Zhu <http://irl.cs.ucla.edu/~zhenkai/>
  */
 
-#ifndef NDN_NAME_HPP
-#define NDN_NAME_HPP
+#ifndef NDN_CXX_NAME_HPP
+#define NDN_CXX_NAME_HPP
 
 #include "ndn-cxx/name-component.hpp"
 
@@ -39,6 +39,7 @@
 using PartialName = Name;
 
 /** @brief Represents an absolute name
+ *  @sa https://named-data.net/doc/NDN-packet-spec/0.3/name.html
  */
 class Name
 {
@@ -82,34 +83,34 @@
 
   /** @brief Parse name from NDN URI
    *  @param uri a null-terminated URI string
-   *  @sa https://named-data.net/doc/NDN-packet-spec/current/name.html#ndn-uri-scheme
+   *  @sa https://named-data.net/doc/NDN-packet-spec/0.3/name.html#ndn-uri-scheme
    */
   Name(const char* uri);
 
   /** @brief Create name from NDN URI
    *  @param uri a URI string
-   *  @sa https://named-data.net/doc/NDN-packet-spec/current/name.html#ndn-uri-scheme
+   *  @sa https://named-data.net/doc/NDN-packet-spec/0.3/name.html#ndn-uri-scheme
    */
   Name(std::string uri);
 
   /** @brief Write URI representation of the name to the output stream
-   *  @sa https://named-data.net/doc/NDN-packet-spec/current/name.html#ndn-uri-scheme
+   *  @sa https://named-data.net/doc/NDN-packet-spec/0.3/name.html#ndn-uri-scheme
    */
   void
   toUri(std::ostream& os, name::UriFormat format = name::UriFormat::DEFAULT) const;
 
   /** @brief Get URI representation of the name
-   *  @return URI representation; "ndn:" scheme identifier is not included
-   *  @sa https://named-data.net/doc/NDN-packet-spec/current/name.html#ndn-uri-scheme
-   *  @note To print URI representation into a stream, it is more efficient to use ``os << name``.
+   *  @return URI representation; the "ndn:" scheme identifier is not included
+   *  @note To print URI representation into a stream, it is more efficient to use `os << name`.
+   *  @sa https://named-data.net/doc/NDN-packet-spec/0.3/name.html#ndn-uri-scheme
    */
   std::string
   toUri(name::UriFormat format = name::UriFormat::DEFAULT) const;
 
-  /** @brief Check if this Name instance already has wire encoding
+  /** @brief Check if this instance already has wire encoding
    */
   bool
-  hasWire() const
+  hasWire() const noexcept
   {
     return m_wire.hasWire();
   }
@@ -374,9 +375,8 @@
   append(const PartialName& name);
 
   /** @brief Append a component with a nonNegativeInteger
-   *  @sa number the number
    *  @return a reference to this name, to allow chaining
-   *  @sa https://named-data.net/doc/NDN-packet-spec/current/tlv.html#non-negative-integer-encoding
+   *  @sa https://named-data.net/doc/NDN-packet-spec/0.3/tlv.html#non-negative-integer-encoding
    */
   Name&
   appendNumber(uint64_t number)
@@ -578,7 +578,7 @@
    *  @retval zero this equals other
    *  @retval positive this comes after other in canonical ordering
    *
-   *  @sa https://named-data.net/doc/NDN-packet-spec/current/name.html#canonical-order
+   *  @sa https://named-data.net/doc/NDN-packet-spec/0.3/name.html#canonical-order
    */
   int
   compare(const Name& other) const
@@ -635,8 +635,8 @@
     return lhs.compare(rhs) >= 0;
   }
 
-  /** @brief Print URI representation of a name
-   *  @sa https://named-data.net/doc/NDN-packet-spec/current/name.html#ndn-uri-scheme
+  /** @brief Print the URI representation of a name.
+   *  @sa https://named-data.net/doc/NDN-packet-spec/0.3/name.html#ndn-uri-scheme
    */
   friend std::ostream&
   operator<<(std::ostream& os, const Name& name)
@@ -656,8 +656,8 @@
 
 NDN_CXX_DECLARE_WIRE_ENCODE_INSTANTIATIONS(Name);
 
-/** @brief Parse URI from stream as Name
- *  @sa https://named-data.net/doc/NDN-packet-spec/current/name.html#ndn-uri-scheme
+/** @brief Parse URI from stream as Name.
+ *  @sa https://named-data.net/doc/NDN-packet-spec/0.3/name.html#ndn-uri-scheme
  */
 std::istream&
 operator>>(std::istream& is, Name& name);
@@ -675,4 +675,4 @@
 
 } // namespace std
 
-#endif // NDN_NAME_HPP
+#endif // NDN_CXX_NAME_HPP
diff --git a/tests/unit/data.t.cpp b/tests/unit/data.t.cpp
index f741a82..e6c7b4b 100644
--- a/tests/unit/data.t.cpp
+++ b/tests/unit/data.t.cpp
@@ -158,9 +158,27 @@
   0xe4, 0x82, 0x43, 0x20, 0x46, 0x7d, 0x0a, 0xb6
 };
 
-BOOST_FIXTURE_TEST_CASE(Encode, DataSigningKeyFixture)
+BOOST_AUTO_TEST_SUITE(Encode)
+
+BOOST_AUTO_TEST_CASE(NotSigned)
 {
-  Data d(Name("/local/ndn/prefix"));
+  Data d;
+  BOOST_CHECK_EXCEPTION(d.wireEncode(), tlv::Error, [] (const auto& e) {
+    return e.what() == "Requested wire format, but Data has not been signed"s;
+  });
+}
+
+BOOST_AUTO_TEST_CASE(Minimal)
+{
+  Data d;
+  d.setSignatureInfo(SignatureInfo(tlv::DigestSha256));
+  d.setSignatureValue(std::make_shared<Buffer>());
+  BOOST_CHECK_EQUAL(d.wireEncode(), "060D 0700 1400 1500 16031B0100 1700"_block);
+}
+
+BOOST_FIXTURE_TEST_CASE(Full, DataSigningKeyFixture)
+{
+  Data d("/local/ndn/prefix");
   d.setContentType(tlv::ContentType_Blob);
   d.setFreshnessPeriod(10_s);
   d.setContent(CONTENT1, sizeof(CONTENT1));
@@ -190,34 +208,18 @@
                                 dataBlock.begin(), dataBlock.end());
 }
 
-BOOST_FIXTURE_TEST_CASE(Decode02, DataSigningKeyFixture)
-{
-  Block dataBlock(DATA1, sizeof(DATA1));
-  Data d(dataBlock);
+BOOST_AUTO_TEST_SUITE_END() // Encode
 
-  BOOST_CHECK_EQUAL(d.getName().toUri(), "/local/ndn/prefix");
-  BOOST_CHECK_EQUAL(d.getContentType(), tlv::ContentType_Blob);
-  BOOST_CHECK_EQUAL(d.getFreshnessPeriod(), 10_s);
-  BOOST_CHECK_EQUAL(std::string(reinterpret_cast<const char*>(d.getContent().value()),
-                                d.getContent().value_size()), "SUCCESS!");
-  BOOST_CHECK_EQUAL(d.getSignatureType(), tlv::SignatureSha256WithRsa);
-
-  Block block = d.getSignatureInfo().wireEncode();
-  block.parse();
-  KeyLocator keyLocator(block.get(tlv::KeyLocator));
-  BOOST_CHECK_EQUAL(keyLocator.getName().toUri(), "/test/key/locator");
-
-  BOOST_CHECK(security::verifySignature(d, m_pubKey));
-}
-
-class Decode03Fixture
+class DecodeFixture
 {
 protected:
-  Decode03Fixture()
+  DecodeFixture()
   {
     // initialize all elements to non-empty, to verify wireDecode clears them
     d.setName("/A");
     d.setContentType(tlv::ContentType_Key);
+    d.setFreshnessPeriod(123_s);
+    d.setFinalBlock(name::Component::fromNumber(42));
     d.setContent("1504C0C1C2C3"_block);
     d.setSignatureInfo(SignatureInfo("160A 1B0101 1C050703080142"_block));
     d.setSignatureValue(fromHex("B48F1707A3BCA3CFC5F32DE51D9B46C32D7D262A21544EBDA88C3B415D637503"
@@ -230,11 +232,18 @@
   Data d;
 };
 
-BOOST_FIXTURE_TEST_SUITE(Decode03, Decode03Fixture)
+BOOST_FIXTURE_TEST_SUITE(Decode, DecodeFixture)
+
+BOOST_AUTO_TEST_CASE(NotData)
+{
+  BOOST_CHECK_EXCEPTION(d.wireDecode("4202CAFE"_block), tlv::Error, [] (const auto& e) {
+    return e.what() == "Expecting Data element, but TLV has type 66"s;
+  });
+}
 
 BOOST_AUTO_TEST_CASE(Minimal)
 {
-  d.wireDecode("062C 0703080144 16031B0100 "
+  d.wireDecode("062C 0703(080144) 1603(1B0100) "
                "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block);
   BOOST_CHECK_EQUAL(d.getName(), "/D");
   BOOST_CHECK_EQUAL(d.getContentType(), tlv::ContentType_Blob);
@@ -246,18 +255,20 @@
   BOOST_CHECK_EQUAL(d.getSignatureValue().value_size(), 32);
 
   // encode without modification: retain original wire encoding
+  BOOST_CHECK_EQUAL(d.hasWire(), true);
   BOOST_CHECK_EQUAL(d.wireEncode().value_size(), 44);
 
-  // modify then re-encode as v0.2 format
+  // modify then re-encode
   d.setName("/E");
+  BOOST_CHECK_EQUAL(d.hasWire(), false);
   BOOST_CHECK_EQUAL(d.wireEncode(),
-    "0630 0703080145 1400 1500 16031B0100 "
-    "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block);
+                    "0630 0703080145 1400 1500 16031B0100 "
+                    "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block);
 }
 
 BOOST_AUTO_TEST_CASE(MinimalEmptyName)
 {
-  d.wireDecode("0609 0700 16031B0100 1700"_block);
+  d.wireDecode("0609 0700 1603(1B0100) 1700"_block);
   BOOST_CHECK_EQUAL(d.getName(), "/"); // empty Name is allowed in Data
   BOOST_CHECK_EQUAL(d.getContentType(), tlv::ContentType_Blob);
   BOOST_CHECK_EQUAL(d.getFreshnessPeriod(), 0_ms);
@@ -270,7 +281,22 @@
 
 BOOST_AUTO_TEST_CASE(Full)
 {
-  d.wireDecode("063A 0703080144 FC00 1400 FC00 1500 FC00 16031B0100 FC00 "
+  d.wireDecode(Block(DATA1, sizeof(DATA1)));
+  BOOST_CHECK_EQUAL(d.getName(), "/local/ndn/prefix");
+  BOOST_CHECK_EQUAL(d.getContentType(), tlv::ContentType_Blob);
+  BOOST_CHECK_EQUAL(d.getFreshnessPeriod(), 10_s);
+  BOOST_CHECK_EQUAL(d.getFinalBlock().has_value(), false);
+  BOOST_CHECK_EQUAL(std::string(reinterpret_cast<const char*>(d.getContent().value()),
+                                d.getContent().value_size()), "SUCCESS!");
+  BOOST_CHECK_EQUAL(d.getSignatureType(), tlv::SignatureSha256WithRsa);
+  BOOST_REQUIRE(d.getKeyLocator().has_value());
+  BOOST_CHECK_EQUAL(d.getKeyLocator()->getName(), "/test/key/locator");
+  BOOST_CHECK_EQUAL(d.getSignatureValue().value_size(), 128);
+}
+
+BOOST_AUTO_TEST_CASE(UnrecognizedNonCriticalElements)
+{
+  d.wireDecode("063A 0703(080144) FC00 1400 FC00 1500 FC00 1603(1B0100) FC00 "
                "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76 FC00"_block);
   BOOST_CHECK_EQUAL(d.getName(), "/D");
   BOOST_CHECK_EQUAL(d.getContentType(), tlv::ContentType_Blob);
@@ -282,72 +308,84 @@
   BOOST_CHECK_EQUAL(d.getSignatureValue().value_size(), 32);
 
   // encode without modification: retain original wire encoding
+  BOOST_CHECK_EQUAL(d.hasWire(), true);
   BOOST_CHECK_EQUAL(d.wireEncode().value_size(), 58);
 
-  // modify then re-encode as v0.2 format
+  // modify then re-encode
   d.setName("/E");
+  BOOST_CHECK_EQUAL(d.hasWire(), false);
   BOOST_CHECK_EQUAL(d.wireEncode(),
-    "0630 0703080145 1400 1500 16031B0100 "
-    "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block);
+                    "0630 0703080145 1400 1500 16031B0100 "
+                    "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block);
 }
 
 BOOST_AUTO_TEST_CASE(CriticalElementOutOfOrder)
 {
-  BOOST_CHECK_THROW(d.wireDecode(
+  BOOST_CHECK_EXCEPTION(d.wireDecode(
     "0630 1400 0703080145 1500 16031B0100 "
     "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
-    tlv::Error);
-  BOOST_CHECK_THROW(d.wireDecode(
+    tlv::Error,
+    [] (const auto& e) { return e.what() == "Name element is missing or out of order"s; });
+  BOOST_CHECK_EXCEPTION(d.wireDecode(
     "0630 0703080145 1500 1400 16031B0100 "
     "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
-    tlv::Error);
-  BOOST_CHECK_THROW(d.wireDecode(
+    tlv::Error,
+    [] (const auto& e) { return e.what() == "MetaInfo element is out of order"s; });
+  BOOST_CHECK_EXCEPTION(d.wireDecode(
     "0630 0703080145 1400 16031B0100 1500 "
     "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
-    tlv::Error);
-  BOOST_CHECK_THROW(d.wireDecode(
+    tlv::Error,
+    [] (const auto& e) { return e.what() == "Content element is out of order"s; });
+  BOOST_CHECK_EXCEPTION(d.wireDecode(
     "0630 0703080145 1400 1500 "
     "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76 16031B0100"_block),
-    tlv::Error);
-  BOOST_CHECK_THROW(d.wireDecode(
+    tlv::Error,
+    [] (const auto& e) { return e.what() == "SignatureInfo element is out of order"s; });
+  BOOST_CHECK_EXCEPTION(d.wireDecode(
     "0652 0703080145 1400 1500 16031B0100 "
     "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"
     "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
-    tlv::Error);
+    tlv::Error,
+    [] (const auto& e) { return e.what() == "SignatureValue element is out of order"s; });
 }
 
-BOOST_AUTO_TEST_CASE(NameMissing)
+BOOST_AUTO_TEST_CASE(MissingName)
 {
-  BOOST_CHECK_THROW(d.wireDecode("0605 16031B0100 1700"_block), tlv::Error);
+  BOOST_CHECK_EXCEPTION(d.wireDecode("0607 16031B0100 1700"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "Name element is missing or out of order"s; });
 }
 
-BOOST_AUTO_TEST_CASE(SigInfoMissing)
+BOOST_AUTO_TEST_CASE(MissingSignatureInfo)
 {
-  BOOST_CHECK_THROW(d.wireDecode("0605 0703080144 1700"_block), tlv::Error);
+  BOOST_CHECK_EXCEPTION(d.wireDecode("0607 0703080144 1700"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "SignatureInfo element is missing"s; });
 }
 
-BOOST_AUTO_TEST_CASE(SigValueMissing)
+BOOST_AUTO_TEST_CASE(MissingSignatureValue)
 {
-  BOOST_CHECK_THROW(d.wireDecode("0607 0700 16031B0100"_block), tlv::Error);
+  BOOST_CHECK_EXCEPTION(d.wireDecode("0607 0700 16031B0100"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "SignatureValue element is missing"s; });
 }
 
 BOOST_AUTO_TEST_CASE(UnrecognizedNonCriticalElementBeforeName)
 {
-  BOOST_CHECK_THROW(d.wireDecode(
-    "062F FC00 0703080144 16031B0100 "
-    "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
-    tlv::Error);
+  BOOST_CHECK_EXCEPTION(d.wireDecode(
+                          "062E FC00 0703080144 16031B0100 "
+                          "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
+                        tlv::Error,
+                        [] (const auto& e) { return e.what() == "Name element is missing or out of order"s; });
 }
 
 BOOST_AUTO_TEST_CASE(UnrecognizedCriticalElement)
 {
-  BOOST_CHECK_THROW(d.wireDecode(
-    "0632 0703080145 FB00 1400 1500 16031B0100 "
-    "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
-    tlv::Error);
+  BOOST_CHECK_EXCEPTION(d.wireDecode(
+                          "0632 0703080145 FB00 1400 1500 16031B0100 "
+                          "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
+                        tlv::Error,
+                        [] (const auto& e) { return e.what() == "Unrecognized element of critical type 251"s; });
 }
 
-BOOST_AUTO_TEST_SUITE_END() // Decode03
+BOOST_AUTO_TEST_SUITE_END() // Decode
 
 BOOST_FIXTURE_TEST_CASE(FullName, IdentityManagementFixture)
 {
@@ -378,7 +416,87 @@
     "sha256digest=28bad4b5275bd392dbb670c75cf0b66f13f7942b21e80f55c0e86b374753a548");
 }
 
-BOOST_AUTO_TEST_CASE(Content)
+BOOST_AUTO_TEST_CASE(SetName)
+{
+  Data d;
+  d.setName("/first");
+  BOOST_CHECK_EQUAL(d.getName(), "/first");
+  BOOST_CHECK_EQUAL(d.hasWire(), false);
+
+  d.setSignatureInfo(SignatureInfo(tlv::DigestSha256));
+  d.setSignatureValue(std::make_shared<Buffer>());
+  d.wireEncode();
+  BOOST_CHECK_EQUAL(d.hasWire(), true);
+  d.setName("/first");
+  BOOST_CHECK_EQUAL(d.getName(), "/first");
+  BOOST_CHECK_EQUAL(d.hasWire(), true);
+
+  d.setName("/second");
+  BOOST_CHECK_EQUAL(d.getName(), "/second");
+  BOOST_CHECK_EQUAL(d.hasWire(), false);
+}
+
+BOOST_AUTO_TEST_CASE(SetContentType)
+{
+  Data d;
+  d.setContentType(tlv::ContentType_Key);
+  BOOST_CHECK_EQUAL(d.getContentType(), tlv::ContentType_Key);
+  BOOST_CHECK_EQUAL(d.hasWire(), false);
+
+  d.setSignatureInfo(SignatureInfo(tlv::DigestSha256));
+  d.setSignatureValue(std::make_shared<Buffer>());
+  d.wireEncode();
+  BOOST_CHECK_EQUAL(d.hasWire(), true);
+  d.setContentType(tlv::ContentType_Key);
+  BOOST_CHECK_EQUAL(d.getContentType(), tlv::ContentType_Key);
+  BOOST_CHECK_EQUAL(d.hasWire(), true);
+
+  d.setContentType(tlv::ContentType_PrefixAnn);
+  BOOST_CHECK_EQUAL(d.getContentType(), tlv::ContentType_PrefixAnn);
+  BOOST_CHECK_EQUAL(d.hasWire(), false);
+}
+
+BOOST_AUTO_TEST_CASE(SetFreshnessPeriod)
+{
+  Data d;
+  d.setFreshnessPeriod(15_min);
+  BOOST_CHECK_EQUAL(d.getFreshnessPeriod(), 15_min);
+  BOOST_CHECK_EQUAL(d.hasWire(), false);
+
+  d.setSignatureInfo(SignatureInfo(tlv::DigestSha256));
+  d.setSignatureValue(std::make_shared<Buffer>());
+  d.wireEncode();
+  BOOST_CHECK_EQUAL(d.hasWire(), true);
+  d.setFreshnessPeriod(15_min);
+  BOOST_CHECK_EQUAL(d.getFreshnessPeriod(), 15_min);
+  BOOST_CHECK_EQUAL(d.hasWire(), true);
+
+  d.setFreshnessPeriod(1_h);
+  BOOST_CHECK_EQUAL(d.getFreshnessPeriod(), 1_h);
+  BOOST_CHECK_EQUAL(d.hasWire(), false);
+}
+
+BOOST_AUTO_TEST_CASE(SetFinalBlock)
+{
+  Data d;
+  d.setFinalBlock(name::Component("foo"));
+  BOOST_CHECK(d.getFinalBlock() == name::Component("foo"));
+  BOOST_CHECK_EQUAL(d.hasWire(), false);
+
+  d.setSignatureInfo(SignatureInfo(tlv::DigestSha256));
+  d.setSignatureValue(std::make_shared<Buffer>());
+  d.wireEncode();
+  BOOST_CHECK_EQUAL(d.hasWire(), true);
+  d.setFinalBlock(name::Component("foo"));
+  BOOST_CHECK(d.getFinalBlock() == name::Component("foo"));
+  BOOST_CHECK_EQUAL(d.hasWire(), true);
+
+  d.setFinalBlock(name::Component("bar"));
+  BOOST_CHECK(d.getFinalBlock() == name::Component("bar"));
+  BOOST_CHECK_EQUAL(d.hasWire(), false);
+}
+
+BOOST_AUTO_TEST_CASE(SetContent)
 {
   Data d;
   BOOST_CHECK_EQUAL(d.getContent().type(), tlv::Content);
@@ -413,7 +531,7 @@
   BOOST_CHECK_THROW(d.setContent(nullptr), std::invalid_argument);
 }
 
-BOOST_AUTO_TEST_CASE(SignatureValue)
+BOOST_AUTO_TEST_CASE(SetSignatureValue)
 {
   Data d;
   BOOST_CHECK_EQUAL(d.getSignatureValue().type(), tlv::Invalid);
diff --git a/tests/unit/interest.t.cpp b/tests/unit/interest.t.cpp
index abb4280..81ae7b4 100644
--- a/tests/unit/interest.t.cpp
+++ b/tests/unit/interest.t.cpp
@@ -354,7 +354,9 @@
   i.setName(Name("/A").appendParametersSha256DigestPlaceholder());
   i.setCanBePrefix(false);
   BOOST_CHECK_EQUAL(i.isParametersDigestValid(), false);
-  BOOST_CHECK_THROW(i.wireEncode(), tlv::Error);
+  BOOST_CHECK_EXCEPTION(i.wireEncode(), tlv::Error, [] (const auto& e) {
+    return e.what() == "Interest without parameters must not have a ParametersSha256DigestComponent"s;
+  });
 }
 
 BOOST_AUTO_TEST_CASE(MissingParametersSha256DigestComponent)
@@ -367,8 +369,12 @@
   BOOST_CHECK_EQUAL(i.isParametersDigestValid(), false);
   BOOST_CHECK_NO_THROW(i.wireEncode()); // this succeeds because it uses the cached wire encoding
 
-  i.setNonce(42); // trigger reencoding
-  BOOST_CHECK_THROW(i.wireEncode(), tlv::Error); // now the check fails while attempting to reencode
+  // trigger reencoding
+  i.setNonce(42);
+  // now the check fails while attempting to reencode
+  BOOST_CHECK_EXCEPTION(i.wireEncode(), tlv::Error, [] (const auto& e) {
+    return e.what() == "Interest with parameters must have a ParametersSha256DigestComponent"s;
+  });
 }
 
 BOOST_AUTO_TEST_SUITE_END() // Encode
@@ -395,7 +401,9 @@
 
 BOOST_AUTO_TEST_CASE(NotAnInterest)
 {
-  BOOST_CHECK_THROW(i.wireDecode("4202CAFE"_block), tlv::Error);
+  BOOST_CHECK_EXCEPTION(i.wireDecode("4202CAFE"_block), tlv::Error, [] (const auto& e) {
+    return e.what() == "Expecting Interest element, but TLV has type 66"s;
+  });
 }
 
 BOOST_AUTO_TEST_CASE(NameOnly)
@@ -500,30 +508,36 @@
 
 BOOST_AUTO_TEST_CASE(CriticalElementOutOfOrder)
 {
-  BOOST_CHECK_THROW(i.wireDecode(
+  BOOST_CHECK_EXCEPTION(i.wireDecode(
     "0529 2100 0703080149 1200 1E0B(1F09 1E023E15 0703080148) "
     "0A044ACB1E4C 0C0276A1 2201D6 2404C0C1C2C3"_block),
-    tlv::Error);
-  BOOST_CHECK_THROW(i.wireDecode(
+    tlv::Error,
+    [] (const auto& e) { return e.what() == "Name element is missing or out of order"s; });
+  BOOST_CHECK_EXCEPTION(i.wireDecode(
     "0529 0703080149 1200 2100 1E0B(1F09 1E023E15 0703080148) "
     "0A044ACB1E4C 0C0276A1 2201D6 2404C0C1C2C3"_block),
-    tlv::Error);
-  BOOST_CHECK_THROW(i.wireDecode(
+    tlv::Error,
+    [] (const auto& e) { return e.what() == "CanBePrefix element is out of order"s; });
+  BOOST_CHECK_EXCEPTION(i.wireDecode(
     "0529 0703080149 2100 1E0B(1F09 1E023E15 0703080148) 1200 "
     "0A044ACB1E4C 0C0276A1 2201D6 2404C0C1C2C3"_block),
-    tlv::Error);
-  BOOST_CHECK_THROW(i.wireDecode(
+    tlv::Error,
+    [] (const auto& e) { return e.what() == "MustBeFresh element is out of order"s; });
+  BOOST_CHECK_EXCEPTION(i.wireDecode(
     "0529 0703080149 2100 1200 0A044ACB1E4C "
     "1E0B(1F09 1E023E15 0703080148) 0C0276A1 2201D6 2404C0C1C2C3"_block),
-    tlv::Error);
-  BOOST_CHECK_THROW(i.wireDecode(
+    tlv::Error,
+    [] (const auto& e) { return e.what() == "ForwardingHint element is out of order"s; });
+  BOOST_CHECK_EXCEPTION(i.wireDecode(
     "0529 0703080149 2100 1200 1E0B(1F09 1E023E15 0703080148) "
     "0C0276A1 0A044ACB1E4C 2201D6 2404C0C1C2C3"_block),
-    tlv::Error);
-  BOOST_CHECK_THROW(i.wireDecode(
+    tlv::Error,
+    [] (const auto& e) { return e.what() == "Nonce element is out of order"s; });
+  BOOST_CHECK_EXCEPTION(i.wireDecode(
     "0529 0703080149 2100 1200 1E0B(1F09 1E023E15 0703080148) "
     "0A044ACB1E4C 2201D6 0C0276A1 2404C0C1C2C3"_block),
-    tlv::Error);
+    tlv::Error,
+    [] (const auto& e) { return e.what() == "InterestLifetime element is out of order"s; });
 }
 
 BOOST_AUTO_TEST_CASE(NonCriticalElementOutOfOrder)
@@ -549,44 +563,52 @@
 
 BOOST_AUTO_TEST_CASE(MissingName)
 {
-  BOOST_CHECK_THROW(i.wireDecode("0500"_block), tlv::Error);
-  BOOST_CHECK_THROW(i.wireDecode("0502 1200"_block), tlv::Error);
+  BOOST_CHECK_EXCEPTION(i.wireDecode("0500"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "Name element is missing or out of order"s; });
+  BOOST_CHECK_EXCEPTION(i.wireDecode("0502 1200"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "Name element is missing or out of order"s; });
 }
 
 BOOST_AUTO_TEST_CASE(BadName)
 {
-  // empty
-  BOOST_CHECK_THROW(i.wireDecode("0502 0700"_block), tlv::Error);
-
-  // more than one ParametersSha256DigestComponent
-  BOOST_CHECK_THROW(i.wireDecode("054C 074A(080149"
-                                 "02200000000000000000000000000000000000000000000000000000000000000000"
-                                 "080132"
-                                 "02200000000000000000000000000000000000000000000000000000000000000000)"_block),
-                    tlv::Error);
+  BOOST_CHECK_EXCEPTION(i.wireDecode("0502 0700"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "Name has zero name components"s; });
+  BOOST_CHECK_EXCEPTION(i.wireDecode("054C 074A(080149"
+    "02200000000000000000000000000000000000000000000000000000000000000000"
+    "080132"
+    "02200000000000000000000000000000000000000000000000000000000000000000)"_block),
+    tlv::Error,
+    [] (const auto& e) { return e.what() == "Name has more than one ParametersSha256DigestComponent"s; });
 }
 
 BOOST_AUTO_TEST_CASE(BadCanBePrefix)
 {
-  BOOST_CHECK_THROW(i.wireDecode("0508 0703080149 210102"_block), tlv::Error);
+  BOOST_CHECK_EXCEPTION(i.wireDecode("0508 0703080149 210102"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "CanBePrefix element has non-zero TLV-LENGTH"s; });
 }
 
 BOOST_AUTO_TEST_CASE(BadMustBeFresh)
 {
-  BOOST_CHECK_THROW(i.wireDecode("0508 0703080149 120102"_block), tlv::Error);
+  BOOST_CHECK_EXCEPTION(i.wireDecode("0508 0703080149 120102"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "MustBeFresh element has non-zero TLV-LENGTH"s; });
 }
 
 BOOST_AUTO_TEST_CASE(BadNonce)
 {
-  BOOST_CHECK_THROW(i.wireDecode("0507 0703080149 0A00"_block), tlv::Error);
-  BOOST_CHECK_THROW(i.wireDecode("050A 0703080149 0A0304C263"_block), tlv::Error);
-  BOOST_CHECK_THROW(i.wireDecode("050C 0703080149 0A05EFA420B262"_block), tlv::Error);
+  BOOST_CHECK_EXCEPTION(i.wireDecode("0507 0703080149 0A00"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "Nonce element is malformed"s; });
+  BOOST_CHECK_EXCEPTION(i.wireDecode("050A 0703080149 0A0304C263"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "Nonce element is malformed"s; });
+  BOOST_CHECK_EXCEPTION(i.wireDecode("050C 0703080149 0A05EFA420B262"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "Nonce element is malformed"s; });
 }
 
 BOOST_AUTO_TEST_CASE(BadHopLimit)
 {
-  BOOST_CHECK_THROW(i.wireDecode("0507 0703080149 2200"_block), tlv::Error);
-  BOOST_CHECK_THROW(i.wireDecode("0509 0703080149 22021356"_block), tlv::Error);
+  BOOST_CHECK_EXCEPTION(i.wireDecode("0507 0703080149 2200"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "HopLimit element is malformed"s; });
+  BOOST_CHECK_EXCEPTION(i.wireDecode("0509 0703080149 22021356"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "HopLimit element is malformed"s; });
 }
 
 BOOST_AUTO_TEST_CASE(BadParametersDigest)
@@ -614,14 +636,17 @@
 
 BOOST_AUTO_TEST_CASE(UnrecognizedNonCriticalElementBeforeName)
 {
-  BOOST_CHECK_THROW(i.wireDecode("0507 FC00 0703080149"_block), tlv::Error);
+  BOOST_CHECK_EXCEPTION(i.wireDecode("0507 FC00 0703080149"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "Name element is missing or out of order"s; });
 }
 
 BOOST_AUTO_TEST_CASE(UnrecognizedCriticalElement)
 {
-  BOOST_CHECK_THROW(i.wireDecode("0507 0703080149 FB00"_block), tlv::Error);
+  BOOST_CHECK_EXCEPTION(i.wireDecode("0507 0703080149 FB00"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "Unrecognized element of critical type 251"s; });
   // v0.2 packet with Selectors
-  BOOST_CHECK_THROW(i.wireDecode("0507 0703080149 09030D0101 0A0401000000"_block), tlv::Error);
+  BOOST_CHECK_EXCEPTION(i.wireDecode("0510 0703080149 09030D0101 0A0401000000"_block), tlv::Error,
+                        [] (const auto& e) { return e.what() == "Unrecognized element of critical type 9"s; });
 }
 
 BOOST_AUTO_TEST_SUITE_END() // Decode