name: alternative URI syntax for component types used in naming conventions

Refs: #4777
Change-Id: I4647fee54dffe104ca3cc31d67b0ad44fcf0de76
diff --git a/ndn-cxx/impl/name-component-types.hpp b/ndn-cxx/impl/name-component-types.hpp
index 2a8563c..760495f 100644
--- a/ndn-cxx/impl/name-component-types.hpp
+++ b/ndn-cxx/impl/name-component-types.hpp
@@ -33,7 +33,7 @@
 namespace name {
 namespace detail {
 
-/** \brief Declare rules regarding a NameComponent type.
+/** \brief Declare rules for a NameComponent type.
  */
 class ComponentType : noncopyable
 {
@@ -151,7 +151,7 @@
   }
 };
 
-/** \brief Rules regarding GenericNameComponent.
+/** \brief Rules for GenericNameComponent.
  *
  *  GenericNameComponent has an alternate URI representation that omits the `<type-number>` prefix.
  *  This must be special-cased in the caller, and is not handled by this class.
@@ -166,7 +166,8 @@
   }
 };
 
-/** \brief Rules regarding a component type holding a SHA256 digest value.
+/** \brief Rules for a component type holding a SHA256 digest value, written as
+ *         a hex string in URI representation.
  */
 class Sha256ComponentType final : public ComponentType
 {
@@ -187,7 +188,8 @@
   void
   check(const Component& comp) const final
   {
-    if (!match(comp)) {
+    BOOST_ASSERT(comp.type() == m_type);
+    if (comp.value_size() != util::Sha256::DIGEST_SIZE) {
       NDN_THROW(Error(m_typeName + " TLV-LENGTH must be " + to_string(util::Sha256::DIGEST_SIZE)));
     }
   }
@@ -250,27 +252,87 @@
   }
 
 private:
-  uint32_t m_type;
-  std::string m_typeName;
-  std::string m_uriPrefix;
+  const uint32_t m_type;
+  const std::string m_typeName;
+  const std::string m_uriPrefix;
 };
 
 inline const Sha256ComponentType&
 getComponentType1()
 {
-  static Sha256ComponentType ct1(tlv::ImplicitSha256DigestComponent,
-                                 "ImplicitSha256DigestComponent", "sha256digest");
+  static const Sha256ComponentType ct1(tlv::ImplicitSha256DigestComponent,
+                                       "ImplicitSha256DigestComponent", "sha256digest");
   return ct1;
 }
 
 inline const Sha256ComponentType&
 getComponentType2()
 {
-  static Sha256ComponentType ct2(tlv::ParametersSha256DigestComponent,
-                                 "ParametersSha256DigestComponent", "params-sha256");
+  static const Sha256ComponentType ct2(tlv::ParametersSha256DigestComponent,
+                                       "ParametersSha256DigestComponent", "params-sha256");
   return ct2;
 }
 
+/** \brief Rules for a component type holding a nonNegativeInteger value, written as
+ *         a decimal number in URI representation.
+ */
+class DecimalComponentType final : public ComponentType
+{
+public:
+  DecimalComponentType(uint32_t type, const std::string& typeName, const std::string& uriPrefix)
+    : m_type(type)
+    , m_typeName(typeName)
+    , m_uriPrefix(uriPrefix)
+  {
+  }
+
+  // NOTE:
+  // We do not override check() and ensure that the component value is a well-formed
+  // nonNegativeInteger, because the application may be using the same typed component
+  // with different syntax and semantics.
+
+  const char*
+  getAltUriPrefix() const final
+  {
+    return m_uriPrefix.data();
+  }
+
+  Component
+  parseAltUriValue(const std::string& input) const final
+  {
+    uint64_t n = 0;
+    try {
+      n = std::stoull(input);
+    }
+    catch (const std::invalid_argument&) {
+      NDN_THROW(Error("Cannot convert to " + m_typeName + " (invalid format)"));
+    }
+    catch (const std::out_of_range&) {
+      NDN_THROW(Error("Cannot convert to " + m_typeName + " (out of range)"));
+    }
+    if (to_string(n) != input) {
+      NDN_THROW(Error("Cannot convert to " + m_typeName + " (invalid format)"));
+    }
+    return Component::fromNumber(n, m_type);
+  }
+
+  void
+  writeUri(std::ostream& os, const Component& comp) const final
+  {
+    if (comp.isNumber()) {
+      os << m_uriPrefix << '=' << comp.toNumber();
+    }
+    else {
+      ComponentType::writeUri(os, comp);
+    }
+  }
+
+private:
+  const uint32_t m_type;
+  const std::string m_typeName;
+  const std::string m_uriPrefix;
+};
+
 /** \brief Rules regarding NameComponent types.
  */
 class ComponentTypeTable : noncopyable
@@ -312,8 +374,8 @@
   }
 
 private:
