mgmt: tables.cs_policy config option

refs #3148

Change-Id: Icf30bbe508c1c491949d9fcb433fdfa16842d312
diff --git a/daemon/mgmt/tables-config-section.cpp b/daemon/mgmt/tables-config-section.cpp
index 397f26c..7d355d0 100644
--- a/daemon/mgmt/tables-config-section.cpp
+++ b/daemon/mgmt/tables-config-section.cpp
@@ -50,6 +50,7 @@
   }
 
   m_forwarder.getCs().setLimit(DEFAULT_CS_MAX_PACKETS);
+  // Don't set default cs_policy because it's already created by CS itself.
   m_forwarder.setUnsolicitedDataPolicy(make_unique<fw::DefaultUnsolicitedDataPolicy>());
 
   m_isConfigured = true;
@@ -66,14 +67,25 @@
     nCsMaxPackets = ConfigFile::parseNumber<size_t>(*csMaxPacketsNode, "cs_max_packets", "tables");
   }
 
+  unique_ptr<cs::Policy> csPolicy;
+  OptionalNode csPolicyNode = section.get_child_optional("cs_policy");
+  if (csPolicyNode) {
+    std::string policyName = csPolicyNode->get_value<std::string>();
+    csPolicy = cs::Policy::create(policyName);
+    if (csPolicy == nullptr) {
+      BOOST_THROW_EXCEPTION(ConfigFile::Error(
+        "Unknown cs_policy \"" + policyName + "\" in \"tables\" section"));
+    }
+  }
+
   unique_ptr<fw::UnsolicitedDataPolicy> unsolicitedDataPolicy;
   OptionalNode unsolicitedDataPolicyNode = section.get_child_optional("cs_unsolicited_policy");
   if (unsolicitedDataPolicyNode) {
-    std::string policyKey = unsolicitedDataPolicyNode->get_value<std::string>();
-    unsolicitedDataPolicy = fw::UnsolicitedDataPolicy::create(policyKey);
+    std::string policyName = unsolicitedDataPolicyNode->get_value<std::string>();
+    unsolicitedDataPolicy = fw::UnsolicitedDataPolicy::create(policyName);
     if (unsolicitedDataPolicy == nullptr) {
       BOOST_THROW_EXCEPTION(ConfigFile::Error(
-        "Unknown cs_unsolicited_policy \"" + policyKey + "\" in \"tables\" section"));
+        "Unknown cs_unsolicited_policy \"" + policyName + "\" in \"tables\" section"));
     }
   }
   else {
@@ -94,7 +106,11 @@
     return;
   }
 
-  m_forwarder.getCs().setLimit(nCsMaxPackets);
+  Cs& cs = m_forwarder.getCs();
+  cs.setLimit(nCsMaxPackets);
+  if (cs.size() == 0 && csPolicy != nullptr) {
+    cs.setPolicy(std::move(csPolicy));
+  }
 
   m_forwarder.setUnsolicitedDataPolicy(std::move(unsolicitedDataPolicy));
 
diff --git a/daemon/mgmt/tables-config-section.hpp b/daemon/mgmt/tables-config-section.hpp
index 8afa608..d1ac1d4 100644
--- a/daemon/mgmt/tables-config-section.hpp
+++ b/daemon/mgmt/tables-config-section.hpp
@@ -38,7 +38,7 @@
  *  tables
  *  {
  *    cs_max_packets 65536
- *
+ *    cs_policy priority_fifo
  *    cs_unsolicited_policy drop-all
  *
  *    strategy_choice
@@ -58,7 +58,7 @@
  *  \endcode
  *
  *  During a configuration reload,
- *  \li cs_max_packets and cs_unsolicited_policy are applied;
+ *  \li cs_max_packets, cs_policy, and cs_unsolicited_policy are applied;
  *      defaults are used if an option is omitted.
  *  \li strategy_choice entries are inserted, but old entries are not deleted.
  *  \li network_region is applied; it's kept unchanged if the section is omitted.
diff --git a/daemon/table/cs.cpp b/daemon/table/cs.cpp
index 0dd5ca2..dd72214 100644
--- a/daemon/table/cs.cpp
+++ b/daemon/table/cs.cpp
@@ -51,9 +51,9 @@
   return Policy::create(DEFAULT_POLICY);
 }
 
