mgmt+tools: rework FacePersistency handling in faces/create and faces/update

1. enable changing persistency in faces/update command
2. reject faces/create request if the face already exists
3. nfdc changes to support the above

Change-Id: I659906da846608a42a768f08fb110ceee1a947a7
refs: #3232
diff --git a/tests/daemon/mgmt/face-manager-update-face.t.cpp b/tests/daemon/mgmt/face-manager-update-face.t.cpp
index df1f97f..6757622 100644
--- a/tests/daemon/mgmt/face-manager-update-face.t.cpp
+++ b/tests/daemon/mgmt/face-manager-update-face.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -23,10 +23,15 @@
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "mgmt/face-manager.hpp"
+#include "face/generic-link-service.hpp"
 #include "face-manager-command-fixture.hpp"
 #include "nfd-manager-common-fixture.hpp"
+
 #include <ndn-cxx/lp/tags.hpp>
 
+#include <thread>
+
 namespace nfd {
 namespace tests {
 
@@ -56,36 +61,54 @@
     params.setFacePersistency(persistency);
     params.setFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED, enableLocalFields);
 
+    createFace(params);
+  }
+
+  void
+  createFace(const ControlParameters& createParams,
+             bool isForOnDemandFace = false)
+  {
     Name commandName("/localhost/nfd/faces/create");
-    commandName.append(params.wireEncode());
+    commandName.append(createParams.wireEncode());
     auto command = makeInterest(commandName);
     m_keyChain.sign(*command);
 
+    // if this creation if for on-demand face then create it on node2
+    FaceManagerCommandNode& target = isForOnDemandFace ? this->node2 : this->node1;
+
     bool hasCallbackFired = false;
-    signal::ScopedConnection connection = this->node1.face.onSendData.connect(
-      [this, command, &hasCallbackFired] (const Data& response) {
+    signal::ScopedConnection connection = target.face.onSendData.connect(
+      [&, command, isForOnDemandFace, this] (const Data& response) {
         if (!command->getName().isPrefixOf(response.getName())) {
           return;
         }
 
         ControlResponse create(response.getContent().blockFromValue());
         BOOST_REQUIRE_EQUAL(create.getCode(), 200);
+        BOOST_REQUIRE(create.getBody().hasWire());
 
-        if (create.getBody().hasWire()) {
-          ControlParameters faceParams(create.getBody());
-          BOOST_REQUIRE(faceParams.hasFaceId());
-          this->faceId = faceParams.getFaceId();
-        }
-        else {
-          BOOST_FAIL("Face creation failed");
-        }
+        ControlParameters faceParams(create.getBody());
+        BOOST_REQUIRE(faceParams.hasFaceId());
+        this->faceId = faceParams.getFaceId();
 
         hasCallbackFired = true;
+
+        if (isForOnDemandFace) {
+          auto face = target.faceTable.get(static_cast<FaceId>(this->faceId));
+
+          // to force creation of on-demand face
+          face->sendInterest(*make_shared<Interest>("/hello/world"));
+        }
       });
 
-    this->node1.face.receive(*command);
+    target.face.receive(*command);
     this->advanceClocks(time::milliseconds(1), 5);
 
+    if (isForOnDemandFace) {
+      std::this_thread::sleep_for(std::chrono::milliseconds(100)); // allow wallclock time for socket IO
+      this->advanceClocks(time::milliseconds(1), 5); // let node1 accept Interest and create on-demand face
+    }
+
     BOOST_REQUIRE(hasCallbackFired);
   }
 
@@ -176,29 +199,63 @@
   });
 }
 
-// TODO #3232: Expected failure until FacePersistency updating implemented
-BOOST_AUTO_TEST_CASE(UpdatePersistency)
+template<bool CAN_CHANGE_PERSISTENCY>
+class UpdatePersistencyDummyTransport : public face::Transport
 {
-  createFace();
+public:
+  UpdatePersistencyDummyTransport()
+  {
+    this->setPersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+  }
 
-  ControlParameters requestParams;
-  requestParams.setFaceId(faceId);
-  requestParams.setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERMANENT);
+protected:
+  bool
+  canChangePersistencyToImpl(ndn::nfd::FacePersistency newPersistency) const override
+  {
+    return CAN_CHANGE_PERSISTENCY;
+  }
 
-  updateFace(requestParams, false, [] (const ControlResponse& actual) {
-    ControlResponse expected(409, "Invalid fields specified");
-    BOOST_CHECK_EQUAL(actual.getCode(), expected.getCode());
-    BOOST_TEST_MESSAGE(actual.getText());
+  void
+  doClose() override
+  {
+  }
 
-    if (actual.getBody().hasWire()) {
-      ControlParameters actualParams(actual.getBody());
+private:
+  void
+  doSend(face::Transport::Packet&& packet) override
+  {
+  }
+};
 
-      BOOST_REQUIRE(actualParams.hasFacePersistency());
-      BOOST_CHECK_EQUAL(actualParams.getFacePersistency(), ndn::nfd::FACE_PERSISTENCY_PERMANENT);
-    }
-    else {
-      BOOST_ERROR("Response does not contain ControlParameters");
-    }
+namespace mpl = boost::mpl;
+
+using UpdatePersistencyTests =
+  mpl::vector<mpl::pair<UpdatePersistencyDummyTransport<true>, CommandSuccess>,
+              mpl::pair<UpdatePersistencyDummyTransport<false>, CommandFailure<409>>>;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(UpdatePersistency, T, UpdatePersistencyTests, FaceManagerUpdateFixture)
+{
+  using TransportType = typename T::first;
+  using ResultType = typename T::second;
+
+  auto face = make_shared<face::Face>(make_unique<face::GenericLinkService>(),
+                                      make_unique<TransportType>());
+  this->node1.faceTable.add(face);
+
+  auto parameters = ControlParameters()
+    .setFaceId(face->getId())
+    .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERMANENT);
+
+  updateFace(parameters, false, [] (const ControlResponse& actual) {
+      BOOST_TEST_MESSAGE(actual.getText());
+      BOOST_CHECK_EQUAL(actual.getCode(), ResultType::getExpected().getCode());
+
+      // the response for either 200 or 409 will have a content body
+      BOOST_REQUIRE(actual.getBody().hasWire());
+
+      ControlParameters resp;
+      resp.wireDecode(actual.getBody());
+      BOOST_CHECK_EQUAL(resp.getFacePersistency(), ndn::nfd::FACE_PERSISTENCY_PERMANENT);
   });
 }
 
@@ -413,8 +470,6 @@
   }
 };
 
-namespace mpl = boost::mpl;
-
 typedef mpl::vector<mpl::pair<TcpLocalFieldsEnable, CommandSuccess>,
                     mpl::pair<TcpLocalFieldsDisable, CommandSuccess>,
                     mpl::pair<UdpLocalFieldsEnable, CommandFailure<409>>,