-  ComponentType m_baseType;
-  std::array<const ComponentType*, 32> m_table;
+  const ComponentType m_baseType;
+  std::array<const ComponentType*, 38> m_table;
   std::unordered_map<std::string, const ComponentType*> m_uriPrefixes;
 };
 
@@ -322,11 +384,22 @@
 {
   m_table.fill(nullptr);
 
-  static GenericNameComponentType ct8;
-  set(tlv::GenericNameComponent, ct8);
-
   set(tlv::ImplicitSha256DigestComponent, getComponentType1());
   set(tlv::ParametersSha256DigestComponent, getComponentType2());
+
+  static const GenericNameComponentType ct8;
+  set(tlv::GenericNameComponent, ct8);
+
+  static const DecimalComponentType ct33(tlv::SegmentNameComponent, "SegmentNameComponent", "seg");
+  set(tlv::SegmentNameComponent, ct33);
+  static const DecimalComponentType ct34(tlv::ByteOffsetNameComponent, "ByteOffsetNameComponent", "off");
+  set(tlv::ByteOffsetNameComponent, ct34);
+  static const DecimalComponentType ct35(tlv::VersionNameComponent, "VersionNameComponent", "v");
+  set(tlv::VersionNameComponent, ct35);
+  static const DecimalComponentType ct36(tlv::TimestampNameComponent, "TimestampNameComponent", "t");
+  set(tlv::TimestampNameComponent, ct36);
+  static const DecimalComponentType ct37(tlv::SequenceNumNameComponent, "SequenceNumNameComponent", "seq");
+  set(tlv::SequenceNumNameComponent, ct37);
 }
 
 /** \brief Get the global ComponentTypeTable.
diff --git a/ndn-cxx/name-component.cpp b/ndn-cxx/name-component.cpp
index b7b9333..f41fda0 100644
--- a/ndn-cxx/name-component.cpp
+++ b/ndn-cxx/name-component.cpp
@@ -163,7 +163,7 @@
 
   auto ct = detail::getComponentTypeTable().findByUriPrefix(typePrefix);
   if (ct == nullptr) {
-    NDN_THROW(Error("Incorrect TLV-TYPE '" + typePrefix + "' in NameComponent URI"));
+    NDN_THROW(Error("Unknown TLV-TYPE '" + typePrefix + "' in NameComponent URI"));
   }
   return ct->parseAltUriValue(input.substr(equalPos + 1));
 }
@@ -187,16 +187,15 @@
 bool
 Component::isNumber() const
 {
-  return (value_size() == 1 || value_size() == 2 ||
-          value_size() == 4 || value_size() == 8);
+  return value_size() == 1 || value_size() == 2 ||
+         value_size() == 4 || value_size() == 8;
 }
 
 bool
 Component::isNumberWithMarker(uint8_t marker) const
 {
-  return (!empty() && value()[0] == marker &&
-          (value_size() == 2 || value_size() == 3 ||
-           value_size() == 5 || value_size() == 9));
+  return (value_size() == 2 || value_size() == 3 ||
+          value_size() == 5 || value_size() == 9) && value()[0] == marker;
 }
 
 bool
diff --git a/ndn-cxx/name-component.hpp b/ndn-cxx/name-component.hpp
index fbea9ac..2039e21 100644
--- a/ndn-cxx/name-component.hpp
+++ b/ndn-cxx/name-component.hpp
@@ -31,6 +31,7 @@
 namespace name {
 
 /** @brief Identify a style of NDN Naming Conventions.
+ *  @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
  */
 enum class Convention {
   MARKER = 1 << 0, ///< component markers (revision 1)
@@ -249,21 +250,17 @@
   fromEscapedString(const std::string& input);
 
   /**
-   * @brief Write *this to the output stream, escaping characters according to the NDN URI Scheme
-   *
-   * This also adds "..." to a value with zero or more "."
-   *
+   * @brief Write *this to the output stream, escaping characters according to the NDN URI format.
    * @param os The output stream where to write the URI escaped version of *this
+   * @sa http://named-data.net/doc/NDN-packet-spec/current/name.html#ndn-uri-scheme
    */
   void
   toUri(std::ostream& os) const;
 
   /**
-   * @brief Convert *this by escaping characters according to the NDN URI Scheme
-   *
-   * This also adds "..." to a value with zero or more "."
-   *
+   * @brief Convert *this by escaping characters according to the NDN URI format.
    * @return The escaped string
+   * @sa http://named-data.net/doc/NDN-packet-spec/current/name.html#ndn-uri-scheme
    */
   std::string
   toUri() const;
@@ -278,28 +275,29 @@
 
   /**
    * @brief Check if the component is a NameComponentWithMarker per NDN naming conventions rev1
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa NDN Naming Conventions revision 1:
+   *     https://named-data.net/wp-content/uploads/2014/08/ndn-tr-22-ndn-memo-naming-conventions.pdf
    */
   bool
   isNumberWithMarker(uint8_t marker) const;
 
   /**
    * @brief Check if the component is a version per NDN naming conventions
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
    */
   bool
   isVersion() const;
 
   /**
    * @brief Check if the component is a segment number per NDN naming conventions
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
    */
   bool
   isSegment() const;
 
   /**
    * @brief Check if the component is a byte offset per NDN naming conventions
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
    */
   bool
   isByteOffset() const;
@@ -313,14 +311,14 @@
 
   /**
    * @brief Check if the component is a timestamp per NDN naming conventions
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
    */
   bool
   isTimestamp() const;
 
   /**
    * @brief Check if the component is a sequence number per NDN naming conventions
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
    */
   bool
   isSequenceNumber() const;
@@ -338,7 +336,8 @@
   /**
    * @brief Interpret this name component as NameComponentWithMarker
    *
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa NDN Naming Conventions revision 1:
+   *     https://named-data.net/wp-content/uploads/2014/08/ndn-tr-22-ndn-memo-naming-conventions.pdf
    *
    * @param marker 1-byte octet of the marker
    * @return The integer number.
@@ -351,7 +350,7 @@
   /**
    * @brief Interpret as version component using NDN naming conventions
    *
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
    *
    * @throw tlv::Error not a Version component interpreted by the chosen convention(s).
    */
@@ -361,7 +360,7 @@
   /**
    * @brief Interpret as segment number component using NDN naming conventions
    *
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
    *
    * @throw tlv::Error not a Segment component interpreted by the chosen convention(s).
    */
@@ -371,7 +370,7 @@
   /**
    * @brief Interpret as byte offset component using NDN naming conventions
    *
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
    *
    * @throw tlv::Error not a ByteOffset component interpreted by the chosen convention(s).
    */
@@ -388,7 +387,7 @@
   /**
    * @brief Interpret as timestamp component using NDN naming conventions
    *
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
    *
    * @throw tlv::Error not a Timestamp component interpreted by the chosen convention(s).
    */
@@ -398,7 +397,7 @@
   /**
    * @brief Interpret as sequence number component using NDN naming conventions
    *
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
    *
    * @throw tlv::Error not a SequenceNumber component interpreted by the chosen convention(s).
    */
@@ -421,17 +420,14 @@
    *
    * NameComponentWithMarker is defined as:
    *
-   *     NameComponentWithMarker ::= NAME-COMPONENT-TYPE TLV-LEGTH
+   *     NameComponentWithMarker ::= NAME-COMPONENT-TYPE TLV-LENGTH
    *                                   Marker
    *                                   includedNonNegativeInteger
    *     Marker ::= BYTE
    *     includedNonNegativeInteger ::= BYTE{1,2,4,8}
-   *     NDN-TLV := TLV-TYPE TLV-LENGTH TLV-VALUE?
-   *     TLV-TYPE := VAR-NUMBER
-   *     TLV-LENGTH := VAR-NUMBER
-   *     TLV-VALUE := BYTE+
    *
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa NDN Naming Conventions revision 1:
+   *     https://named-data.net/wp-content/uploads/2014/08/ndn-tr-22-ndn-memo-naming-conventions.pdf
    *
    * @param marker 1-byte marker octet
    * @param number The non-negative number
@@ -442,7 +438,7 @@
   /**
    * @brief Create version component using NDN naming conventions
    *
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
    */
   static Component
   fromVersion(uint64_t version);
@@ -450,7 +446,7 @@
   /**
    * @brief Create segment number component using NDN naming conventions
    *
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
    */
   static Component
   fromSegment(uint64_t segmentNo);
@@ -458,7 +454,7 @@
   /**
    * @brief Create byte offset component using NDN naming conventions
    *
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
    */
   static Component
   fromByteOffset(uint64_t offset);
@@ -473,7 +469,7 @@
   /**
    * @brief Create sequence number component using NDN naming conventions
    *
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
    */
   static Component
   fromTimestamp(const time::system_clock::TimePoint& timePoint);
@@ -481,7 +477,7 @@
   /**
    * @brief Create sequence number component using NDN naming conventions
    *
-   * @sa NDN Naming Conventions https://named-data.net/doc/tech-memos/naming-conventions.pdf
+   * @sa https://named-data.net/publications/techreports/ndn-tr-22-2-ndn-memo-naming-conventions/
    */
   static Component
   fromSequenceNumber(uint64_t seqNo);
diff --git a/tests/unit/name-component.t.cpp b/tests/unit/name-component.t.cpp
index 4ee25ea..c009175 100644
--- a/tests/unit/name-component.t.cpp
+++ b/tests/unit/name-component.t.cpp
@@ -26,6 +26,8 @@
 #include "tests/boost-test.hpp"
 
 #include <boost/algorithm/string/case_conv.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/lexical_cast.hpp>
 #include <boost/mpl/vector.hpp>
 
 namespace ndn {
@@ -36,11 +38,17 @@
 
 BOOST_AUTO_TEST_SUITE(Decode)
 
+#define CHECK_COMP_ERR(expr, whatstring) \
+  BOOST_CHECK_EXCEPTION(expr, Component::Error, \
+                        [] (const auto& e) { return boost::contains(e.what(), whatstring); })
+
 BOOST_AUTO_TEST_CASE(Generic)
 {
   Component comp("0807 6E646E2D637878"_block);
   BOOST_CHECK_EQUAL(comp.type(), tlv::GenericNameComponent);
+  BOOST_CHECK_EQUAL(comp.isGeneric(), true);
   BOOST_CHECK_EQUAL(comp.toUri(), "ndn-cxx");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(comp), "ndn-cxx");
   BOOST_CHECK_EQUAL(Component::fromEscapedString("ndn-cxx"), comp);
   BOOST_CHECK_EQUAL(Component::fromEscapedString("8=ndn-cxx"), comp);
 
@@ -82,38 +90,96 @@
 }
 
 static void
-testSha256(uint32_t type, const std::string& uriPrefix)
+testSha256Component(uint32_t type, const std::string& uriPrefix)
 {
-  std::string hexLower = "28bad4b5275bd392dbb670c75cf0b66f13f7942b21e80f55c0e86b374753a548";
-  std::string hexUpper = boost::to_upper_copy(hexLower);
+  const std::string hexLower = "28bad4b5275bd392dbb670c75cf0b66f13f7942b21e80f55c0e86b374753a548";
+  const std::string hexUpper = boost::to_upper_copy(hexLower);
   std::string hexPct;
   for (size_t i = 0; i < hexUpper.size(); i += 2) {
     hexPct += "%" + hexUpper.substr(i, 2);
   }
 
   Component comp(Block(type, fromHex(hexLower)));
+
   BOOST_CHECK_EQUAL(comp.type(), type);
   BOOST_CHECK_EQUAL(comp.toUri(), uriPrefix + hexLower);
-  BOOST_CHECK_EQUAL(Component::fromEscapedString(uriPrefix + hexLower), comp);
-  BOOST_CHECK_EQUAL(Component::fromEscapedString(uriPrefix + hexUpper), comp);
-  BOOST_CHECK_EQUAL(Component::fromEscapedString(to_string(type) + "=" + hexPct), comp);
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(comp), uriPrefix + hexLower);
+  BOOST_CHECK_EQUAL(comp, Component::fromEscapedString(uriPrefix + hexLower));
+  BOOST_CHECK_EQUAL(comp, Component::fromEscapedString(uriPrefix + hexUpper));
+  BOOST_CHECK_EQUAL(comp, Component::fromEscapedString(to_string(type) + "=" + hexPct));
 
-  BOOST_CHECK_THROW(comp.wireDecode(Block(type, fromHex("A791806951F25C4D"))), Component::Error);
-  BOOST_CHECK_THROW(Component::fromEscapedString(uriPrefix), Component::Error);
-  BOOST_CHECK_THROW(Component::fromEscapedString(uriPrefix + "a791806951f25c4d"),
-                    Component::Error);
-  BOOST_CHECK_THROW(Component::fromEscapedString(boost::to_upper_copy(uriPrefix) + hexLower),
-                    Component::Error);
+  CHECK_COMP_ERR(comp.wireDecode(Block(type, fromHex("A791806951F25C4D"))), "TLV-LENGTH must be 32");
+  CHECK_COMP_ERR(Component::fromEscapedString(uriPrefix), "TLV-LENGTH must be 32");
+  CHECK_COMP_ERR(Component::fromEscapedString(uriPrefix + "a791806951f25c4d"), "TLV-LENGTH must be 32");
+  CHECK_COMP_ERR(Component::fromEscapedString(uriPrefix + "foo"), "invalid hex encoding");
+  CHECK_COMP_ERR(Component::fromEscapedString(boost::to_upper_copy(uriPrefix) + hexLower), "Unknown TLV-TYPE");
 }
 