-Cs::Cs(size_t nMaxPackets, unique_ptr<Policy> policy)
+Cs::Cs(size_t nMaxPackets)
 {
-  this->setPolicyImpl(std::move(policy));
+  this->setPolicyImpl(makeDefaultPolicy());
   m_policy->setLimit(nMaxPackets);
 }
 
diff --git a/daemon/table/cs.hpp b/daemon/table/cs.hpp
index 8c3e46b..03d6fdd 100644
--- a/daemon/table/cs.hpp
+++ b/daemon/table/cs.hpp
@@ -57,16 +57,13 @@
 namespace nfd {
 namespace cs {
 
-unique_ptr<Policy>
-makeDefaultPolicy();
-
 /** \brief represents the ContentStore
  */
 class Cs : noncopyable
 {
 public:
   explicit
-  Cs(size_t nMaxPackets = 10, unique_ptr<Policy> policy = makeDefaultPolicy());
+  Cs(size_t nMaxPackets = 10);
 
   /** \brief inserts a Data packet
    */
diff --git a/nfd.conf.sample.in b/nfd.conf.sample.in
index 8b0e30b..58ef855 100644
--- a/nfd.conf.sample.in
+++ b/nfd.conf.sample.in
@@ -49,6 +49,10 @@
   ; default is 65536, about 500MB with 8KB packet size
   cs_max_packets 65536
 
+  ; Set the CS replacement policy.
+  ; Available policies are: priority_fifo, lru
+  cs_policy priority_fifo
+
   ; Set a policy to decide whether to cache or drop unsolicited Data.
   ; Available policies are: drop-all, admit-local, admit-network, admit-all
   cs_unsolicited_policy drop-all
diff --git a/tests/daemon/mgmt/tables-config-section.t.cpp b/tests/daemon/mgmt/tables-config-section.t.cpp
index 139d61c..25c621c 100644
--- a/tests/daemon/mgmt/tables-config-section.t.cpp
+++ b/tests/daemon/mgmt/tables-config-section.t.cpp
@@ -25,6 +25,8 @@
 
 #include "mgmt/tables-config-section.hpp"
 #include "fw/forwarder.hpp"
+#include "table/cs-policy-lru.hpp"
+#include "table/cs-policy-priority-fifo.hpp"
 
 #include "tests/test-common.hpp"
 #include "tests/check-typeid.hpp"
@@ -141,6 +143,54 @@
 
 BOOST_AUTO_TEST_SUITE_END() // CsMaxPackets
 
+BOOST_AUTO_TEST_SUITE(CsPolicy)
+
+BOOST_AUTO_TEST_CASE(Default)
+{
+  const std::string CONFIG = R"CONFIG(
+    tables
+    {
+    }
+  )CONFIG";
+
+  BOOST_REQUIRE_NO_THROW(runConfig(CONFIG, false));
+  cs::Policy* currentPolicy = cs.getPolicy();
+  NFD_CHECK_TYPEID_EQUAL(*currentPolicy, cs::PriorityFifoPolicy);
+}
+
+BOOST_AUTO_TEST_CASE(Known)
+{
+  const std::string CONFIG = R"CONFIG(
+    tables
+    {
+      cs_policy lru
+    }
+  )CONFIG";
+
+  BOOST_REQUIRE_NO_THROW(runConfig(CONFIG, true));
+  cs::Policy* currentPolicy = cs.getPolicy();
+  NFD_CHECK_TYPEID_EQUAL(*currentPolicy, cs::PriorityFifoPolicy);
+
+  BOOST_REQUIRE_NO_THROW(runConfig(CONFIG, false));
+  currentPolicy = cs.getPolicy();
+  NFD_CHECK_TYPEID_EQUAL(*currentPolicy, cs::LruPolicy);
+}
+
+BOOST_AUTO_TEST_CASE(Unknown)
+{
+  const std::string CONFIG = R"CONFIG(
+    tables
+    {
+      cs_policy unknown
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(runConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(runConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // CsPolicy
+
 class CsUnsolicitedPolicyFixture : public TablesConfigSectionFixture
 {
 protected: