face: Allow creation of Face with custom IO service and default transport

Change-Id: I3f3a77c980d23f5ff53d21d4669582caff259a63
Refs: #2500
diff --git a/src/face.cpp b/src/face.cpp
index a4f3615..9f64aa6 100644
--- a/src/face.cpp
+++ b/src/face.cpp
@@ -37,7 +37,7 @@
   , m_internalKeyChain(new KeyChain())
   , m_impl(new Impl(*this))
 {
-  construct(*m_internalKeyChain);
+  construct(nullptr, *m_internalKeyChain);
 }
 
 Face::Face(boost::asio::io_service& ioService)
@@ -45,7 +45,7 @@
   , m_internalKeyChain(new KeyChain())
   , m_impl(new Impl(*this))
 {
-  construct(*m_internalKeyChain);
+  construct(nullptr, *m_internalKeyChain);
 }
 
 Face::Face(const std::string& host, const std::string& port/* = "6363"*/)
@@ -57,7 +57,7 @@
   construct(make_shared<TcpTransport>(host, port), *m_internalKeyChain);
 }
 
-Face::Face(const shared_ptr<Transport>& transport)
+Face::Face(shared_ptr<Transport> transport)
   : m_internalIoService(new boost::asio::io_service())
   , m_ioService(*m_internalIoService)
   , m_internalKeyChain(new KeyChain())
@@ -66,7 +66,7 @@
   construct(transport, *m_internalKeyChain);
 }
 
-Face::Face(const shared_ptr<Transport>& transport,
+Face::Face(shared_ptr<Transport> transport,
            boost::asio::io_service& ioService)
   : m_ioService(ioService)
   , m_internalKeyChain(new KeyChain())
@@ -85,36 +85,34 @@
   construct(transport, keyChain);
 }
 
-void
-Face::construct(KeyChain& keyChain)
+shared_ptr<Transport>
+Face::makeDefaultTransport()
 {
   // transport=unix:///var/run/nfd.sock
   // transport=tcp://localhost:6363
 
   ConfigFile config;
-  const auto& transportType = config.getParsedConfiguration()
-                                .get_optional<std::string>("transport");
-  if (!transportType) {
+  const auto& transportUri = config.getParsedConfiguration()
+                               .get_optional<std::string>("transport");
+  if (!transportUri) {
     // transport not specified, use default Unix transport.
-    construct(UnixTransport::create(config), keyChain);
-    return;
+    return UnixTransport::create(config);
   }
 
-  unique_ptr<util::FaceUri> uri;
+  std::string protocol;
   try {
-    uri.reset(new util::FaceUri(*transportType));
+    util::FaceUri uri(*transportUri);
+    protocol = uri.getScheme();
   }
   catch (const util::FaceUri::Error& error) {
     BOOST_THROW_EXCEPTION(ConfigFile::Error(error.what()));
   }
 
-  const std::string protocol = uri->getScheme();
-
   if (protocol == "unix") {
-    construct(UnixTransport::create(config), keyChain);
+    return UnixTransport::create(config);
   }
   else if (protocol == "tcp" || protocol == "tcp4" || protocol == "tcp6") {
-    construct(TcpTransport::create(config), keyChain);
+    return TcpTransport::create(config);
   }
   else {
     BOOST_THROW_EXCEPTION(ConfigFile::Error("Unsupported transport protocol \"" + protocol + "\""));
@@ -124,10 +122,14 @@
 void
 Face::construct(shared_ptr<Transport> transport, KeyChain& keyChain)
 {
-  m_nfdController.reset(new nfd::Controller(*this, keyChain));
-
+  if (transport == nullptr) {
+    transport = makeDefaultTransport();
+  }
+  BOOST_ASSERT(transport != nullptr);
   m_transport = transport;
 
+  m_nfdController.reset(new nfd::Controller(*this, keyChain));
+
   m_ioService.post([=] { m_impl->ensureConnected(false); });
 }
 
diff --git a/src/face.hpp b/src/face.hpp
index 9e6faa4..1fa2aca 100644
--- a/src/face.hpp
+++ b/src/face.hpp
@@ -179,27 +179,31 @@
 
   /**
    * @brief Create a new Face using the given Transport
-   *
-   * @param transport A shared_ptr to a Transport object used for communication
+   * @param transport the Transport used for communication. If nullptr, then the default
+   *                  transport will be used.
    *
    * @throws Face::Error on unsupported protocol
    */
   explicit
-  Face(const shared_ptr<Transport>& transport);
+  Face(shared_ptr<Transport> transport);
 
   /**
    * @brief Create a new Face using the given Transport and IO service object
+   * @param transport the Transport used for communication. If nullptr, then the default
+   *                  transport will be used.
+   * @param ioService the io_service that controls all IO operations
    *
    * @sa Face(boost::asio::io_service&)
    *
    * @throws Face::Error on unsupported protocol
    */
-  Face(const shared_ptr<Transport>& transport,
+  Face(shared_ptr<Transport> transport,
        boost::asio::io_service& ioService);
 
   /**
    * @brief Create a new Face using the given Transport and IO service object
-   * @param transport the Transport used for communication
+   * @param transport the Transport used for communication. If nullptr, then the default
+   *                  transport will be used.
    * @param ioService the io_service that controls all IO operations
    * @param keyChain the KeyChain to sign commands
    * @throws Face::Error on unsupported protocol
@@ -684,8 +688,8 @@
   /**
    * @throws ConfigFile::Error on parse error and unsupported protocols
    */
-  void
-  construct(KeyChain& keyChain);
+  shared_ptr<Transport>
+  makeDefaultTransport();
 
   /**
    * @throws Face::Error on unsupported protocol
diff --git a/tests/unit-tests/face.t.cpp b/tests/unit-tests/face.t.cpp
index 928a050..5c8dda0 100644
--- a/tests/unit-tests/face.t.cpp
+++ b/tests/unit-tests/face.t.cpp
@@ -602,9 +602,9 @@
 
   BOOST_CHECK(Face().getTransport() != nullptr);
 
-  BOOST_CHECK(Face(shared_ptr<Transport>()).getTransport() == nullptr);
-  BOOST_CHECK(Face(shared_ptr<Transport>(), io).getTransport() == nullptr);
-  BOOST_CHECK(Face(shared_ptr<Transport>(), io, keyChain).getTransport() == nullptr);
+  BOOST_CHECK(Face(shared_ptr<Transport>()).getTransport() != nullptr);
+  BOOST_CHECK(Face(shared_ptr<Transport>(), io).getTransport() != nullptr);
+  BOOST_CHECK(Face(shared_ptr<Transport>(), io, keyChain).getTransport() != nullptr);
 
   auto transport = make_shared<TcpTransport>("localhost", "6363"); // no real io operations will be scheduled
   BOOST_CHECK(Face(transport).getTransport() == transport);