-BOOST_AUTO_TEST_CASE(Digest)
+BOOST_AUTO_TEST_CASE(ImplicitDigest)
 {
-  testSha256(tlv::ImplicitSha256DigestComponent, "sha256digest=");
+  testSha256Component(tlv::ImplicitSha256DigestComponent, "sha256digest=");
 }
 
-BOOST_AUTO_TEST_CASE(Params)
+BOOST_AUTO_TEST_CASE(ParametersDigest)
 {
-  testSha256(tlv::ParametersSha256DigestComponent, "params-sha256=");
+  testSha256Component(tlv::ParametersSha256DigestComponent, "params-sha256=");
+}
+
+static void
+testDecimalComponent(uint32_t type, const std::string& uriPrefix)
+{
+  const Component comp(makeNonNegativeIntegerBlock(type, 42)); // TLV-VALUE is a nonNegativeInteger
+  BOOST_CHECK_EQUAL(comp.type(), type);
+  BOOST_CHECK_EQUAL(comp.isNumber(), true);
+  const auto compUri = uriPrefix + "42";
+  BOOST_CHECK_EQUAL(comp.toUri(), compUri);
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(comp), compUri);
+  BOOST_CHECK_EQUAL(comp, Component::fromEscapedString(compUri));
+  BOOST_CHECK_EQUAL(comp, Component::fromEscapedString(to_string(type) + "=%2A"));
+  BOOST_CHECK_EQUAL(comp, Component::fromNumber(42, type));
+
+  const Component comp2(Block(type, fromHex("010203"))); // TLV-VALUE is *not* a nonNegativeInteger
+  BOOST_CHECK_EQUAL(comp2.type(), type);
+  BOOST_CHECK_EQUAL(comp2.isNumber(), false);
+  const auto comp2Uri = to_string(type) + "=%01%02%03";
+  BOOST_CHECK_EQUAL(comp2.toUri(), comp2Uri);
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(comp2), comp2Uri);
+  BOOST_CHECK_EQUAL(comp2, Component::fromEscapedString(comp2Uri));
+
+  CHECK_COMP_ERR(Component::fromEscapedString(uriPrefix), "invalid format");
+  CHECK_COMP_ERR(Component::fromEscapedString(uriPrefix + "foo"), "invalid format");
+  CHECK_COMP_ERR(Component::fromEscapedString(uriPrefix + "00"), "invalid format");
+  CHECK_COMP_ERR(Component::fromEscapedString(uriPrefix + "-1"), "invalid format");
+  CHECK_COMP_ERR(Component::fromEscapedString(uriPrefix + "9.3"), "invalid format");
+  CHECK_COMP_ERR(Component::fromEscapedString(uriPrefix + " 84"), "invalid format");
+  CHECK_COMP_ERR(Component::fromEscapedString(uriPrefix + "0xAF"), "invalid format");
+  CHECK_COMP_ERR(Component::fromEscapedString(uriPrefix + "18446744073709551616"), "out of range");
+  CHECK_COMP_ERR(Component::fromEscapedString(boost::to_upper_copy(uriPrefix) + "42"), "Unknown TLV-TYPE");
+}
+
+BOOST_AUTO_TEST_CASE(Segment)
+{
+  testDecimalComponent(tlv::SegmentNameComponent, "seg=");
+}
+
+BOOST_AUTO_TEST_CASE(ByteOffset)
+{
+  testDecimalComponent(tlv::ByteOffsetNameComponent, "off=");
+}
+
+BOOST_AUTO_TEST_CASE(Version)
+{
+  testDecimalComponent(tlv::VersionNameComponent, "v=");
+}
+
+BOOST_AUTO_TEST_CASE(Timestamp)
+{
+  testDecimalComponent(tlv::TimestampNameComponent, "t=");
+}
+
+BOOST_AUTO_TEST_CASE(SequenceNum)
+{
+  testDecimalComponent(tlv::SequenceNumNameComponent, "seq=");
 }
 
 BOOST_AUTO_TEST_CASE(OtherType)
@@ -165,7 +231,7 @@
 
 BOOST_AUTO_TEST_CASE(Compare)
 {
-  std::vector<Component> comps = {
+  const std::vector<Component> comps = {
     Component("0120 0000000000000000000000000000000000000000000000000000000000000000"_block),
     Component("0120 0000000000000000000000000000000000000000000000000000000000000001"_block),
     Component("0120 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"_block),
@@ -202,12 +268,10 @@
 
 BOOST_AUTO_TEST_SUITE(CreateFromIterators) // Bug 2490
 
-typedef boost::mpl::vector<
-  std::vector<uint8_t>,
-  std::list<uint8_t>,
-  std::vector<int8_t>,
-  std::list<int8_t>
-> ContainerTypes;
+using ContainerTypes = boost::mpl::vector<std::vector<uint8_t>,
+                                          std::list<uint8_t>,
+                                          std::vector<int8_t>,
+                                          std::list<int8_t>>;
 
 BOOST_AUTO_TEST_CASE_TEMPLATE(ZeroOctet, T, ContainerTypes)
 {
@@ -243,12 +307,12 @@
 template<typename ArgType>
 struct ConventionTest
 {
-  function<Component(ArgType)> makeComponent;
-  function<ArgType(const Component&)> getValue;
-  function<Name&(Name&, ArgType)> append;
+  std::function<Component(ArgType)> makeComponent;
+  std::function<ArgType(const Component&)> getValue;
+  std::function<Name&(Name&, ArgType)> append;
   Name expected;
   ArgType value;
-  function<bool(const Component&)> isComponent;
+  std::function<bool(const Component&)> isComponent;
 };
 
 class ConventionMarker
@@ -478,7 +542,7 @@
   auto test = T()();
 
   const Name& expected = test.expected;
-  BOOST_TEST_MESSAGE("Check " << expected[0].toUri());
+  BOOST_TEST_MESSAGE("Check " << expected[0]);
 
   Component actualComponent = test.makeComponent(test.value);
   BOOST_CHECK_EQUAL(actualComponent, expected[0]);
diff --git a/tests/unit/name.t.cpp b/tests/unit/name.t.cpp
index 362f443..44c261e 100644
--- a/tests/unit/name.t.cpp
+++ b/tests/unit/name.t.cpp
@@ -323,7 +323,7 @@
   const time::system_clock::TimePoint tp = time::system_clock::now();
   time::system_clock::TimePoint tp2;
   BOOST_REQUIRE_NO_THROW(tp2 = name.appendTimestamp(tp).at(-1).toTimestamp());
-  BOOST_CHECK_LE(std::abs(time::duration_cast<time::microseconds>(tp2 - tp).count()), 1);
+  BOOST_CHECK_LE(time::abs(tp2 - tp), 1_us);
 
   BOOST_REQUIRE_NO_THROW(number = name.appendSequenceNumber(11676).at(-1).toSequenceNumber());
   BOOST_CHECK_EQUAL(number, 11676);