mgmt: FaceStatus equality operators and formatted output

Change-Id: I724227cf82d254cc1caac8a68d8ba24e608d01be
Refs: #3903
diff --git a/src/mgmt/nfd/face-status.cpp b/src/mgmt/nfd/face-status.cpp
index d54f072..9417e28 100644
--- a/src/mgmt/nfd/face-status.cpp
+++ b/src/mgmt/nfd/face-status.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -20,26 +20,24 @@
  */
 
 #include "face-status.hpp"
-#include "encoding/tlv-nfd.hpp"
 #include "encoding/block-helpers.hpp"
+#include "encoding/encoding-buffer.hpp"
+#include "encoding/tlv-nfd.hpp"
 #include "util/concepts.hpp"
 
+#include <iomanip>
+
 namespace ndn {
 namespace nfd {
 
-//BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceStatus>));
-BOOST_CONCEPT_ASSERT((WireEncodable<FaceStatus>));
-BOOST_CONCEPT_ASSERT((WireDecodable<FaceStatus>));
-static_assert(std::is_base_of<tlv::Error, FaceStatus::Error>::value,
-              "FaceStatus::Error must inherit from tlv::Error");
+BOOST_CONCEPT_ASSERT((StatusDatasetItem<FaceStatus>));
 
 FaceStatus::FaceStatus()
-  : m_hasExpirationPeriod(false)
-  , m_nInInterests(0)
-  , m_nInDatas(0)
+  : m_nInInterests(0)
+  , m_nInData(0)
   , m_nInNacks(0)
   , m_nOutInterests(0)
-  , m_nOutDatas(0)
+  , m_nOutData(0)
   , m_nOutNacks(0)
   , m_nInBytes(0)
   , m_nOutBytes(0)
@@ -66,13 +64,13 @@
   totalLength += prependNonNegativeIntegerBlock(encoder,
                  tlv::nfd::NOutNacks, m_nOutNacks);
   totalLength += prependNonNegativeIntegerBlock(encoder,
-                 tlv::nfd::NOutDatas, m_nOutDatas);
+                 tlv::nfd::NOutDatas, m_nOutData);
   totalLength += prependNonNegativeIntegerBlock(encoder,
                  tlv::nfd::NOutInterests, m_nOutInterests);
   totalLength += prependNonNegativeIntegerBlock(encoder,
                  tlv::nfd::NInNacks, m_nInNacks);
   totalLength += prependNonNegativeIntegerBlock(encoder,
-                 tlv::nfd::NInDatas, m_nInDatas);
+                 tlv::nfd::NInDatas, m_nInData);
   totalLength += prependNonNegativeIntegerBlock(encoder,
                  tlv::nfd::NInInterests, m_nInInterests);
   totalLength += prependNonNegativeIntegerBlock(encoder,
@@ -81,14 +79,14 @@
                  tlv::nfd::FacePersistency, m_facePersistency);
   totalLength += prependNonNegativeIntegerBlock(encoder,
                  tlv::nfd::FaceScope, m_faceScope);
-  if (m_hasExpirationPeriod) {
+  if (m_expirationPeriod) {
     totalLength += prependNonNegativeIntegerBlock(encoder,
-                   tlv::nfd::ExpirationPeriod, m_expirationPeriod.count());
+                   tlv::nfd::ExpirationPeriod, static_cast<uint64_t>(m_expirationPeriod->count()));
   }
   totalLength += encoder.prependByteArrayBlock(tlv::nfd::LocalUri,
-                 reinterpret_cast<const uint8_t*>(m_localUri.c_str()), m_localUri.size());
+                 reinterpret_cast<const uint8_t*>(m_localUri.data()), m_localUri.size());
   totalLength += encoder.prependByteArrayBlock(tlv::nfd::Uri,
-                 reinterpret_cast<const uint8_t*>(m_remoteUri.c_str()), m_remoteUri.size());
+                 reinterpret_cast<const uint8_t*>(m_remoteUri.data()), m_remoteUri.size());
   totalLength += prependNonNegativeIntegerBlock(encoder,
                  tlv::nfd::FaceId, m_faceId);
 
@@ -154,13 +152,11 @@
   }
 
   if (val != m_wire.elements_end() && val->type() == tlv::nfd::ExpirationPeriod) {
-    m_expirationPeriod = time::milliseconds(readNonNegativeInteger(*val));
-    m_hasExpirationPeriod = true;
+    m_expirationPeriod.emplace(readNonNegativeInteger(*val));
     ++val;
   }
   else {
-    m_hasExpirationPeriod = false;
-    // ExpirationPeriod is optional
+    m_expirationPeriod = nullopt;
   }
 
   if (val != m_wire.elements_end() && val->type() == tlv::nfd::FaceScope) {
@@ -196,7 +192,7 @@
   }
 
   if (val != m_wire.elements_end() && val->type() == tlv::nfd::NInDatas) {
-    m_nInDatas = readNonNegativeInteger(*val);
+    m_nInData = readNonNegativeInteger(*val);
     ++val;
   }
   else {
@@ -220,7 +216,7 @@
   }
 
   if (val != m_wire.elements_end() && val->type() == tlv::nfd::NOutDatas) {
-    m_nOutDatas = readNonNegativeInteger(*val);
+    m_nOutData = readNonNegativeInteger(*val);
     ++val;
   }
   else {
@@ -261,11 +257,10 @@
 }
 
 FaceStatus&
-FaceStatus::setExpirationPeriod(const time::milliseconds& expirationPeriod)
+FaceStatus::setExpirationPeriod(time::milliseconds expirationPeriod)
 {
   m_wire.reset();
   m_expirationPeriod = expirationPeriod;
-  m_hasExpirationPeriod = true;
   return *this;
 }
 
@@ -278,10 +273,10 @@
 }
 
 FaceStatus&
-FaceStatus::setNInDatas(uint64_t nInDatas)
+FaceStatus::setNInDatas(uint64_t nInData)
 {
   m_wire.reset();
-  m_nInDatas = nInDatas;
+  m_nInData = nInData;
   return *this;
 }
 
@@ -302,10 +297,10 @@
 }
 
 FaceStatus&
-FaceStatus::setNOutDatas(uint64_t nOutDatas)
+FaceStatus::setNOutDatas(uint64_t nOutData)
 {
   m_wire.reset();
-  m_nOutDatas = nOutDatas;
+  m_nOutData = nOutData;
   return *this;
 }
 
@@ -339,39 +334,62 @@
   m_wire.reset();
 }
 
+bool
+operator==(const FaceStatus& a, const FaceStatus& b)
+{
+  return a.getFaceId() == b.getFaceId() &&
+      a.getRemoteUri() == b.getRemoteUri() &&
+      a.getLocalUri() == b.getLocalUri() &&
+      a.getFaceScope() == b.getFaceScope() &&
+      a.getFacePersistency() == b.getFacePersistency() &&
+      a.getLinkType() == b.getLinkType() &&
+      a.getFlags() == b.getFlags() &&
+      a.hasExpirationPeriod() == b.hasExpirationPeriod() &&
+      (!a.hasExpirationPeriod() || a.getExpirationPeriod() == b.getExpirationPeriod()) &&
+      a.getNInInterests() == b.getNInInterests() &&
+      a.getNInDatas() == b.getNInDatas() &&
+      a.getNInNacks() == b.getNInNacks() &&
+      a.getNOutInterests() == b.getNOutInterests() &&
+      a.getNOutDatas() == b.getNOutDatas() &&
+      a.getNOutNacks() == b.getNOutNacks() &&
+      a.getNInBytes() == b.getNInBytes() &&
+      a.getNOutBytes() == b.getNOutBytes();
+}
+
 std::ostream&
 operator<<(std::ostream& os, const FaceStatus& status)
 {
-  os << "FaceStatus("
-     << "FaceID: " << status.getFaceId() << ",\n"
-     << "RemoteUri: " << status.getRemoteUri() << ",\n"
-     << "LocalUri: " << status.getLocalUri() << ",\n";
+  os << "Face(FaceId: " << status.getFaceId() << ",\n"
+     << "     RemoteUri: " << status.getRemoteUri() << ",\n"
+     << "     LocalUri: " << status.getLocalUri() << ",\n";
 
   if (status.hasExpirationPeriod()) {
-    os << "ExpirationPeriod: " << status.getExpirationPeriod() << ",\n";
+    os << "     ExpirationPeriod: " << status.getExpirationPeriod() << ",\n";
   }
   else {
-    os << "ExpirationPeriod: infinite,\n";
+    os << "     ExpirationPeriod: infinite,\n";
   }
 
-  os << "FaceScope: " << status.getFaceScope() << ",\n"
-     << "FacePersistency: " << status.getFacePersistency() << ",\n"
-     << "LinkType: " << status.getLinkType() << ",\n";
+  os << "     FaceScope: " << status.getFaceScope() << ",\n"
+     << "     FacePersistency: " << status.getFacePersistency() << ",\n"
+     << "     LinkType: " << status.getLinkType() << ",\n";
 
   auto osFlags = os.flags();
-  os << "Flags: " << std::showbase << std::hex << status.getFlags() << ",\n";
+  // std::showbase doesn't work with number 0
+  os << "     Flags: 0x" << std::noshowbase << std::noshowpos << std::nouppercase
+     << std::hex << status.getFlags() << ",\n";
   os.flags(osFlags);
 
-  os << "Counters: { Interests: {in: " << status.getNInInterests() << ", "
+  os << "     Counters: {Interests: {in: " << status.getNInInterests() << ", "
      << "out: " << status.getNOutInterests() << "},\n"
-     << "            Data: {in: " << status.getNInDatas() << ", "
+     << "                Data: {in: " << status.getNInDatas() << ", "
      << "out: " << status.getNOutDatas() << "},\n"
-     << "            Nack: {in: " << status.getNInNacks() << ", "
+     << "                Nacks: {in: " << status.getNInNacks() << ", "
      << "out: " << status.getNOutNacks() << "},\n"
-     << "            bytes: {in: " << status.getNInBytes() << ", "
-     << "out: " << status.getNOutBytes() << "} }\n"
-     << ")";
-  return os;
+     << "                bytes: {in: " << status.getNInBytes() << ", "
+     << "out: " << status.getNOutBytes() << "}}\n";
+
+  return os << "     )";
 }
 
 } // namespace nfd
diff --git a/src/mgmt/nfd/face-status.hpp b/src/mgmt/nfd/face-status.hpp
index cbd2e60..9bffa81 100644
--- a/src/mgmt/nfd/face-status.hpp
+++ b/src/mgmt/nfd/face-status.hpp
@@ -31,8 +31,8 @@
 
 /**
  * \ingroup management
- * \brief represents Face status
- * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Face-Dataset
+ * \brief represents an item in NFD Face dataset
+ * \sa https://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Face-Dataset
  */
 class FaceStatus : public FaceTraits<FaceStatus>
 {
@@ -62,18 +62,18 @@
   bool
   hasExpirationPeriod() const
   {
-    return m_hasExpirationPeriod;
+    return !!m_expirationPeriod;
   }
 
-  const time::milliseconds&
+  time::milliseconds
   getExpirationPeriod() const
   {
-    BOOST_ASSERT(m_hasExpirationPeriod);
-    return m_expirationPeriod;
+    BOOST_ASSERT(hasExpirationPeriod());
+    return *m_expirationPeriod;
   }
 
   FaceStatus&
-  setExpirationPeriod(const time::milliseconds& expirationPeriod);
+  setExpirationPeriod(time::milliseconds expirationPeriod);
 
   uint64_t
   getNInInterests() const
@@ -87,11 +87,11 @@
   uint64_t
   getNInDatas() const
   {
-    return m_nInDatas;
+    return m_nInData;
   }
 
   FaceStatus&
-  setNInDatas(uint64_t nInDatas);
+  setNInDatas(uint64_t nInData);
 
   uint64_t
   getNInNacks() const
@@ -114,11 +114,11 @@
   uint64_t
   getNOutDatas() const
   {
-    return m_nOutDatas;
+    return m_nOutData;
   }
 
   FaceStatus&
-  setNOutDatas(uint64_t nOutDatas);
+  setNOutDatas(uint64_t nOutData);
 
   uint64_t
   getNOutNacks() const
@@ -152,13 +152,12 @@
   wireReset() const override;
 
 private:
-  time::milliseconds m_expirationPeriod;
-  bool m_hasExpirationPeriod;
+  optional<time::milliseconds> m_expirationPeriod;
   uint64_t m_nInInterests;
-  uint64_t m_nInDatas;
+  uint64_t m_nInData;
   uint64_t m_nInNacks;
   uint64_t m_nOutInterests;
-  uint64_t m_nOutDatas;
+  uint64_t m_nOutData;
   uint64_t m_nOutNacks;
   uint64_t m_nInBytes;
   uint64_t m_nOutBytes;
@@ -166,6 +165,15 @@
   mutable Block m_wire;
 };
 
+bool
+operator==(const FaceStatus& a, const FaceStatus& b);
+
+inline bool
+operator!=(const FaceStatus& a, const FaceStatus& b)
+{
+  return !(a == b);
+}
+
 std::ostream&
 operator<<(std::ostream& os, const FaceStatus& status);
 
diff --git a/tests/unit-tests/mgmt/nfd/face-status.t.cpp b/tests/unit-tests/mgmt/nfd/face-status.t.cpp
index 9f7118d..47fefae 100644
--- a/tests/unit-tests/mgmt/nfd/face-status.t.cpp
+++ b/tests/unit-tests/mgmt/nfd/face-status.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -22,6 +22,7 @@
 #include "mgmt/nfd/face-status.hpp"
 
 #include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
 
 namespace ndn {
 namespace nfd {
@@ -31,33 +32,37 @@
 BOOST_AUTO_TEST_SUITE(Nfd)
 BOOST_AUTO_TEST_SUITE(TestFaceStatus)
 
+static FaceStatus
+makeFaceStatus()
+{
+  return FaceStatus()
+      .setFaceId(100)
+      .setRemoteUri("tcp4://192.0.2.1:6363")
+      .setLocalUri("tcp4://192.0.2.2:55555")
+      .setFaceScope(FACE_SCOPE_LOCAL)
+      .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+      .setLinkType(LINK_TYPE_MULTI_ACCESS)
+      .setExpirationPeriod(time::seconds(10))
+      .setNInInterests(10)
+      .setNInDatas(200)
+      .setNInNacks(1)
+      .setNOutInterests(3000)
+      .setNOutDatas(4)
+      .setNOutNacks(2)
+      .setNInBytes(1329719163)
+      .setNOutBytes(999110448)
+      .setFlags(0x7);
+}
+
 BOOST_AUTO_TEST_CASE(Encode)
 {
-  FaceStatus status1;
-  status1.setFaceId(100)
-         .setRemoteUri("tcp4://192.0.2.1:6363")
-         .setLocalUri("tcp4://192.0.2.2:55555")
-         .setFaceScope(FACE_SCOPE_LOCAL)
-         .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
-         .setLinkType(LINK_TYPE_MULTI_ACCESS)
-         .setExpirationPeriod(time::seconds(10))
-         .setNInInterests(10)
-         .setNInDatas(200)
-         .setNInNacks(1)
-         .setNOutInterests(3000)
-         .setNOutDatas(4)
-         .setNOutNacks(2)
-         .setNInBytes(1329719163)
-         .setNOutBytes(999110448)
-         .setFlags(0x7);
-
-  Block wire;
-  BOOST_REQUIRE_NO_THROW(wire = status1.wireEncode());
+  FaceStatus status1 = makeFaceStatus();
+  Block wire = status1.wireEncode();
 
   // These octets are obtained by the snippet below.
   // This check is intended to detect unexpected encoding change in the future.
   // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
-  //  printf("0x%02x, ", *it);
+  //   printf("0x%02x, ", *it);
   // }
   static const uint8_t expected[] = {
     0x80, 0x61, 0x69, 0x01, 0x64, 0x72, 0x15, 0x74, 0x63, 0x70,
@@ -74,39 +79,55 @@
   BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
                                 wire.begin(), wire.end());
 
-  BOOST_REQUIRE_NO_THROW(FaceStatus(wire));
   FaceStatus status2(wire);
-  BOOST_CHECK_EQUAL(status1.getFaceId(), status2.getFaceId());
-  BOOST_CHECK_EQUAL(status1.getRemoteUri(), status2.getRemoteUri());
-  BOOST_CHECK_EQUAL(status1.getLocalUri(), status2.getLocalUri());
-  BOOST_CHECK_EQUAL(status1.getFaceScope(), status2.getFaceScope());
-  BOOST_CHECK_EQUAL(status1.getFacePersistency(), status2.getFacePersistency());
-  BOOST_CHECK_EQUAL(status1.getLinkType(), status2.getLinkType());
-  BOOST_CHECK_EQUAL(status1.getNInInterests(), status2.getNInInterests());
-  BOOST_CHECK_EQUAL(status1.getNInDatas(), status2.getNInDatas());
-  BOOST_CHECK_EQUAL(status1.getNInNacks(), status2.getNInNacks());
-  BOOST_CHECK_EQUAL(status1.getNOutInterests(), status2.getNOutInterests());
-  BOOST_CHECK_EQUAL(status1.getNOutDatas(), status2.getNOutDatas());
-  BOOST_CHECK_EQUAL(status1.getNOutNacks(), status2.getNOutNacks());
-  BOOST_CHECK_EQUAL(status1.getNInBytes(), status2.getNInBytes());
-  BOOST_CHECK_EQUAL(status1.getNOutBytes(), status2.getNOutBytes());
-  BOOST_CHECK_EQUAL(status1.getFlags(), status2.getFlags());
+  BOOST_CHECK_EQUAL(status1, status2);
+}
 
-  std::ostringstream os;
-  os << status2;
-  BOOST_CHECK_EQUAL(os.str(), "FaceStatus(FaceID: 100,\n"
-                              "RemoteUri: tcp4://192.0.2.1:6363,\n"
-                              "LocalUri: tcp4://192.0.2.2:55555,\n"
-                              "ExpirationPeriod: 10000 milliseconds,\n"
-                              "FaceScope: local,\n"
-                              "FacePersistency: on-demand,\n"
-                              "LinkType: multi-access,\n"
-                              "Flags: 0x7,\n"
-                              "Counters: { Interests: {in: 10, out: 3000},\n"
-                              "            Data: {in: 200, out: 4},\n"
-                              "            Nack: {in: 1, out: 2},\n"
-                              "            bytes: {in: 1329719163, out: 999110448} }\n"
-                              ")");
+BOOST_AUTO_TEST_CASE(Equality)
+{
+  FaceStatus status1, status2;
+
+  status1 = makeFaceStatus();
+  status2 = status1;
+  BOOST_CHECK_EQUAL(status1, status2);
+
+  status2.setFaceId(42);
+  BOOST_CHECK_NE(status1, status2);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+  FaceStatus status;
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(status),
+                    "Face(FaceId: 0,\n"
+                    "     RemoteUri: ,\n"
+                    "     LocalUri: ,\n"
+                    "     ExpirationPeriod: infinite,\n"
+                    "     FaceScope: non-local,\n"
+                    "     FacePersistency: persistent,\n"
+                    "     LinkType: point-to-point,\n"
+                    "     Flags: 0x0,\n"
+                    "     Counters: {Interests: {in: 0, out: 0},\n"
+                    "                Data: {in: 0, out: 0},\n"
+                    "                Nacks: {in: 0, out: 0},\n"
+                    "                bytes: {in: 0, out: 0}}\n"
+                    "     )");
+
+  status = makeFaceStatus();
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(status),
+                    "Face(FaceId: 100,\n"
+                    "     RemoteUri: tcp4://192.0.2.1:6363,\n"
+                    "     LocalUri: tcp4://192.0.2.2:55555,\n"
+                    "     ExpirationPeriod: 10000 milliseconds,\n"
+                    "     FaceScope: local,\n"
+                    "     FacePersistency: on-demand,\n"
+                    "     LinkType: multi-access,\n"
+                    "     Flags: 0x7,\n"
+                    "     Counters: {Interests: {in: 10, out: 3000},\n"
+                    "                Data: {in: 200, out: 4},\n"
+                    "                Nacks: {in: 1, out: 2},\n"
+                    "                bytes: {in: 1329719163, out: 999110448}}\n"
+                    "     )");
 }
 
 BOOST_AUTO_TEST_CASE(FlagBit)