]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
perfdhcp avalanche: improvements after review
authorMichal Nowikowski <godfryd@isc.org>
Mon, 18 Feb 2019 12:30:40 +0000 (13:30 +0100)
committerMichal Nowikowski <godfryd@isc.org>
Tue, 19 Feb 2019 20:54:31 +0000 (21:54 +0100)
- CommandOptions is no longer a signleton - this makes testing easier
- added -i option taking into account in Avalanche scen (execute only DO exchange)
- fixed collecting stats in Avalanche scen
- improved TestControl tests, moved some of them to BasicScen tests
- made PerfSocket testable: it has a base class which is used for mocking in TestControl tests
- all references to another singleton, IfaceMgr wrapped into PerfSocket - this makes testing easier
- added unit tests for basic and avalanche scenarios, and perf socket
- added -Werror to prevent ignore warnings
- added more comments

27 files changed:
src/bin/perfdhcp/Makefile.am
src/bin/perfdhcp/abstract_scen.h
src/bin/perfdhcp/avalanche_scen.cc
src/bin/perfdhcp/avalanche_scen.h
src/bin/perfdhcp/basic_scen.cc
src/bin/perfdhcp/basic_scen.h
src/bin/perfdhcp/command_options.cc
src/bin/perfdhcp/command_options.h
src/bin/perfdhcp/main.cc
src/bin/perfdhcp/perf_socket.cc
src/bin/perfdhcp/perf_socket.h
src/bin/perfdhcp/receiver.cc
src/bin/perfdhcp/receiver.h
src/bin/perfdhcp/stats_mgr.cc
src/bin/perfdhcp/stats_mgr.h
src/bin/perfdhcp/test_control.cc
src/bin/perfdhcp/test_control.h
src/bin/perfdhcp/tests/Makefile.am
src/bin/perfdhcp/tests/avalanche_scen_unittest.cc [new file with mode: 0644]
src/bin/perfdhcp/tests/basic_scen_unittest.cc [new file with mode: 0644]
src/bin/perfdhcp/tests/command_options_helper.h
src/bin/perfdhcp/tests/command_options_unittest.cc
src/bin/perfdhcp/tests/perf_socket_unittest.cc [new file with mode: 0644]
src/bin/perfdhcp/tests/receiver_unittest.cc
src/bin/perfdhcp/tests/stats_mgr_unittest.cc
src/bin/perfdhcp/tests/test_control_unittest.cc
src/lib/dhcp/pkt.h

index d595588245f74317941605a75f4216ed07371a66..973ff3e581df4aa759bd4349566a0d685ef107f9 100644 (file)
@@ -3,6 +3,7 @@ SUBDIRS = . tests
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
 AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -Werror
 
 AM_CXXFLAGS = $(KEA_CXXFLAGS)
 
index bb2f5c892f762d5af17deb9b413915c56cd4f3db..d57278cf421f8aebd783508a77872ae53bf29ee6 100644 (file)
@@ -20,15 +20,26 @@ namespace perfdhcp {
 /// This class must be inherited by scenario classes.
 class AbstractScen : public boost::noncopyable {
 public:
+    /// \brief Default and the only constructor of AbstractScen.
+    ///
+    /// \param options reference to command options,
+    /// \param socket reference to a socket.
+    AbstractScen(CommandOptions& options, BasePerfSocket &socket) :
+        options_(options),
+        tc_(options, socket) {};
+
     /// \brief Run performance test.
     ///
     /// Method runs whole performance test.
+    ///
+    /// \return execution status.
     virtual int run() = 0;
 
     /// \brief Trivial virtual destructor.
     virtual ~AbstractScen() {};
 
 protected:
+    CommandOptions& options_;
     TestControl tc_;  ///< Object for controling sending and receiving packets.
 };
 
index 7145e228e3945aa7ef517bad962cc2bce3f629bb..da85ef86ed23640aa330e563268141deca3f67f8 100644 (file)
@@ -20,7 +20,6 @@ namespace perfdhcp {
 
 int
 AvalancheScen::resendPackets(ExchangeType xchg_type) {
-    CommandOptions& options = CommandOptions::instance();
     const StatsMgr& stats_mgr(tc_.getStatsMgr());
 
     // get list of sent packets that potentially need to be resent
@@ -65,12 +64,12 @@ AvalancheScen::resendPackets(ExchangeType xchg_type) {
             total_resent_++;
 
             // do resend packet
-            if (options.getIpVersion() == 4) {
+            if (options_.getIpVersion() == 4) {
                 Pkt4Ptr pkt4 = boost::dynamic_pointer_cast<Pkt4>(pkt);
-                IfaceMgr::instance().send(pkt4);
+                socket_.send(pkt4);
             } else {
                 Pkt6Ptr pkt6 = boost::dynamic_pointer_cast<Pkt6>(pkt);
-                IfaceMgr::instance().send(pkt6);
+                socket_.send(pkt6);
             }
 
             // restore sending time of original packet
@@ -104,10 +103,8 @@ AvalancheScen::run() {
     // and printed during runtime. The whole procedure is stopeed when
     // all packets got reponses.
 
-    CommandOptions& options = CommandOptions::instance();
-
-    uint32_t clients_num = options.getClientsNum() == 0 ?
-        1 : options.getClientsNum();
+    uint32_t clients_num = options_.getClientsNum() == 0 ?
+        1 : options_.getClientsNum();
 
     StatsMgr& stats_mgr(tc_.getStatsMgr());
 
@@ -131,12 +128,16 @@ AvalancheScen::run() {
         if (now - prev_cycle_time > milliseconds(200)) { // check if 0.2s elapsed
             prev_cycle_time = now;
             int still_left_cnt = 0;
-            if (options.getIpVersion() == 4) {
+            if (options_.getIpVersion() == 4) {
                 still_left_cnt += resendPackets(ExchangeType::DO);
-                still_left_cnt += resendPackets(ExchangeType::RA);
+                if (options_.getExchangeMode() == CommandOptions::DORA_SARR) {
+                    still_left_cnt += resendPackets(ExchangeType::RA);
+                }
             } else {
                 still_left_cnt += resendPackets(ExchangeType::SA);
-                still_left_cnt += resendPackets(ExchangeType::RR);
+                if (options_.getExchangeMode() == CommandOptions::DORA_SARR) {
+                    still_left_cnt += resendPackets(ExchangeType::RR);
+                }
             }
 
             if (still_left_cnt == 0) {
@@ -157,25 +158,44 @@ AvalancheScen::run() {
     tc_.printStats();
 
     // Print packet timestamps
-    if (testDiags('t')) {
+    if (options_.testDiags('t')) {
         stats_mgr.printTimestamps();
     }
 
     // Print server id.
-    if (testDiags('s') && tc_.serverIdReceived()) {
+    if (options_.testDiags('s') && tc_.serverIdReceived()) {
         std::cout << "Server id: " << tc_.getServerId() << std::endl;
     }
 
     // Diagnostics flag 'e' means show exit reason.
-    if (testDiags('e')) {
+    if (options_.testDiags('e')) {
         std::cout << "Interrupted" << std::endl;
     }
 
+    // Calculate total stats.
+    int total_sent_pkts = total_resent_;
+    int total_rcvd_pkts = 0;
+    if (options_.getIpVersion() == 4) {
+        total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO);
+        total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO);
+        if (options_.getExchangeMode() == CommandOptions::DORA_SARR) {
+            total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA);
+            total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA);
+        }
+    } else {
+        total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA);
+        total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA);
+        if (options_.getExchangeMode() == CommandOptions::DORA_SARR) {
+            total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR);
+            total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR);
+        }
+    }
+
     std::cout << "It took " << duration.length() << " to provision " << clients_num
-              << " clients. " << (clients_num * 2 + total_resent_)
-              << " packets were sent, " << total_resent_
-              << " retransmissions needed, received " << (clients_num * 2)
-              << " responses." << std::endl;
+              << " clients. " << std::endl
+              << "Requests sent + resent: " << total_sent_pkts << std::endl
+              << "Requests resent: " << total_resent_ << std::endl
+              << "Responses received: " << total_rcvd_pkts << std::endl;
 
     return (0);
 }
index d973ff81a7c08562a50b2e1e04be4a301a01a7cb..c93db852b9064815f720a9b66f73910019268a1c 100644 (file)
@@ -24,16 +24,27 @@ namespace perfdhcp {
 /// Full DORA and SARR message sequences are expected.
 class AvalancheScen : public AbstractScen {
 public:
-    /// Default and the only constructor of AvalancheScen.
-    AvalancheScen(): total_resent_(0) {};
+    /// \brief Default and the only constructor of AvalancheScen.
+    ///
+    /// \param options reference to command options,
+    /// \param socket reference to a socket.
+    AvalancheScen(CommandOptions& options, BasePerfSocket &socket):
+        AbstractScen(options, socket),
+        socket_(socket),
+        total_resent_(0) {};
 
     /// brief\ Run performance test.
     ///
     /// Method runs whole performance test.
-    int run();
+    ///
+    /// \return execution status.
+    int run() override;
 
 private:
 
+    // A reference to socket;
+    BasePerfSocket &socket_;
+
     /// A map xchg type -> (a map of trans id -> retransmissions count.
     std::unordered_map<ExchangeType, std::unordered_map<uint32_t, int>> retransmissions_;
     /// A map xchg type -> (a map of trans id -> time of sending first packet.
@@ -42,8 +53,13 @@ private:
     /// Total number of resent packets.
     int total_resent_;
 
-    /// Resend packets for given exchange type that did not receive
+    /// \\brief Resend packets.
+    ///
+    /// It resends packets for given exchange type that did not receive
     /// a response yet.
+    ///
+    /// \param xchg_type exchange type that should be looked for.
+    /// \return number of packets still waiting for resending.
     int resendPackets(ExchangeType xchg_type);
 
 };
index cda35f644ceb8a107bfbbf32c96c0b7bece90fb2..b4236244c7615b28d0c5cb715e1704a466ad2402 100644 (file)
@@ -27,17 +27,16 @@ BasicScen::checkExitConditions() {
 
     const StatsMgr& stats_mgr(tc_.getStatsMgr());
 
-    CommandOptions& options = CommandOptions::instance();
     bool test_period_reached = false;
     // Check if test period passed.
-    if (options.getPeriod() != 0) {
+    if (options_.getPeriod() != 0) {
         time_period period(stats_mgr.getTestPeriod());
-        if (period.length().total_seconds() >= options.getPeriod()) {
+        if (period.length().total_seconds() >= options_.getPeriod()) {
             test_period_reached = true;
         }
     }
     if (test_period_reached) {
-        if (testDiags('e')) {
+        if (options_.testDiags('e')) {
             std::cout << "reached test-period." << std::endl;
         }
         if (!tc_.waitToExit()) {
@@ -47,35 +46,35 @@ BasicScen::checkExitConditions() {
 
     bool max_requests = false;
     // Check if we reached maximum number of DISCOVER/SOLICIT sent.
-    if (options.getNumRequests().size() > 0) {
-        if (options.getIpVersion() == 4) {
+    if (options_.getNumRequests().size() > 0) {
+        if (options_.getIpVersion() == 4) {
             if (stats_mgr.getSentPacketsNum(ExchangeType::DO) >=
-                options.getNumRequests()[0]) {
+                options_.getNumRequests()[0]) {
                 max_requests = true;
             }
-        } else if (options.getIpVersion() == 6) {
+        } else if (options_.getIpVersion() == 6) {
             if (stats_mgr.getSentPacketsNum(ExchangeType::SA) >=
-                options.getNumRequests()[0]) {
+                options_.getNumRequests()[0]) {
                 max_requests = true;
             }
         }
     }
     // Check if we reached maximum number REQUEST packets.
-    if (options.getNumRequests().size() > 1) {
-        if (options.getIpVersion() == 4) {
+    if (options_.getNumRequests().size() > 1) {
+        if (options_.getIpVersion() == 4) {
             if (stats_mgr.getSentPacketsNum(ExchangeType::RA) >=
-                options.getNumRequests()[1]) {
+                options_.getNumRequests()[1]) {
                 max_requests = true;
             }
-        } else if (options.getIpVersion() == 6) {
+        } else if (options_.getIpVersion() == 6) {
             if (stats_mgr.getSentPacketsNum(ExchangeType::RR) >=
-                options.getNumRequests()[1]) {
+                options_.getNumRequests()[1]) {
                 max_requests = true;
             }
         }
     }
     if (max_requests) {
-        if (testDiags('e')) {
+        if (options_.testDiags('e')) {
             std::cout << "Reached max requests limit." << std::endl;
         }
         if (!tc_.waitToExit()) {
@@ -85,35 +84,35 @@ BasicScen::checkExitConditions() {
 
     // Check if we reached maximum number of drops of OFFER/ADVERTISE packets.
     bool max_drops = false;
-    if (options.getMaxDrop().size() > 0) {
-        if (options.getIpVersion() == 4) {
+    if (options_.getMaxDrop().size() > 0) {
+        if (options_.getIpVersion() == 4) {
             if (stats_mgr.getDroppedPacketsNum(ExchangeType::DO) >=
-                options.getMaxDrop()[0]) {
+                options_.getMaxDrop()[0]) {
                 max_drops = true;
             }
-        } else if (options.getIpVersion() == 6) {
+        } else if (options_.getIpVersion() == 6) {
             if (stats_mgr.getDroppedPacketsNum(ExchangeType::SA) >=
-                options.getMaxDrop()[0]) {
+                options_.getMaxDrop()[0]) {
                 max_drops = true;
             }
         }
     }
     // Check if we reached maximum number of drops of ACK/REPLY packets.
-    if (options.getMaxDrop().size() > 1) {
-        if (options.getIpVersion() == 4) {
+    if (options_.getMaxDrop().size() > 1) {
+        if (options_.getIpVersion() == 4) {
             if (stats_mgr.getDroppedPacketsNum(ExchangeType::RA) >=
-                options.getMaxDrop()[1]) {
+                options_.getMaxDrop()[1]) {
                 max_drops = true;
             }
-        } else if (options.getIpVersion() == 6) {
+        } else if (options_.getIpVersion() == 6) {
             if (stats_mgr.getDroppedPacketsNum(ExchangeType::RR) >=
-                options.getMaxDrop()[1]) {
+                options_.getMaxDrop()[1]) {
                 max_drops = true;
             }
         }
     }
     if (max_drops) {
-        if (testDiags('e')) {
+        if (options_.testDiags('e')) {
             std::cout << "Reached maximum drops number." << std::endl;
         }
         if (!tc_.waitToExit()) {
@@ -123,44 +122,44 @@ BasicScen::checkExitConditions() {
 
     // Check if we reached maximum drops percentage of OFFER/ADVERTISE packets.
     bool max_pdrops = false;
-    if (options.getMaxDropPercentage().size() > 0) {
-        if (options.getIpVersion() == 4) {
+    if (options_.getMaxDropPercentage().size() > 0) {
+        if (options_.getIpVersion() == 4) {
             if ((stats_mgr.getSentPacketsNum(ExchangeType::DO) > 10) &&
                 ((100. * stats_mgr.getDroppedPacketsNum(ExchangeType::DO) /
                  stats_mgr.getSentPacketsNum(ExchangeType::DO)) >=
-                 options.getMaxDropPercentage()[0])) {
+                 options_.getMaxDropPercentage()[0])) {
                 max_pdrops = true;
 
             }
-        } else if (options.getIpVersion() == 6) {
+        } else if (options_.getIpVersion() == 6) {
             if ((stats_mgr.getSentPacketsNum(ExchangeType::SA) > 10) &&
                 ((100. * stats_mgr.getDroppedPacketsNum(ExchangeType::SA) /
                   stats_mgr.getSentPacketsNum(ExchangeType::SA)) >=
-                 options.getMaxDropPercentage()[0])) {
+                 options_.getMaxDropPercentage()[0])) {
                 max_pdrops = true;
             }
         }
     }
     // Check if we reached maximum drops percentage of ACK/REPLY packets.
-    if (options.getMaxDropPercentage().size() > 1) {
-        if (options.getIpVersion() == 4) {
+    if (options_.getMaxDropPercentage().size() > 1) {
+        if (options_.getIpVersion() == 4) {
             if ((stats_mgr.getSentPacketsNum(ExchangeType::RA) > 10) &&
                 ((100. * stats_mgr.getDroppedPacketsNum(ExchangeType::RA) /
                  stats_mgr.getSentPacketsNum(ExchangeType::RA)) >=
-                 options.getMaxDropPercentage()[1])) {
+                 options_.getMaxDropPercentage()[1])) {
                 max_pdrops = true;
             }
-        } else if (options.getIpVersion() == 6) {
+        } else if (options_.getIpVersion() == 6) {
             if ((stats_mgr.getSentPacketsNum(ExchangeType::RR) > 10) &&
                 ((100. * stats_mgr.getDroppedPacketsNum(ExchangeType::RR) /
                   stats_mgr.getSentPacketsNum(ExchangeType::RR)) >=
-                 options.getMaxDropPercentage()[1])) {
+                 options_.getMaxDropPercentage()[1])) {
                 max_pdrops = true;
             }
         }
     }
     if (max_pdrops) {
-        if (testDiags('e')) {
+        if (options_.testDiags('e')) {
             std::cout << "Reached maximum percentage of drops." << std::endl;
         }
         if (!tc_.waitToExit()) {
@@ -172,21 +171,15 @@ BasicScen::checkExitConditions() {
 
 int
 BasicScen::run() {
-    CommandOptions& options = CommandOptions::instance();
-
-    basic_rate_control_.setRate(options.getRate());
-    renew_rate_control_.setRate(options.getRenewRate());
-    release_rate_control_.setRate(options.getReleaseRate());
-
     StatsMgr& stats_mgr(tc_.getStatsMgr());
 
     // Preload server with the number of packets.
-    if (options.getPreload() > 0) {
-        tc_.sendPackets(options.getPreload(), true);
+    if (options_.getPreload() > 0) {
+        tc_.sendPackets(options_.getPreload(), true);
     }
 
     // Fork and run command specified with -w<wrapped-command>
-    if (!options.getWrapped().empty()) {
+    if (!options_.getWrapped().empty()) {
         tc_.runWrapped();
     }
 
@@ -196,7 +189,7 @@ BasicScen::run() {
         // Calculate number of packets to be sent to stay
         // catch up with rate.
         uint64_t packets_due = basic_rate_control_.getOutboundMessageCount();
-        if ((packets_due == 0) && testDiags('i')) {
+        if ((packets_due == 0) && options_.testDiags('i')) {
             stats_mgr.incrementCounter("shortwait");
         }
 
@@ -207,7 +200,7 @@ BasicScen::run() {
         // If there is nothing to do in this loop iteration then do some sleep to make
         // CPU idle for a moment, to not consume 100% CPU all the time
         // but only if it is not that high request rate expected.
-        if (options.getRate() < 10000 && packets_due == 0 && pkt_count == 0) {
+        if (options_.getRate() < 10000 && packets_due == 0 && pkt_count == 0) {
             /// @todo: need to implement adaptive time here, so the sleep time
             /// is not fixed, but adjusts to current situation.
             usleep(1);
@@ -225,12 +218,12 @@ BasicScen::run() {
 
         // If -f<renew-rate> option was specified we have to check how many
         // Renew packets should be sent to catch up with a desired rate.
-        if (options.getRenewRate() != 0) {
+        if (options_.getRenewRate() != 0) {
             uint64_t renew_packets_due =
                 renew_rate_control_.getOutboundMessageCount();
 
             // Send multiple renews to satisfy the desired rate.
-            if (options.getIpVersion() == 4) {
+            if (options_.getIpVersion() == 4) {
                 tc_.sendMultipleRequests(renew_packets_due);
             } else {
                 tc_.sendMultipleMessages6(DHCPV6_RENEW, renew_packets_due);
@@ -239,7 +232,7 @@ BasicScen::run() {
 
         // If -F<release-rate> option was specified we have to check how many
         // Release messages should be sent to catch up with a desired rate.
-        if ((options.getIpVersion() == 6) && (options.getReleaseRate() != 0)) {
+        if ((options_.getIpVersion() == 6) && (options_.getReleaseRate() != 0)) {
             uint64_t release_packets_due =
                 release_rate_control_.getOutboundMessageCount();
             // Send Release messages.
@@ -248,7 +241,7 @@ BasicScen::run() {
 
         // Report delay means that user requested printing number
         // of sent/received/dropped packets repeatedly.
-        if (options.getReportDelay() > 0) {
+        if (options_.getReportDelay() > 0) {
             tc_.printIntermediateStats();
         }
 
@@ -265,28 +258,28 @@ BasicScen::run() {
 
     tc_.printStats();
 
-    if (!options.getWrapped().empty()) {
+    if (!options_.getWrapped().empty()) {
         // true means that we execute wrapped command with 'stop' argument.
         tc_.runWrapped(true);
     }
 
     // Print packet timestamps
-    if (testDiags('t')) {
+    if (options_.testDiags('t')) {
         stats_mgr.printTimestamps();
     }
 
     // Print server id.
-    if (testDiags('s') && tc_.serverIdReceived()) {
+    if (options_.testDiags('s') && tc_.serverIdReceived()) {
         std::cout << "Server id: " << tc_.getServerId() << std::endl;
     }
 
     // Diagnostics flag 'e' means show exit reason.
-    if (testDiags('e')) {
+    if (options_.testDiags('e')) {
         std::cout << "Interrupted" << std::endl;
     }
     // Print packet templates. Even if -T options have not been specified the
     // dynamically build packet will be printed if at least one has been sent.
-    if (testDiags('T')) {
+    if (options_.testDiags('T')) {
         tc_.printTemplates();
     }
 
index 31df72eb229c5c121fec82a0006c8b8c8f4119ec..b88986d7dfb63fccb62612584fe5377c59ac16ed 100644 (file)
@@ -22,8 +22,17 @@ namespace perfdhcp {
 /// is continuously loaded with DHCP messages according to given rate.
 class BasicScen : public AbstractScen {
 public:
-    /// Default and the only constructor of BasicScen.
-    BasicScen() {};
+    /// \brief Default and the only constructor of BasicScen.
+    ///
+    /// \param options reference to command options,
+    /// \param socket reference to a socket.
+    BasicScen(CommandOptions& options, BasePerfSocket &socket):
+        AbstractScen(options, socket)
+    {
+        basic_rate_control_.setRate(options_.getRate());
+        renew_rate_control_.setRate(options_.getRenewRate());
+        release_rate_control_.setRate(options_.getReleaseRate());
+    };
 
     /// brief\ Run performance test.
     ///
@@ -33,11 +42,10 @@ public:
     ///
     /// \throw isc::InvalidOperation if command line options are not parsed.
     /// \throw isc::Unexpected if internal Test Controller error occurred.
-    /// \return error_code, 3 if number of received packets is not equal
-    /// to number of sent packets, 0 if everything is ok.
-    int run();
+    /// \return execution status.
+    int run() override;
 
-private:
+protected:
     /// \brief A rate control class for Discover and Solicit messages.
     RateControl basic_rate_control_;
     /// \brief A rate control class for Renew messages.
index 289b51562198e5a1b6ae2c4c9534af236b7bef66..f15b72d41d94766d15fb1e173015cf86db1de377 100644 (file)
@@ -97,12 +97,6 @@ CommandOptions::LeaseType::toText() const {
     }
 }
 
-CommandOptions&
-CommandOptions::instance() {
-    static CommandOptions options;
-    return (options);
-}
-
 void
 CommandOptions::reset() {
     // Default mac address used in DHCP messages
@@ -854,7 +848,7 @@ bool CommandOptions::decodeMacString(const std::string& line) {
 }
 
 void
-CommandOptions::validate() const {
+CommandOptions::validate() {
     check((getIpVersion() != 4) && (isBroadcast() != 0),
           "-B is not compatible with IPv6 (-6)");
     check((getIpVersion() != 6) && (isRapidCommit() != 0),
@@ -931,6 +925,19 @@ CommandOptions::validate() const {
                   << "WARNING: Better results are achieved when run in multi-threaded mode." << std::endl
                   << "WARNING: To switch use -g multi option." << std::endl;
     }
+
+    if (scenario_ == Scenario::AVALANCHE) {
+        check(getClientsNum() <= 0,
+              "in case of avalanche scenario number\nof clients must be specified"
+              " using -R option explicitly");
+
+        // in case of AVALANCHE drops ie. long responses should not be observed by perfdhcp
+        double dt[2] = { 1000.0, 1000.0 };
+        drop_time_.assign(dt, dt + 2);
+        if (drop_time_set_) {
+            std::cout << "INFO: in avalanche scenario drop time is ignored" << std::endl;
+        }
+    }
 }
 
 void
@@ -1265,15 +1272,6 @@ CommandOptions::version() const {
     std::cout << "VERSION: " << VERSION << std::endl;
 }
 
-bool
-testDiags(const char diag) {
-    std::string diags(CommandOptions::instance().getDiags());
-    if (diags.find(diag) != std::string::npos) {
-        return (true);
-    }
-    return (false);
-}
-
 
 }  // namespace perfdhcp
 }  // namespace isc
index 92ca961cf4f9ee443cb9e2f0e63c69adc57bf100..693a2458e231ef9ab3eed2f77badde61eb88ce27 100644 (file)
@@ -30,6 +30,14 @@ enum class Scenario {
 class CommandOptions : public boost::noncopyable {
 public:
 
+    /// \brief Default Constructor.
+    ///
+    /// Private constructor as this is a singleton class.
+    /// Use CommandOptions::instance() to get instance of it.
+    CommandOptions() {
+        reset();
+    }
+
     /// @brief A vector holding MAC addresses.
     typedef std::vector<std::vector<uint8_t> > MacAddrsVector;
 
@@ -111,12 +119,6 @@ public:
         DORA_SARR
     };
 
-    /// CommandOptions is a singleton class. This method returns reference
-    /// to its sole instance.
-    ///
-    /// \return the only existing instance of command options
-    static CommandOptions& instance();
-
     /// \brief Reset to defaults
     ///
     /// Reset data members to default values. This is specifically
@@ -363,6 +365,18 @@ public:
     /// \return server name.
     std::string getServerName() const { return server_name_; }
 
+
+    /// \brief Find if diagnostic flag has been set.
+    ///
+    /// \param diag diagnostic flag (a,e,i,s,r,t,T).
+    /// \return true if diagnostics flag has been set.
+    bool testDiags(const char diag) {
+        if (getDiags().find(diag) != std::string::npos) {
+            return (true);
+        }
+        return (false);
+    }
+
     /// \brief Print command line arguments.
     void printCommandLine() const;
 
@@ -377,15 +391,6 @@ public:
     void version() const;
 
 private:
-
-    /// \brief Default Constructor.
-    ///
-    /// Private constructor as this is a singleton class.
-    /// Use CommandOptions::instance() to get instance of it.
-    CommandOptions() {
-        reset();
-    }
-
     /// \brief Initializes class members based on the command line.
     ///
     /// Reads each command line parameter and sets class member values.
@@ -400,7 +405,7 @@ private:
     /// \brief Validates initialized options.
     ///
     /// \throws isc::InvalidParameter if command line validation fails.
-    void validate() const;
+    void validate();
 
     /// \brief Throws !InvalidParameter exception if condition is true.
     ///
@@ -669,13 +674,6 @@ private:
     Scenario scenario_;
 };
 
-/// \brief Find if diagnostic flag has been set.
-///
-/// \param diag diagnostic flag (a,e,i,s,r,t,T).
-/// \return true if diagnostics flag has been set.
-bool
-testDiags(const char diag);
-
 }  // namespace perfdhcp
 }  // namespace isc
 
index 2ab854208a863a9ae2bac1120be870ad11a9a0e5..aa002d78ab4de1ceeb37766da3c70184df974552 100644 (file)
@@ -19,7 +19,7 @@ using namespace isc::perfdhcp;
 
 int
 main(int argc, char* argv[]) {
-    CommandOptions& command_options = CommandOptions::instance();
+    CommandOptions command_options;
     std::string diags(command_options.getDiags());
     int ret_code = 0;
     try {
@@ -36,7 +36,7 @@ main(int argc, char* argv[]) {
     } catch(isc::Exception& e) {
         ret_code = 1;
         command_options.usage();
-        std::cerr << "Error parsing command line options: "
+        std::cerr << "\nERROR: parsing command line options: "
                   << e.what() << std::endl;
         if (diags.find('e') != std::string::npos) {
             std::cerr << "Fatal error" << std::endl;
@@ -45,16 +45,17 @@ main(int argc, char* argv[]) {
     }
     try{
         auto scenario = command_options.getScenario();
+        PerfSocket socket(command_options);
         if (scenario == Scenario::BASIC) {
-            BasicScen scen;
+            BasicScen scen(command_options, socket);
             ret_code = scen.run();
         } else if (scenario == Scenario::AVALANCHE) {
-            AvalancheScen scen;
+            AvalancheScen scen(command_options, socket);
             ret_code = scen.run();
         }
     } catch (std::exception& e) {
         ret_code = 1;
-        std::cerr << "Error running perfdhcp: " << e.what() << std::endl;
+        std::cerr << "\nERROR: running perfdhcp: " << e.what() << std::endl;
         if (diags.find('e') != std::string::npos) {
             std::cerr << "Fatal error" << std::endl;
         }
index e9a55dfa2b8e2784cfbb8efdf121f39f608d7b7c..a27296682a1cf040b14521c3ec0b3bd564cf50f1 100644 (file)
@@ -20,17 +20,14 @@ using namespace isc::asiolink;
 namespace isc {
 namespace perfdhcp {
 
-PerfSocket::PerfSocket() :
-    SocketInfo(asiolink::IOAddress("127.0.0.1"), 0, openSocket()),
-    ifindex_(0)
-{
+PerfSocket::PerfSocket(CommandOptions& options) {
+    sockfd_ = openSocket(options);
     initSocketData();
 }
 
 
 int
-PerfSocket::openSocket() const {
-    CommandOptions& options = CommandOptions::instance();
+PerfSocket::openSocket(CommandOptions& options) const {
     std::string localname = options.getLocalName();
     std::string servername = options.getServerName();
     uint16_t port = options.getLocalPort();
@@ -149,5 +146,42 @@ PerfSocket::initSocketData() {
     isc_throw(BadValue, "interface for specified socket descriptor not found");
 }
 
+Pkt4Ptr
+PerfSocket::receive4(uint32_t timeout_sec, uint32_t timeout_usec) {
+    Pkt4Ptr pkt = IfaceMgr::instance().receive4(timeout_sec, timeout_usec);
+    if (pkt) {
+        /// @todo: Add packet exception handling here. Right now any
+        /// malformed packet will cause perfdhcp to abort.
+        pkt->unpack();
+    }
+    return (pkt);
+}
+
+Pkt6Ptr
+PerfSocket::receive6(uint32_t timeout_sec, uint32_t timeout_usec) {
+    Pkt6Ptr pkt = IfaceMgr::instance().receive6(timeout_sec, timeout_usec);
+    if (pkt) {
+        /// @todo: Add packet exception handling here. Right now any
+        /// malformed packet will cause perfdhcp to abort.
+        pkt->unpack();
+    }
+    return (pkt);
+}
+
+bool
+PerfSocket::send(const Pkt4Ptr& pkt) {
+    return IfaceMgr::instance().send(pkt);
+}
+
+bool
+PerfSocket::send(const Pkt6Ptr& pkt) {
+    return IfaceMgr::instance().send(pkt);
+}
+
+IfacePtr
+PerfSocket::getIface() {
+    return (IfaceMgr::instance().getIface(ifindex_));
+}
+
 }
 }
index a49cc92e32650780eee1331045ff4439e88a6c5b..83f8b971b85547ca3bfa3b9b72900f5f070e3c9f 100644 (file)
@@ -7,11 +7,34 @@
 #ifndef PERF_SOCKET_H
 #define PERF_SOCKET_H
 
+#include <perfdhcp/command_options.h>
+
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
 #include <dhcp/socket_info.h>
+#include <dhcp/iface_mgr.h>
 
 namespace isc {
 namespace perfdhcp {
 
+class BasePerfSocket : public dhcp::SocketInfo {
+public:
+    /// Interface index.
+    uint16_t ifindex_;
+
+    BasePerfSocket() :
+        SocketInfo(asiolink::IOAddress("127.0.0.1"), 0, 0),
+        ifindex_(0) {}
+
+    /// \brief Destructor of the socket wrapper class.
+    virtual ~BasePerfSocket() = default;
+
+    virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) = 0;
+    virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) = 0;
+    virtual bool send(const dhcp::Pkt4Ptr& pkt) = 0;
+    virtual bool send(const dhcp::Pkt6Ptr& pkt) = 0;
+    virtual dhcp::IfacePtr getIface() = 0;
+};
 
 /// \brief Socket wrapper structure.
 ///
@@ -22,23 +45,52 @@ namespace perfdhcp {
 /// when exception occurs). This structure extends parent
 /// structure with new field ifindex_ that holds interface
 /// index where socket is bound to.
-struct PerfSocket : public dhcp::SocketInfo {
-    /// Interface index.
-    uint16_t ifindex_;
-
+class PerfSocket : public BasePerfSocket {
+public:
     /// \brief Constructor of socket wrapper class.
     ///
     /// This constructor uses provided socket descriptor to
     /// find the name of the interface where socket has been
     /// bound to.
-    PerfSocket();
+    PerfSocket(CommandOptions& options);
 
     /// \brief Destructor of the socket wrapper class.
     ///
     /// Destructor closes wrapped socket.
     virtual ~PerfSocket();
 
-private:
+    /// \brief Receive DHCPv4 packet from interface.
+    ///
+    /// \param timeout_sec number of seconds for waiting for a packet,
+    /// \param timeout_sec number of microseconds for waiting for a packet,
+    /// \return received packet or nullptr if timed out
+    virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) override;
+
+    /// \brief Receive DHCPv6 packet from interface.
+    ///
+    /// \param timeout_sec number of seconds for waiting for a packet,
+    /// \param timeout_sec number of microseconds for waiting for a packet,
+    /// \return received packet or nullptr if timed out
+    virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) override;
+
+    /// \brief Send DHCPv4 packet through interface.
+    ///
+    /// \param pkt a packet for sending
+    /// \return true if operation succeeded
+    virtual bool send(const dhcp::Pkt4Ptr& pkt) override;
+
+    /// \brief Send DHCPv6 packet through interface.
+    ///
+    /// \param pkt a packet for sending
+    /// \return true if operation succeeded
+    virtual bool send(const dhcp::Pkt6Ptr& pkt) override;
+
+    /// \brief Get interface from IfaceMgr.
+    ///
+    /// \return shared pointer to Iface.
+    virtual dhcp::IfacePtr getIface() override;
+
+protected:
     /// \brief Initialize socket data.
     ///
     /// This method initializes members of the class that Interface
@@ -66,7 +118,7 @@ private:
     /// for the v6 socket.
     /// \throw isc::Unexpected if internal unexpected error occurred.
     /// \return socket descriptor.
-    int openSocket() const;
+    int openSocket(CommandOptions& options) const;
 };
 
 }
index 68cff6bfd1e060f5186897568288e66cae2c20b0..d55830220cad3410ef1076fc30d5b5fc040e7c5c 100644 (file)
@@ -61,7 +61,7 @@ Receiver::getPkt() {
         // In multi thread mode read packet from the queue which is feed by Receiver thread.
         util::thread::Mutex::Locker lock(pkt_queue_mutex_);
         if (pkt_queue_.empty()) {
-            if (CommandOptions::instance().getIpVersion() == 4) {
+            if (ip_version_ == 4) {
                 return Pkt4Ptr();
             } else {
                 return Pkt6Ptr();
@@ -109,21 +109,15 @@ Receiver::readPktFromSocket() {
         timeout = 1000;
     }
     try {
-        if (CommandOptions::instance().getIpVersion() == 4) {
-            pkt = IfaceMgr::instance().receive4(0, timeout);
+        if (ip_version_ == 4) {
+            pkt = socket_.receive4(0, timeout);
         } else {
-            pkt = IfaceMgr::instance().receive6(0, timeout);
+            pkt = socket_.receive6(0, timeout);
         }
     } catch (const Exception& e) {
         cerr << "Failed to receive DHCP packet: " << e.what() << endl;
     }
 
-    if (pkt) {
-        /// @todo: Add packet exception handling here. Right now any
-        /// malformed packet will cause perfdhcp to abort.
-        pkt->unpack();
-    }
-
     return (pkt);
 }
 
index 4a0afc5a54e5a4aebe143e9b41de809a9ecdcda3..e51e4f4076c79ee4e3d2ef8ffd6116875ead9415 100644 (file)
@@ -46,15 +46,23 @@ private:
     /// \brief Mutex for controlling access to the queue.
     util::thread::Mutex pkt_queue_mutex_;
 
+    BasePerfSocket &socket_;
+
     /// \brief Single- or thread-mode indicator.
     bool single_threaded_;
 
+    uint8_t ip_version_;
+
 public:
     /// \brief Receiver constructor.
     ///
     /// \param socket A socket for receiving packets.
-    Receiver() :
-        single_threaded_(CommandOptions::instance().isSingleThreaded()) {
+    /// \param single_threaded A flag indicating running mode.
+    /// \param ip_version An IP version: 4 or 6
+    Receiver(BasePerfSocket &socket, bool single_threaded, uint8_t ip_version) :
+        socket_(socket),
+        single_threaded_(single_threaded),
+        ip_version_(ip_version) {
     }
 
     /// \brief Destructor.
index bd741e31ed5e4972e5d97fbd8feeae5f538ea59a..51c706804afe40946a74705a0cbafb6d5501bde8 100644 (file)
@@ -5,7 +5,6 @@
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 #include <perfdhcp/stats_mgr.h>
-#include <perfdhcp/command_options.h>
 
 
 namespace isc {
@@ -78,7 +77,9 @@ ExchangeStats::updateDelays(const dhcp::PktPtr& sent_packet,
         rcvd_time.is_not_a_date_time()) {
         isc_throw(Unexpected,
                   "Timestamp must be set for sent and "
-                  "received packet to measure RTT");
+                  "received packet to measure RTT,"
+                  << " sent: " << sent_time
+                  << " recv: " << rcvd_time);
     }
     boost::posix_time::time_period period(sent_time, rcvd_time);
     // We don't bother calculating deltas in nanoseconds. It is much
@@ -313,16 +314,15 @@ ExchangeStats::printTimestamps() {
     }
 }
 
-StatsMgr::StatsMgr() :
+StatsMgr::StatsMgr(CommandOptions& options) :
     exchanges_(),
-    boot_time_(boost::posix_time::microsec_clock::universal_time())
+    boot_time_(boost::posix_time::microsec_clock::universal_time()),
+    options_(options)
 {
-    CommandOptions& options = CommandOptions::instance();
-
     // Check if packet archive mode is required. If user
     // requested diagnostics option -x t we have to enable
     // it so as StatsMgr preserves all packets.
-    archive_enabled_ = testDiags('t') ? true : false;
+    archive_enabled_ = options.testDiags('t') ? true : false;
 
     if (options.getIpVersion() == 4) {
         addExchangeStats(ExchangeType::DO, options.getDropTime()[0]);
@@ -345,7 +345,7 @@ StatsMgr::StatsMgr() :
             addExchangeStats(ExchangeType::RL);
         }
     }
-    if (testDiags('i')) {
+    if (options.testDiags('i')) {
         addCustomCounter("shortwait", "Short waits for packets");
     }
 }
index b05a75b800304e0d90798db7c937770a0e93018e..351e51be08530335188de2bb00b7433275016104 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <dhcp/pkt.h>
 #include <exceptions/exceptions.h>
+#include <perfdhcp/command_options.h>
 
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
@@ -649,7 +650,7 @@ public:
     /// the test. If this is not selected archiving should be disabled
     /// for performance reasons and to avoid waste of memory for storing
     /// large list of archived packets.
-    StatsMgr();
+    StatsMgr(CommandOptions& options);
 
     /// \brief Specify new exchange type.
     ///
@@ -1109,6 +1110,8 @@ private:
     bool archive_enabled_;
 
     boost::posix_time::ptime boot_time_; ///< Time when test is started.
+
+    CommandOptions& options_;
 };
 
 /// Pointer to Statistics Manager;
index 9a5cad831065edc7f7316a0c7b045f6fb7494bab..1493a8f1e6cfcae1a680ffbdf9f6528af24e53da 100644 (file)
@@ -45,8 +45,7 @@ bool TestControl::interrupted_ = false;
 bool
 TestControl::waitToExit() const {
     static ptime exit_time = ptime(not_a_date_time);
-    CommandOptions& options = CommandOptions::instance();
-    uint32_t wait_time = options.getExitWaitTime();
+    uint32_t wait_time = options_.getExitWaitTime();
 
     // If we care and not all packets are in yet
     if (wait_time && !haveAllPacketsBeenReceived()) {
@@ -67,9 +66,8 @@ TestControl::waitToExit() const {
 
 bool
 TestControl::haveAllPacketsBeenReceived() const {
-    const CommandOptions& options = CommandOptions::instance();
-    const uint8_t& ipversion = options.getIpVersion();
-    const std::vector<int>& num_request = options.getNumRequests();
+    const uint8_t& ipversion = options_.getIpVersion();
+    const std::vector<int>& num_request = options_.getNumRequests();
     const size_t& num_request_size = num_request.size();
 
     if (num_request_size == 0) {
@@ -95,10 +93,9 @@ TestControl::haveAllPacketsBeenReceived() const {
 
 void
 TestControl::cleanCachedPackets() {
-    CommandOptions& options = CommandOptions::instance();
     // When Renews are not sent, Reply packets are not cached so there
     // is nothing to do.
-    if (options.getRenewRate() == 0) {
+    if (options_.getRenewRate() == 0) {
         return;
     }
 
@@ -115,9 +112,9 @@ TestControl::cleanCachedPackets() {
         // since we want to randomize leases to be renewed so leave 5
         // times more packets to randomize from.
         /// @todo The cache size might be controlled from the command line.
-        if (reply_storage_.size() > 5 * options.getRenewRate()) {
+        if (reply_storage_.size() > 5 * options_.getRenewRate()) {
             reply_storage_.clear(reply_storage_.size() -
-                                 5 * options.getRenewRate());
+                                 5 * options_.getRenewRate());
         }
         // Remember when we performed a cleanup for the last time.
         // We want to do the next cleanup not earlier than in one second.
@@ -132,7 +129,7 @@ TestControl::copyIaOptions(const Pkt6Ptr& pkt_from, Pkt6Ptr& pkt_to) {
                   " for the copyIaOptions function");
     }
     // IA_NA
-    if (CommandOptions::instance().getLeaseType()
+    if (options_.getLeaseType()
         .includes(CommandOptions::LeaseType::ADDRESS)) {
         OptionPtr option = pkt_from->getOption(D6O_IA_NA);
         if (!option) {
@@ -142,7 +139,7 @@ TestControl::copyIaOptions(const Pkt6Ptr& pkt_from, Pkt6Ptr& pkt_to) {
         pkt_to->addOption(option);
     }
     // IA_PD
-    if (CommandOptions::instance().getLeaseType()
+    if (options_.getLeaseType()
         .includes(CommandOptions::LeaseType::PREFIX)) {
         OptionPtr option = pkt_from->getOption(D6O_IA_PD);
         if (!option) {
@@ -311,9 +308,7 @@ TestControl::factoryRequestList4(Option::Universe u,
 
 std::vector<uint8_t>
 TestControl::generateMacAddress(uint8_t& randomized) {
-    CommandOptions& options = CommandOptions::instance();
-
-    const CommandOptions::MacAddrsVector& macs = options.getMacsFromFile();
+    const CommandOptions::MacAddrsVector& macs = options_.getMacsFromFile();
     // if we are using the -M option return a random one from the list...
     if (macs.size() > 0) {
       uint16_t r = number_generator_();
@@ -324,12 +319,12 @@ TestControl::generateMacAddress(uint8_t& randomized) {
 
     } else {
       // ... otherwise use the standard behavior
-      uint32_t clients_num = options.getClientsNum();
+      uint32_t clients_num = options_.getClientsNum();
       if (clients_num < 2) {
-          return (options.getMacTemplate());
+          return (options_.getMacTemplate());
       }
       // Get the base MAC address. We are going to randomize part of it.
-      std::vector<uint8_t> mac_addr(options.getMacTemplate());
+      std::vector<uint8_t> mac_addr(options_.getMacTemplate());
       if (mac_addr.size() != HW_ETHER_LEN) {
           isc_throw(BadValue, "invalid MAC address template specified");
       }
@@ -368,9 +363,8 @@ TestControl::generateClientId(const dhcp::HWAddrPtr& hwaddr) const {
 
 std::vector<uint8_t>
 TestControl::generateDuid(uint8_t& randomized) {
-    CommandOptions& options = CommandOptions::instance();
     std::vector<uint8_t> mac_addr(generateMacAddress(randomized));
-    const CommandOptions::MacAddrsVector& macs = options.getMacsFromFile();
+    const CommandOptions::MacAddrsVector& macs = options_.getMacsFromFile();
     // pick a random mac address if we are using option -M..
     if (macs.size() > 0) {
       uint16_t r = number_generator_();
@@ -399,12 +393,12 @@ TestControl::generateDuid(uint8_t& randomized) {
       std::copy(mac.begin(), mac.end(), duid.begin() + 4);
       return (duid);
     } else {
-      uint32_t clients_num = options.getClientsNum();
+      uint32_t clients_num = options_.getClientsNum();
       if ((clients_num == 0) || (clients_num == 1)) {
-          return (options.getDuidTemplate());
+          return (options_.getDuidTemplate());
       }
       // Get the base DUID. We are going to randomize part of it.
-      std::vector<uint8_t> duid(options.getDuidTemplate());
+      std::vector<uint8_t> duid(options_.getDuidTemplate());
       /// @todo: add support for DUIDs of different sizes.
       duid.resize(duid.size());
       std::copy(mac_addr.begin(), mac_addr.end(),
@@ -415,10 +409,10 @@ TestControl::generateDuid(uint8_t& randomized) {
 
 int
 TestControl::getElapsedTimeOffset() const {
-    int elp_offset = CommandOptions::instance().getIpVersion() == 4 ?
+    int elp_offset = options_.getIpVersion() == 4 ?
         DHCPV4_ELAPSED_TIME_OFFSET : DHCPV6_ELAPSED_TIME_OFFSET;
-    if (CommandOptions::instance().getElapsedTimeOffset() > 0) {
-        elp_offset = CommandOptions::instance().getElapsedTimeOffset();
+    if (options_.getElapsedTimeOffset() > 0) {
+        elp_offset = options_.getElapsedTimeOffset();
     }
     return (elp_offset);
 }
@@ -440,30 +434,30 @@ TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
 
 int
 TestControl::getRandomOffset(const int arg_idx) const {
-    int rand_offset = CommandOptions::instance().getIpVersion() == 4 ?
+    int rand_offset = options_.getIpVersion() == 4 ?
         DHCPV4_RANDOMIZATION_OFFSET : DHCPV6_RANDOMIZATION_OFFSET;
-    if (CommandOptions::instance().getRandomOffset().size() > arg_idx) {
-        rand_offset = CommandOptions::instance().getRandomOffset()[arg_idx];
+    if (options_.getRandomOffset().size() > arg_idx) {
+        rand_offset = options_.getRandomOffset()[arg_idx];
     }
     return (rand_offset);
 }
 
 int
 TestControl::getRequestedIpOffset() const {
-    int rip_offset = CommandOptions::instance().getIpVersion() == 4 ?
+    int rip_offset = options_.getIpVersion() == 4 ?
         DHCPV4_REQUESTED_IP_OFFSET : DHCPV6_IA_NA_OFFSET;
-    if (CommandOptions::instance().getRequestedIpOffset() > 0) {
-        rip_offset = CommandOptions::instance().getRequestedIpOffset();
+    if (options_.getRequestedIpOffset() > 0) {
+        rip_offset = options_.getRequestedIpOffset();
     }
     return (rip_offset);
 }
 
 int
 TestControl::getServerIdOffset() const {
-    int srvid_offset = CommandOptions::instance().getIpVersion() == 4 ?
+    int srvid_offset = options_.getIpVersion() == 4 ?
         DHCPV4_SERVERID_OFFSET : DHCPV6_SERVERID_OFFSET;
-    if (CommandOptions::instance().getServerIdOffset() > 0) {
-        srvid_offset = CommandOptions::instance().getServerIdOffset();
+    if (options_.getServerIdOffset() > 0) {
+        srvid_offset = options_.getServerIdOffset();
     }
     return (srvid_offset);
 }
@@ -478,10 +472,10 @@ TestControl::getTemplateBuffer(const size_t idx) const {
 
 int
 TestControl::getTransactionIdOffset(const int arg_idx) const {
-    int xid_offset = CommandOptions::instance().getIpVersion() == 4 ?
+    int xid_offset = options_.getIpVersion() == 4 ?
         DHCPV4_TRANSID_OFFSET : DHCPV6_TRANSID_OFFSET;
-    if (CommandOptions::instance().getTransactionIdOffset().size() > arg_idx) {
-        xid_offset = CommandOptions::instance().getTransactionIdOffset()[arg_idx];
+    if (options_.getTransactionIdOffset().size() > arg_idx) {
+        xid_offset = options_.getTransactionIdOffset()[arg_idx];
     }
     return (xid_offset);
 }
@@ -504,8 +498,7 @@ TestControl::initPacketTemplates() {
     template_packets_v4_.clear();
     template_packets_v6_.clear();
     template_buffers_.clear();
-    CommandOptions& options = CommandOptions::instance();
-    std::vector<std::string> template_files = options.getTemplateFiles();
+    std::vector<std::string> template_files = options_.getTemplateFiles();
     for (std::vector<std::string>::const_iterator it = template_files.begin();
          it != template_files.end(); ++it) {
         readPacketTemplate(*it);
@@ -515,9 +508,8 @@ TestControl::initPacketTemplates() {
 void
 TestControl::sendPackets(const uint64_t packets_num,
                          const bool preload /* = false */) {
-    CommandOptions& options = CommandOptions::instance();
     for (uint64_t i = packets_num; i > 0; --i) {
-        if (options.getIpVersion() == 4) {
+        if (options_.getIpVersion() == 4) {
             // No template packets means that no -T option was specified.
             // We have to build packets ourselves.
             if (template_buffers_.empty()) {
@@ -564,15 +556,14 @@ TestControl::sendMultipleMessages6(const uint32_t msg_type,
 
 void
 TestControl::printDiagnostics() const {
-    CommandOptions& options = CommandOptions::instance();
-    if (testDiags('a')) {
+    if (options_.testDiags('a')) {
         // Print all command line parameters.
-        options.printCommandLine();
+        options_.printCommandLine();
         // Print MAC and DUID.
-        std::cout << "Set MAC to " << vector2Hex(options.getMacTemplate(), "::")
+        std::cout << "Set MAC to " << vector2Hex(options_.getMacTemplate(), "::")
                   << std::endl;
-        if (options.getDuidTemplate().size() > 0) {
-            std::cout << "Set DUID to " << vector2Hex(options.getDuidTemplate()) << std::endl;
+        if (options_.getDuidTemplate().size() > 0) {
+            std::cout << "Set DUID to " << vector2Hex(options_.getDuidTemplate()) << std::endl;
         }
     }
 }
@@ -581,7 +572,7 @@ void
 TestControl::printTemplate(const uint8_t packet_type) const {
     std::string hex_buf;
     int arg_idx = 0;
-    if (CommandOptions::instance().getIpVersion() == 4) {
+    if (options_.getIpVersion() == 4) {
         if (packet_type == DHCPREQUEST) {
             arg_idx = 1;
         }
@@ -595,7 +586,7 @@ TestControl::printTemplate(const uint8_t packet_type) const {
             std::vector<uint8_t> buf(out_buf_data, out_buf_data + out_buf.getLength());
             hex_buf = vector2Hex(buf);
         }
-    } else if (CommandOptions::instance().getIpVersion() == 6) {
+    } else if (options_.getIpVersion() == 6) {
         if (packet_type == DHCPV6_REQUEST) {
             arg_idx = 1;
         }
@@ -636,11 +627,10 @@ TestControl::printTemplate(const uint8_t packet_type) const {
 
 void
 TestControl::printTemplates() const {
-    CommandOptions& options = CommandOptions::instance();
-    if (options.getIpVersion() == 4) {
+    if (options_.getIpVersion() == 4) {
         printTemplate(DHCPDISCOVER);
         printTemplate(DHCPREQUEST);
-    } else if (options.getIpVersion() == 6) {
+    } else if (options_.getIpVersion() == 6) {
         printTemplate(DHCPV6_SOLICIT);
         printTemplate(DHCPV6_REQUEST);
     }
@@ -649,22 +639,21 @@ TestControl::printTemplates() const {
 void
 TestControl::printRate() const {
     double rate = 0;
-    CommandOptions& options = CommandOptions::instance();
     std::string exchange_name = "4-way exchanges";
     ExchangeType xchg_type = ExchangeType::DO;
-    if (options.getIpVersion() == 4) {
+    if (options_.getIpVersion() == 4) {
         xchg_type =
-            options.getExchangeMode() == CommandOptions::DO_SA ?
+            options_.getExchangeMode() == CommandOptions::DO_SA ?
             ExchangeType::DO : ExchangeType::RA;
         if (xchg_type == ExchangeType::DO) {
             exchange_name = "DISCOVER-OFFER";
         }
-    } else if (options.getIpVersion() == 6) {
+    } else if (options_.getIpVersion() == 6) {
         xchg_type =
-            options.getExchangeMode() == CommandOptions::DO_SA ?
+            options_.getExchangeMode() == CommandOptions::DO_SA ?
             ExchangeType::SA : ExchangeType::RR;
         if (xchg_type == ExchangeType::SA) {
-            exchange_name = options.isRapidCommit() ? "Solicit-Reply" :
+            exchange_name = options_.isRapidCommit() ? "Solicit-Reply" :
                 "Solicit-Advertise";
         }
     }
@@ -674,8 +663,8 @@ TestControl::printRate() const {
     std::ostringstream s;
     s << "***Rate statistics***" << std::endl;
     s << "Rate: " << rate << " " << exchange_name << "/second";
-    if (options.getRate() > 0) {
-        s << ", expected rate: " << options.getRate() << std::endl;
+    if (options_.getRate() > 0) {
+        s << ", expected rate: " << options_.getRate() << std::endl;
     }
 
     std::cout << s.str() << std::endl;
@@ -683,8 +672,7 @@ TestControl::printRate() const {
 
 void
 TestControl::printIntermediateStats() {
-    CommandOptions& options = CommandOptions::instance();
-    int delay = options.getReportDelay();
+    int delay = options_.getReportDelay();
     ptime now = microsec_clock::universal_time();
     time_period time_since_report(last_report_, now);
     if (time_since_report.length().total_seconds() >= delay) {
@@ -697,7 +685,7 @@ void
 TestControl::printStats() const {
     printRate();
     stats_mgr_.printStats();
-    if (testDiags('i')) {
+    if (options_.testDiags('i')) {
         stats_mgr_.printCustomCounters();
     }
 }
@@ -771,8 +759,7 @@ TestControl::processReceivedPacket4(const Pkt4Ptr& pkt4) {
     if (pkt4->getType() == DHCPOFFER) {
         PktPtr pkt = stats_mgr_.passRcvdPacket(ExchangeType::DO, pkt4);
         Pkt4Ptr discover_pkt4(boost::dynamic_pointer_cast<Pkt4>(pkt));
-        CommandOptions::ExchangeMode xchg_mode =
-            CommandOptions::instance().getExchangeMode();
+        CommandOptions::ExchangeMode xchg_mode = options_.getExchangeMode();
         if ((xchg_mode == CommandOptions::DORA_SARR) && discover_pkt4) {
             if (template_buffers_.size() < 2) {
                 sendRequest4(discover_pkt4, pkt4);
@@ -814,8 +801,7 @@ TestControl::processReceivedPacket6(const Pkt6Ptr& pkt6) {
     if (packet_type == DHCPV6_ADVERTISE) {
         PktPtr pkt = stats_mgr_.passRcvdPacket(ExchangeType::SA, pkt6);
         Pkt6Ptr solicit_pkt6(boost::dynamic_pointer_cast<Pkt6>(pkt));
-        CommandOptions::ExchangeMode xchg_mode =
-            CommandOptions::instance().getExchangeMode();
+        CommandOptions::ExchangeMode xchg_mode = options_.getExchangeMode();
         if ((xchg_mode == CommandOptions::DORA_SARR) && solicit_pkt6) {
             /// @todo check whether received ADVERTISE packet is sane.
             /// We might want to check if STATUS_CODE option is non-zero
@@ -869,7 +855,7 @@ TestControl::consumeReceivedPackets() {
     PktPtr pkt;
     while ((pkt = receiver_.getPkt())) {
         pkt_count += 1;
-        if (CommandOptions::instance().getIpVersion() == 4) {
+        if (options_.getIpVersion() == 4) {
             Pkt4Ptr pkt4 = boost::dynamic_pointer_cast<Pkt4>(pkt);
             processReceivedPacket4(pkt4);
         } else {
@@ -940,8 +926,7 @@ TestControl::registerOptionFactories6() const {
 
 void
 TestControl::registerOptionFactories() const {
-    CommandOptions& options = CommandOptions::instance();
-    switch(options.getIpVersion()) {
+    switch(options_.getIpVersion()) {
     case 4:
         registerOptionFactories4();
         break;
@@ -966,21 +951,24 @@ TestControl::reset() {
     interrupted_ = false;
 }
 
-TestControl::TestControl() :
-    number_generator_(0, CommandOptions::instance().getMacsFromFile().size())
+TestControl::TestControl(CommandOptions& options, BasePerfSocket &socket) :
+    number_generator_(0, options.getMacsFromFile().size()),
+    socket_(socket),
+    receiver_(socket, options.isSingleThreaded(), options.getIpVersion()),
+    stats_mgr_(options),
+    options_(options)
 {
     // Reset singleton state before test starts.
     reset();
 
-    CommandOptions& options = CommandOptions::instance();
     // Ip version is not set ONLY in case the command options
     // were not parsed. This surely means that parse() function
     // was not called prior to starting the test. This is fatal
     // error.
-    if (options.getIpVersion() == 0) {
+    if (options_.getIpVersion() == 0) {
         isc_throw(InvalidOperation,
                   "command options must be parsed before running a test");
-    } else if (options.getIpVersion() == 4) {
+    } else if (options_.getIpVersion() == 4) {
         // Turn off packet queueing.
         IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, data::ElementPtr());
         setTransidGenerator(NumberGeneratorPtr(new SequentialGenerator()));
@@ -990,8 +978,8 @@ TestControl::TestControl() :
         setTransidGenerator(NumberGeneratorPtr(new SequentialGenerator(0x00FFFFFF)));
     }
 
-    uint32_t clients_num = options.getClientsNum() == 0 ?
-        1 : options.getClientsNum();
+    uint32_t clients_num = options_.getClientsNum() == 0 ?
+        1 : options_.getClientsNum();
     setMacAddrGenerator(NumberGeneratorPtr(new SequentialGenerator(clients_num)));
 
     // Diagnostics are command line options mainly.
@@ -1001,8 +989,8 @@ TestControl::TestControl() :
     // Initialize packet templates.
     initPacketTemplates();
     // Initialize randomization seed.
-    if (options.isSeeded()) {
-        srandom(options.getSeed());
+    if (options_.isSeeded()) {
+        srandom(options_.getSeed());
     } else {
         // Seed with current time.
         time_period duration(from_iso_string("20111231T235959"),
@@ -1016,15 +1004,14 @@ TestControl::TestControl() :
 
 void
 TestControl::runWrapped(bool do_stop /*= false */) const {
-    CommandOptions& options = CommandOptions::instance();
-    if (!options.getWrapped().empty()) {
+    if (!options_.getWrapped().empty()) {
         pid_t pid = 0;
         signal(SIGCHLD, handleChild);
         pid = fork();
         if (pid < 0) {
             isc_throw(Unexpected, "unable to fork");
         } else if (pid == 0) {
-            execlp(options.getWrapped().c_str(),
+            execlp(options_.getWrapped().c_str(),
                    do_stop ? "stop" : "start",
                    NULL);
         }
@@ -1033,7 +1020,7 @@ TestControl::runWrapped(bool do_stop /*= false */) const {
 
 void
 TestControl::saveFirstPacket(const Pkt4Ptr& pkt) {
-    if (testDiags('T')) {
+    if (options_.testDiags('T')) {
         if (template_packets_v4_.find(pkt->getType()) == template_packets_v4_.end()) {
             template_packets_v4_[pkt->getType()] = pkt;
         }
@@ -1042,7 +1029,7 @@ TestControl::saveFirstPacket(const Pkt4Ptr& pkt) {
 
 void
 TestControl::saveFirstPacket(const Pkt6Ptr& pkt) {
-    if (testDiags('T')) {
+    if (options_.testDiags('T')) {
         if (template_packets_v6_.find(pkt->getType()) == template_packets_v6_.end()) {
             template_packets_v6_[pkt->getType()] = pkt;
         }
@@ -1086,7 +1073,7 @@ TestControl::sendDiscover4(const bool preload /*= false*/) {
     addExtraOpts(pkt4);
 
     pkt4->pack();
-    IfaceMgr::instance().send(pkt4);
+    socket_.send(pkt4);
     if (!preload) {
         stats_mgr_.passSentPacket(ExchangeType::DO, pkt4);
     }
@@ -1130,7 +1117,7 @@ TestControl::sendDiscover4(const std::vector<uint8_t>& template_buf,
     // Pack the input packet buffer to output buffer so as it can
     // be sent to server.
     pkt4->rawPack();
-    IfaceMgr::instance().send(boost::static_pointer_cast<Pkt4>(pkt4));
+    socket_.send(boost::static_pointer_cast<Pkt4>(pkt4));
     if (!preload) {
         // Update packet stats.
         stats_mgr_.passSentPacket(ExchangeType::DO,
@@ -1156,7 +1143,7 @@ TestControl::sendRequestFromAck() {
 
     msg->pack();
     // And send it.
-    IfaceMgr::instance().send(msg);
+    socket_.send(msg);
     stats_mgr_.passSentPacket(ExchangeType::RNA, msg);
     return (true);
 }
@@ -1182,7 +1169,7 @@ TestControl::sendMessageFromReply(const uint16_t msg_type) {
 
     msg->pack();
     // And send it.
-    IfaceMgr::instance().send(msg);
+    socket_.send(msg);
     stats_mgr_.passSentPacket((msg_type == DHCPV6_RENEW ? ExchangeType::RN
                                 : ExchangeType::RL), msg);
     return (true);
@@ -1197,7 +1184,7 @@ TestControl::sendRequest4(const dhcp::Pkt4Ptr& discover_pkt4,
 
     // Use first flags indicates that we want to use the server
     // id captured in first packet.
-    if (CommandOptions::instance().isUseFirst() &&
+    if (options_.isUseFirst() &&
         (first_packet_serverid_.size() > 0)) {
         pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_SERVER_IDENTIFIER,
                                         first_packet_serverid_));
@@ -1244,7 +1231,7 @@ TestControl::sendRequest4(const dhcp::Pkt4Ptr& discover_pkt4,
     pkt4->setSecs(static_cast<uint16_t>(elapsed_time / 1000));
     // Prepare on wire data to send.
     pkt4->pack();
-    IfaceMgr::instance().send(pkt4);
+    socket_.send(pkt4);
     stats_mgr_.passSentPacket(ExchangeType::RA, pkt4);
     saveFirstPacket(pkt4);
 }
@@ -1297,7 +1284,7 @@ TestControl::sendRequest4(const std::vector<uint8_t>& template_buf,
     size_t sid_offset = getServerIdOffset();
     // Use first flags indicates that we want to use the server
     // id captured in first packet.
-    if (CommandOptions::instance().isUseFirst() &&
+    if (options_.isUseFirst() &&
         (first_packet_serverid_.size() > 0)) {
         boost::shared_ptr<LocalizedOption>
             opt_serverid(new LocalizedOption(Option::V4,
@@ -1351,7 +1338,7 @@ TestControl::sendRequest4(const std::vector<uint8_t>& template_buf,
 
     // Prepare on-wire data.
     pkt4->rawPack();
-    IfaceMgr::instance().send(boost::static_pointer_cast<Pkt4>(pkt4));
+    socket_.send(boost::static_pointer_cast<Pkt4>(pkt4));
     // Update packet stats.
     stats_mgr_.passSentPacket(ExchangeType::RA,
                                boost::static_pointer_cast<Pkt4>(pkt4));
@@ -1375,7 +1362,7 @@ TestControl::sendRequest6(const Pkt6Ptr& advertise_pkt6) {
 
     // Use first flags indicates that we want to use the server
     // id captured in first packet.
-    if (CommandOptions::instance().isUseFirst() &&
+    if (options_.isUseFirst() &&
         (first_packet_serverid_.size() > 0)) {
         pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID,
                                         first_packet_serverid_));
@@ -1405,7 +1392,7 @@ TestControl::sendRequest6(const Pkt6Ptr& advertise_pkt6) {
 
     // Prepare on-wire data.
     pkt6->pack();
-    IfaceMgr::instance().send(pkt6);
+    socket_.send(pkt6);
     stats_mgr_.passSentPacket(ExchangeType::RR, pkt6);
     saveFirstPacket(pkt6);
 }
@@ -1433,7 +1420,7 @@ TestControl::sendRequest6(const std::vector<uint8_t>& template_buf,
     size_t sid_offset = getServerIdOffset();
     // Use first flags indicates that we want to use the server
     // id captured in first packet.
-    if (CommandOptions::instance().isUseFirst() &&
+    if (options_.isUseFirst() &&
         (first_packet_serverid_.size() > 0)) {
         boost::shared_ptr<LocalizedOption>
             opt_serverid(new LocalizedOption(Option::V6,
@@ -1509,7 +1496,7 @@ TestControl::sendRequest6(const std::vector<uint8_t>& template_buf,
     // Prepare on wire data.
     pkt6->rawPack();
     // Send packet.
-    IfaceMgr::instance().send(pkt6);
+    socket_.send(pkt6);
     // Update packet stats.
     stats_mgr_.passSentPacket(ExchangeType::RR, pkt6);
 
@@ -1518,7 +1505,7 @@ TestControl::sendRequest6(const std::vector<uint8_t>& template_buf,
     // contents will be printed. Here we check if this packet has been already
     // collected. If it hasn't we save this packet so as we can print its
     // contents when test is finished.
-    if (testDiags('T') &&
+    if (options_.testDiags('T') &&
         (template_packets_v6_.find(DHCPV6_REQUEST) == template_packets_v6_.end())) {
         template_packets_v6_[DHCPV6_REQUEST] = pkt6;
     }
@@ -1536,7 +1523,7 @@ TestControl::sendSolicit6(const bool preload /*= false*/) {
         isc_throw(Unexpected, "failed to create SOLICIT packet");
     }
     pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME));
-    if (CommandOptions::instance().isRapidCommit()) {
+    if (options_.isRapidCommit()) {
         pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT));
     }
     pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid));
@@ -1546,13 +1533,11 @@ TestControl::sendSolicit6(const bool preload /*= false*/) {
     // IPv6 address (with IA_NA) or IPv6 prefix (IA_PD) or both.
 
     // IA_NA
-    if (CommandOptions::instance().getLeaseType()
-        .includes(CommandOptions::LeaseType::ADDRESS)) {
+    if (options_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) {
         pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA));
     }
     // IA_PD
-    if (CommandOptions::instance().getLeaseType()
-        .includes(CommandOptions::LeaseType::PREFIX)) {
+    if (options_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) {
         pkt6->addOption(Option::factory(Option::V6, D6O_IA_PD));
     }
 
@@ -1562,7 +1547,7 @@ TestControl::sendSolicit6(const bool preload /*= false*/) {
     addExtraOpts(pkt6);
 
     pkt6->pack();
-    IfaceMgr::instance().send(pkt6);
+    socket_.send(pkt6);
     if (!preload) {
         stats_mgr_.passSentPacket(ExchangeType::SA, pkt6);
     }
@@ -1605,7 +1590,7 @@ TestControl::sendSolicit6(const std::vector<uint8_t>& template_buf,
     addExtraOpts(pkt6);
 
     // Send solicit packet.
-    IfaceMgr::instance().send(pkt6);
+    socket_.send(pkt6);
     if (!preload) {
         // Update packet stats.
         stats_mgr_.passSentPacket(ExchangeType::SA, pkt6);
@@ -1616,9 +1601,8 @@ TestControl::sendSolicit6(const std::vector<uint8_t>& template_buf,
 
 void
 TestControl::setDefaults4(const Pkt4Ptr& pkt) {
-    CommandOptions& options = CommandOptions::instance();
     // Interface name.
-    IfacePtr iface = IfaceMgr::instance().getIface(socket_.ifindex_);
+    IfacePtr iface = socket_.getIface();
     if (iface == NULL) {
         isc_throw(BadValue, "unable to find interface with given index");
     }
@@ -1628,13 +1612,13 @@ TestControl::setDefaults4(const Pkt4Ptr& pkt) {
     // Local client's port (68)
     pkt->setLocalPort(DHCP4_CLIENT_PORT);
     // Server's port (67)
-    if (options.getRemotePort()) {
-        pkt->setRemotePort(options.getRemotePort());
+    if (options_.getRemotePort()) {
+        pkt->setRemotePort(options_.getRemotePort());
     } else {
         pkt->setRemotePort(DHCP4_SERVER_PORT);
     }
     // The remote server's name or IP.
-    pkt->setRemoteAddr(IOAddress(options.getServerName()));
+    pkt->setRemoteAddr(IOAddress(options_.getServerName()));
     // Set local address.
     pkt->setLocalAddr(IOAddress(socket_.addr_));
     // Set relay (GIADDR) address to local address.
@@ -1645,9 +1629,8 @@ TestControl::setDefaults4(const Pkt4Ptr& pkt) {
 
 void
 TestControl::setDefaults6(const Pkt6Ptr& pkt) {
-    CommandOptions& options = CommandOptions::instance();
     // Interface name.
-    IfacePtr iface = IfaceMgr::instance().getIface(socket_.ifindex_);
+    IfacePtr iface = socket_.getIface();
     if (iface == NULL) {
         isc_throw(BadValue, "unable to find interface with given index");
     }
@@ -1657,20 +1640,20 @@ TestControl::setDefaults6(const Pkt6Ptr& pkt) {
     // Local client's port (547)
     pkt->setLocalPort(DHCP6_CLIENT_PORT);
     // Server's port (548)
-    if (options.getRemotePort()) {
-        pkt->setRemotePort(options.getRemotePort());
+    if (options_.getRemotePort()) {
+        pkt->setRemotePort(options_.getRemotePort());
     } else {
         pkt->setRemotePort(DHCP6_SERVER_PORT);
     }
     // Set local address.
     pkt->setLocalAddr(socket_.addr_);
     // The remote server's name or IP.
-    pkt->setRemoteAddr(IOAddress(options.getServerName()));
+    pkt->setRemoteAddr(IOAddress(options_.getServerName()));
 
     // only act as a relay agent when told so.
     /// @todo: support more level of encapsulation, at the moment we only support
     /// one, via -A1 option.
-    if (options.isUseRelayedV6()) {
+    if (options_.isUseRelayedV6()) {
       Pkt6::RelayInfo relay_info;
       relay_info.msg_type_ = DHCPV6_RELAY_FORW;
       relay_info.hop_count_ = 1;
@@ -1683,8 +1666,7 @@ TestControl::setDefaults6(const Pkt6Ptr& pkt) {
 void
 TestControl::addExtraOpts(const Pkt4Ptr& pkt) {
     // All all extra options that the user may have specified
-    CommandOptions& options = CommandOptions::instance();
-    const dhcp::OptionCollection& extra_opts = options.getExtraOpts();
+    const dhcp::OptionCollection& extra_opts = options_.getExtraOpts();
     for (auto entry : extra_opts) {
         pkt->addOption(entry.second);
     }
@@ -1693,8 +1675,7 @@ TestControl::addExtraOpts(const Pkt4Ptr& pkt) {
 void
 TestControl::addExtraOpts(const Pkt6Ptr& pkt) {
     // All all extra options that the user may have specified
-    CommandOptions& options = CommandOptions::instance();
-    const dhcp::OptionCollection& extra_opts = options.getExtraOpts();
+    const dhcp::OptionCollection& extra_opts = options_.getExtraOpts();
     for (auto entry : extra_opts) {
         pkt->addOption(entry.second);
     }
index 73dc968d91706bb7f018d22ca6ca4ec52f786a19..61778fa881727cca71c29c4b16441811e1d7c396 100644 (file)
@@ -11,6 +11,8 @@
 #include <perfdhcp/rate_control.h>
 #include <perfdhcp/stats_mgr.h>
 #include <perfdhcp/receiver.h>
+#include <perfdhcp/command_options.h>
+#include <perfdhcp/perf_socket.h>
 
 #include <dhcp/iface_mgr.h>
 #include <dhcp/dhcp6.h>
@@ -124,7 +126,7 @@ public:
 class TestControl : public boost::noncopyable {
 public:
     /// \brief Default constructor.
-    TestControl();
+    TestControl(CommandOptions& options, BasePerfSocket &socket);
 
     /// Packet template buffer.
     typedef std::vector<uint8_t> TemplateBuffer;
@@ -925,7 +927,7 @@ protected:
     /// spaces or hexadecimal digits.
     void readPacketTemplate(const std::string& file_name);
 
-    PerfSocket socket_;
+    BasePerfSocket &socket_;
     Receiver receiver_;
 
     boost::posix_time::ptime last_report_; ///< Last intermediate report time.
@@ -950,6 +952,8 @@ protected:
     std::map<uint8_t, dhcp::Pkt6Ptr> template_packets_v6_;
 
     static bool interrupted_;  ///< Is program interrupted.
+
+    CommandOptions& options_;
 };
 
 }  // namespace perfdhcp
index 73c78ee8d097edd005929b692dab2107dc10ea4f..848492bc98d1eaa6c146bab142853b5fdbd23579 100644 (file)
@@ -4,6 +4,7 @@ AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib -I$(top_builddir
 AM_CPPFLAGS += -I$(srcdir)/.. -I$(builddir)/..
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\"
 AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -Werror
 AM_CXXFLAGS = $(KEA_CXXFLAGS)
 
 if USE_STATIC_LINK
@@ -31,6 +32,9 @@ run_unittests_SOURCES += rate_control_unittest.cc
 run_unittests_SOURCES += stats_mgr_unittest.cc
 run_unittests_SOURCES += test_control_unittest.cc
 run_unittests_SOURCES += receiver_unittest.cc
+run_unittests_SOURCES += perf_socket_unittest.cc
+run_unittests_SOURCES += basic_scen_unittest.cc
+run_unittests_SOURCES += avalanche_scen_unittest.cc
 run_unittests_SOURCES += command_options_helper.h
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
diff --git a/src/bin/perfdhcp/tests/avalanche_scen_unittest.cc b/src/bin/perfdhcp/tests/avalanche_scen_unittest.cc
new file mode 100644 (file)
index 0000000..de8e543
--- /dev/null
@@ -0,0 +1,254 @@
+// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include "command_options_helper.h"
+#include "../avalanche_scen.h"
+
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/iface_mgr.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/foreach.hpp>
+
+#include <algorithm>
+#include <cstddef>
+#include <stdint.h>
+#include <string>
+#include <fstream>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace boost::posix_time;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+/// \brief FakeScenPerfSocket class that mocks PerfSocket.
+///
+/// It stubs send and receive operations and collects statistics.
+/// Beside that it simulates DHCP server responses for received
+/// packets.
+class FakeScenPerfSocket: public BasePerfSocket {
+public:
+    /// \brief Default constructor for FakeScenPerfSocket.
+    FakeScenPerfSocket(CommandOptions &opt) :
+        opt_(opt),
+        iface_(boost::make_shared<Iface>("fake", 0)),
+        sent_cnt_(0),
+        recv_cnt_(0) {};
+
+    CommandOptions &opt_;
+
+    IfacePtr iface_;  ///< Local fake interface.
+
+    int sent_cnt_;  ///< Counter of sent packets.
+    int recv_cnt_;  ///< Counter of received packets.
+
+    /// List of pairs <msg_type, trans_id> containing responses
+    /// planned to send to perfdhcp.
+    std::list<std::tuple<uint8_t, uint32_t>> planned_responses_;
+
+    /// \brief Simulate receiving DHCPv4 packet.
+    virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) override {
+        (void)timeout_sec; // silence compile 'unused parameter' warning;
+        (void)timeout_usec; // silence compile 'unused parameter' warning;
+        recv_cnt_++;
+
+        if (planned_responses_.empty()) {
+            return Pkt4Ptr();
+        }
+        auto msg = planned_responses_.front();
+        planned_responses_.pop_front();
+        auto msg_type = std::get<0>(msg);
+        Pkt4Ptr pkt(new Pkt4(msg_type, std::get<1>(msg)));
+        OptionPtr opt_serverid = Option::factory(Option::V4,
+                                                 DHO_DHCP_SERVER_IDENTIFIER,
+                                                 OptionBuffer(4, 1));
+        pkt->setYiaddr(asiolink::IOAddress("127.0.0.1"));
+        pkt->addOption(opt_serverid);
+        pkt->updateTimestamp();
+        return (pkt);
+    };
+
+    /// \brief Simulate receiving DHCPv6 packet.
+    virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) override {
+        (void)timeout_sec; // silence compile 'unused parameter' warning;
+        (void)timeout_usec; // silence compile 'unused parameter' warning;
+        recv_cnt_++;
+
+        if (planned_responses_.empty()) {
+            return Pkt6Ptr();
+        }
+        auto msg = planned_responses_.front();
+        planned_responses_.pop_front();
+        auto msg_type = std::get<0>(msg);
+        Pkt6Ptr pkt(new Pkt6(msg_type, std::get<1>(msg)));
+        // Add IA_NA if requested by the client.
+        if (opt_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) {
+            OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
+            pkt->addOption(opt_ia_na);
+        }
+        // Add IA_PD if requested by the client.
+        if (opt_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) {
+            OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD);
+            pkt->addOption(opt_ia_pd);
+        }
+        OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
+        std::vector<uint8_t> duid({0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
+        OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
+        pkt->addOption(opt_serverid);
+        pkt->addOption(opt_clientid);
+        pkt->updateTimestamp();
+        return (pkt);
+    };
+
+    /// \brief Simulate sending DHCPv4 packet.
+    virtual bool send(const dhcp::Pkt4Ptr& pkt) override {
+        sent_cnt_++;
+        pkt->updateTimestamp();
+        if (pkt->getType() == DHCPDISCOVER) {
+            planned_responses_.push_back(std::make_tuple(DHCPOFFER, pkt->getTransid()));
+        } else if (pkt->getType() == DHCPREQUEST) {
+            planned_responses_.push_back(std::make_tuple(DHCPACK, pkt->getTransid()));
+        } else {
+            assert(0);
+        }
+        return true;
+    };
+
+    /// \brief Simulate sending DHCPv6 packet.
+    virtual bool send(const dhcp::Pkt6Ptr& pkt) override {
+        sent_cnt_++;
+        pkt->updateTimestamp();
+        if (pkt->getType() == DHCPV6_SOLICIT) {
+            planned_responses_.push_back(std::make_tuple(DHCPV6_ADVERTISE, pkt->getTransid()));
+        } else if (pkt->getType() == DHCPV6_REQUEST) {
+            planned_responses_.push_back(std::make_tuple(DHCPV6_REPLY, pkt->getTransid()));
+        } else {
+            assert(0);
+        }
+        return true;
+    };
+
+    /// \brief Override getting interface.
+    virtual IfacePtr getIface() override { return iface_; }
+
+    void reset() {
+        sent_cnt_ = 0;
+        recv_cnt_ = 0;
+    }
+};
+
+
+/// \brief NakedAvalancheScen class.
+///
+/// It exposes AvalancheScen internals for UT.
+class NakedAvalancheScen: public AvalancheScen {
+public:
+    using AvalancheScen::tc_;
+
+    FakeScenPerfSocket fake_sock_;
+
+    NakedAvalancheScen(CommandOptions &opt) : AvalancheScen(opt, fake_sock_), fake_sock_(opt) {};
+
+};
+
+
+/// \brief Test Fixture Class
+///
+/// This test fixture class is used to perform
+/// unit tests on perfdhcp AvalancheScenTest class.
+class AvalancheScenTest : public virtual ::testing::Test
+{
+public:
+    AvalancheScenTest() { }
+
+    /// \brief Parse command line string with CommandOptions.
+    ///
+    /// \param cmdline command line string to be parsed.
+    /// \throw isc::Unexpected if unexpected error occurred.
+    /// \throw isc::InvalidParameter if command line is invalid.
+    void processCmdLine(CommandOptions &opt, const std::string& cmdline) const {
+        CommandOptionsHelper::process(opt, cmdline);
+    }
+
+    /// \brief Get full path to a file in testdata directory.
+    ///
+    /// \param filename filename being appended to absolute
+    /// path to testdata directory
+    ///
+    /// \return full path to a file in testdata directory.
+    std::string getFullPath(const std::string& filename) const {
+        std::ostringstream stream;
+        stream << TEST_DATA_DIR << "/" << filename;
+        return (stream.str());
+    }
+};
+
+
+TEST_F(AvalancheScenTest, Packet4Exchange) {
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l fake -4 -R 10 --scenario avalanche -g single 127.0.0.1");
+    NakedAvalancheScen as(opt);
+
+    as.run();
+
+    EXPECT_EQ(as.fake_sock_.sent_cnt_, 20); // Discovery + Request
+    EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 10);
+    EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 10);
+    EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 10);
+    EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 10);
+}
+
+
+TEST_F(AvalancheScenTest, Packet4ExchangeOnlyDO) {
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l fake -4 -R 10 -i --scenario avalanche -g single 127.0.0.1");
+    NakedAvalancheScen as(opt);
+
+    as.run();
+
+    EXPECT_EQ(as.fake_sock_.sent_cnt_, 10); // Discovery + Request
+    EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 10);
+    EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 10);
+    EXPECT_EQ(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 0);
+    EXPECT_EQ(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 0);
+}
+
+
+TEST_F(AvalancheScenTest, Packet6Exchange) {
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l fake -6 -R 10 --scenario avalanche -g single -R 20 -L 10547 ::1");
+    NakedAvalancheScen as(opt);
+
+    as.run();
+
+    EXPECT_GE(as.fake_sock_.sent_cnt_, 20); // Solicit + Request
+    EXPECT_GE(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 10);
+    EXPECT_GE(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 10);
+    EXPECT_GE(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 10);
+    EXPECT_GE(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 10);
+}
+
+
+TEST_F(AvalancheScenTest, Packet6ExchangeOnlySA) {
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l fake -6 -R 10 -i --scenario avalanche -g single -R 20 -L 10547 ::1");
+    NakedAvalancheScen as(opt);
+
+    as.run();
+
+    EXPECT_GE(as.fake_sock_.sent_cnt_, 10); // Solicit + Request
+    EXPECT_GE(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 10);
+    EXPECT_GE(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 10);
+    EXPECT_GE(as.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 0);
+    EXPECT_GE(as.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 0);
+}
diff --git a/src/bin/perfdhcp/tests/basic_scen_unittest.cc b/src/bin/perfdhcp/tests/basic_scen_unittest.cc
new file mode 100644 (file)
index 0000000..a3f2f55
--- /dev/null
@@ -0,0 +1,321 @@
+// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include "command_options_helper.h"
+#include "../basic_scen.h"
+
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/iface_mgr.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/foreach.hpp>
+
+#include <algorithm>
+#include <cstddef>
+#include <stdint.h>
+#include <string>
+#include <fstream>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace boost::posix_time;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+/// \brief FakeScenPerfSocket class that mocks PerfSocket.
+///
+/// It stubs send and receive operations and collects statistics.
+/// Beside that it simulates DHCP server responses for received
+/// packets.
+class FakeScenPerfSocket: public BasePerfSocket {
+public:
+    /// \brief Default constructor for FakeScenPerfSocket.
+    FakeScenPerfSocket(CommandOptions &opt) :
+        opt_(opt),
+        iface_(boost::make_shared<Iface>("fake", 0)),
+        sent_cnt_(0),
+        recv_cnt_(0),
+        start_dropping_after_cnt_(100000) {};
+
+    CommandOptions &opt_;
+
+    IfacePtr iface_;  ///< Local fake interface.
+
+    int sent_cnt_;  ///< Counter of sent packets.
+    int recv_cnt_;  ///< Counter of received packets.
+
+    /// List of pairs <msg_type, trans_id> containing responses
+    /// planned to send to perfdhcp.
+    std::list<std::tuple<uint8_t, uint32_t>> planned_responses_;
+
+    /// Limit for sent packets. After this limit not more packets
+    /// are sent. This simulate dropping responses.
+    int start_dropping_after_cnt_;
+
+    /// \brief Simulate receiving DHCPv4 packet.
+    virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) override {
+        (void)timeout_sec; // silence compile 'unused parameter' warning;
+        (void)timeout_usec; // silence compile 'unused parameter' warning;
+        recv_cnt_++;
+
+        if (planned_responses_.empty() || sent_cnt_ >= start_dropping_after_cnt_) {
+            return Pkt4Ptr();
+        }
+        auto msg = planned_responses_.front();
+        planned_responses_.pop_front();
+        auto msg_type = std::get<0>(msg);
+        Pkt4Ptr pkt(new Pkt4(msg_type, std::get<1>(msg)));
+        OptionPtr opt_serverid = Option::factory(Option::V4,
+                                                 DHO_DHCP_SERVER_IDENTIFIER,
+                                                 OptionBuffer(4, 1));
+        pkt->setYiaddr(asiolink::IOAddress("127.0.0.1"));
+        pkt->addOption(opt_serverid);
+        pkt->updateTimestamp();
+        return (pkt);
+    };
+
+    /// \brief Simulate receiving DHCPv6 packet.
+    virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) override {
+        (void)timeout_sec; // silence compile 'unused parameter' warning;
+        (void)timeout_usec; // silence compile 'unused parameter' warning;
+        recv_cnt_++;
+
+        if (planned_responses_.empty() || sent_cnt_ >= start_dropping_after_cnt_) {
+            return Pkt6Ptr();
+        }
+        auto msg = planned_responses_.front();
+        planned_responses_.pop_front();
+        auto msg_type = std::get<0>(msg);
+        Pkt6Ptr pkt(new Pkt6(msg_type, std::get<1>(msg)));
+        // Add IA_NA if requested by the client.
+        if (opt_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) {
+            OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
+            pkt->addOption(opt_ia_na);
+        }
+        // Add IA_PD if requested by the client.
+        if (opt_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) {
+            OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD);
+            pkt->addOption(opt_ia_pd);
+        }
+        OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
+        std::vector<uint8_t> duid({0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
+        OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
+        pkt->addOption(opt_serverid);
+        pkt->addOption(opt_clientid);
+        pkt->updateTimestamp();
+        return (pkt);
+    };
+
+    /// \brief Simulate sending DHCPv4 packet.
+    virtual bool send(const dhcp::Pkt4Ptr& pkt) override {
+        sent_cnt_++;
+        pkt->updateTimestamp();
+        if (sent_cnt_ >= start_dropping_after_cnt_) {
+            return true;
+        }
+        if (pkt->getType() == DHCPDISCOVER) {
+            planned_responses_.push_back(std::make_tuple(DHCPOFFER, pkt->getTransid()));
+        } else if (pkt->getType() == DHCPREQUEST) {
+            planned_responses_.push_back(std::make_tuple(DHCPACK, pkt->getTransid()));
+        } else {
+            assert(0);
+        }
+        return true;
+    };
+
+    /// \brief Simulate sending DHCPv6 packet.
+    virtual bool send(const dhcp::Pkt6Ptr& pkt) override {
+        sent_cnt_++;
+        pkt->updateTimestamp();
+        if (sent_cnt_ >= start_dropping_after_cnt_) {
+            return true;
+        }
+        if (pkt->getType() == DHCPV6_SOLICIT) {
+            planned_responses_.push_back(std::make_tuple(DHCPV6_ADVERTISE, pkt->getTransid()));
+        } else if (pkt->getType() == DHCPV6_REQUEST) {
+            planned_responses_.push_back(std::make_tuple(DHCPV6_REPLY, pkt->getTransid()));
+        } else {
+            assert(0);
+        }
+        return true;
+    };
+
+    /// \brief Override getting interface.
+    virtual IfacePtr getIface() override { return iface_; }
+
+    void reset() {
+        sent_cnt_ = 0;
+        recv_cnt_ = 0;
+    }
+};
+
+
+/// \brief NakedBasicScen class.
+///
+/// It exposes BasicScen internals for UT.
+class NakedBasicScen: public BasicScen {
+public:
+    using BasicScen::basic_rate_control_;
+    using BasicScen::renew_rate_control_;
+    using BasicScen::release_rate_control_;
+    using BasicScen::tc_;
+
+    FakeScenPerfSocket fake_sock_;
+
+    NakedBasicScen(CommandOptions &opt) : BasicScen(opt, fake_sock_), fake_sock_(opt) {};
+
+};
+
+
+/// \brief Test Fixture Class
+///
+/// This test fixture class is used to perform
+/// unit tests on perfdhcp BasicScenTest class.
+class BasicScenTest : public virtual ::testing::Test
+{
+public:
+    BasicScenTest() { }
+
+    /// \brief Parse command line string with CommandOptions.
+    ///
+    /// \param cmdline command line string to be parsed.
+    /// \throw isc::Unexpected if unexpected error occurred.
+    /// \throw isc::InvalidParameter if command line is invalid.
+    void processCmdLine(CommandOptions &opt, const std::string& cmdline) const {
+        CommandOptionsHelper::process(opt, cmdline);
+    }
+
+    /// \brief Get full path to a file in testdata directory.
+    ///
+    /// \param filename filename being appended to absolute
+    /// path to testdata directory
+    ///
+    /// \return full path to a file in testdata directory.
+    std::string getFullPath(const std::string& filename) const {
+        std::ostringstream stream;
+        stream << TEST_DATA_DIR << "/" << filename;
+        return (stream.str());
+    }
+};
+
+
+// This test verifies that the class members are reset to expected values.
+TEST_F(BasicScenTest, initial_settings) {
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -6 -l ethx -r 50 -f 30 -F 10 all");
+    NakedBasicScen bs(opt);
+
+    EXPECT_EQ(50, bs.basic_rate_control_.getRate());
+    EXPECT_EQ(30, bs.renew_rate_control_.getRate());
+    EXPECT_EQ(10, bs.release_rate_control_.getRate());
+}
+
+
+TEST_F(BasicScenTest, Packet4Exchange) {
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l fake -r 100 -n 10 -g single 127.0.0.1");
+    NakedBasicScen bs(opt);
+    bs.run();
+    // The command line restricts the number of iterations to 10
+    // with -n 10 parameter.
+    EXPECT_GE(bs.fake_sock_.sent_cnt_, 20); // Discovery + Request
+    EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 10);
+    EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 10);
+    EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 10);
+    EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 10);
+}
+
+
+TEST_F(BasicScenTest, Packet4ExchangeMaxDrop10Proc) {
+    CommandOptions opt;
+
+    // With the following command line we restrict the maximum
+    // number of dropped packets to 10% of all.
+    // Use templates for this test.
+    processCmdLine(opt, "perfdhcp -l fake -r 100 -R 20 -n 100"
+                   " -D 10% -L 10547  -g single"
+                   // TODO: seems to be broken as it crashes building pkt
+                   // " -T " + getFullPath("discover-example.hex")
+                   // + " -T " + getFullPath("request4-example.hex")
+                   " 127.0.0.1");
+    // The number iterations is restricted by the percentage of
+    // dropped packets (-D 10%).
+    NakedBasicScen bs(opt);
+    bs.fake_sock_.start_dropping_after_cnt_ = 10;
+    bs.run();
+    EXPECT_GE(bs.fake_sock_.sent_cnt_, 15); // Discovery + Request
+    EXPECT_LE(bs.fake_sock_.sent_cnt_, 20); // Discovery + Request
+
+    EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 1);
+    EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::DO), 15);
+
+    EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 1);
+    EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::DO), 15);
+
+    EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 1);
+    EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RA), 15);
+
+    EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 1);
+    EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RA), 15);
+}
+
+
+TEST_F(BasicScenTest, Packet6Exchange) {
+    // Set number of iterations to 10.
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l fake -6 -r 100 -n 10 -g single -R 20 -L 10547 ::1");
+    // Set number of received packets equal to number of iterations.
+    // This simulates no packet drops.
+    NakedBasicScen bs(opt);
+    bs.run();
+    EXPECT_GE(bs.fake_sock_.sent_cnt_, 20); // Solicit + Request
+    EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 10);
+    EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 10);
+    EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 10);
+    EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 10);
+}
+
+TEST_F(BasicScenTest, Packet6ExchangeMaxDrop3Pkt) {
+    CommandOptions opt;
+    // The maximum number of dropped packets is 3 (because of -D 3).
+    processCmdLine(opt, "perfdhcp -l fake"
+                   " -6 -r 100 -n 100 -R 20 -D 3 -L 10547"
+                   // TODO: seems to be broken as it crashes building pkt
+                   // " -T " + getFullPath("solicit-example.hex")
+                   // + " -T " + getFullPath("request6-example.hex")
+                   " ::1");
+
+    // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges.
+    // The test function generates server's responses and passes it to the
+    // TestControl class methods for processing. The number of exchanges
+    // actually performed is controller by 'start_dropping_after_cnt_'.
+    // All exchanged packets carry the IA_NA option
+    // to simulate the IPv6 address acquisition and to verify that the
+    // IA_NA options returned by the server are processed correctly.
+    NakedBasicScen bs(opt);
+    bs.fake_sock_.start_dropping_after_cnt_ = 10;
+    bs.run();
+    EXPECT_GE(bs.fake_sock_.sent_cnt_, 10);
+    EXPECT_LE(bs.fake_sock_.sent_cnt_, 20);
+
+    EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 1);
+    EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::SA), 15);
+
+    EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 1);
+    EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::SA), 15);
+
+    EXPECT_GE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 1);
+    EXPECT_LE(bs.tc_.getStatsMgr().getSentPacketsNum(ExchangeType::RR), 15);
+
+    EXPECT_GE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 1);
+    EXPECT_LE(bs.tc_.getStatsMgr().getRcvdPacketsNum(ExchangeType::RR), 15);
+}
index 7469076f362edf80efffe15163827fcf9fd32445..2184240b9c000ac0186b8ae35a8beebe67583ae8 100644 (file)
@@ -84,8 +84,7 @@ public:
     ///
     /// \param cmdline command line provided as single string.
     /// \return true if program has been run in help or version mode ('h' or 'v' flag).
-    static bool process(const std::string& cmdline) {
-        CommandOptions& opt = CommandOptions::instance();
+    static bool process(CommandOptions& opt, const std::string& cmdline) {
         int argc = 0;
         char** argv = tokenizeString(cmdline, argc);
         ArgvPtr args(argv, argc);
index 148bbb3d3d8411de8f6e6e7628e97f1b9dc3993c..3b0bc79b040e6d3830acee6193bb3cdb29ae1814 100644 (file)
@@ -149,8 +149,8 @@ protected:
     /// \param cmdline Command line to parse.
     /// \throws std::bad allocation if tokenization failed.
     /// \return true if program has been run in help or version mode ('h' or 'v' flag).
-    bool process(const std::string& cmdline) {
-        return (CommandOptionsHelper::process(cmdline));
+    bool process(CommandOptions& opt, const std::string& cmdline) {
+        return (CommandOptionsHelper::process(opt, cmdline));
     }
 
     /// \brief Get full path to a file in testdata directory.
@@ -164,314 +164,306 @@ protected:
         stream << TEST_DATA_DIR << "/" << filename;
         return (stream.str());
     }
+};
+
+TEST_F(CommandOptionsTest, Defaults) {
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp 192.168.0.1"));
+    EXPECT_EQ(4, opt.getIpVersion());
+    EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode());
+    EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS));
+    EXPECT_EQ(0, opt.getRate());
+    EXPECT_EQ(0, opt.getRenewRate());
+    EXPECT_EQ(0, opt.getReleaseRate());
+    EXPECT_EQ(0, opt.getReportDelay());
+    EXPECT_EQ(0, opt.getClientsNum());
 
+    // default mac
+    const uint8_t mac[6] = { 0x00, 0x0C, 0x01, 0x02, 0x03, 0x04 };
+    std::vector<uint8_t> v1 = opt.getMacTemplate();
+    ASSERT_EQ(6, v1.size());
+    EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
 
-    /// \brief Check default initialized values
-    ///
-    /// Check if initialized values are correct
-    void checkDefaults() {
-        CommandOptions& opt = CommandOptions::instance();
-        EXPECT_NO_THROW(process("perfdhcp 192.168.0.1"));
-        EXPECT_EQ(4, opt.getIpVersion());
-        EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode());
-        EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS));
-        EXPECT_EQ(0, opt.getRate());
-        EXPECT_EQ(0, opt.getRenewRate());
-        EXPECT_EQ(0, opt.getReleaseRate());
-        EXPECT_EQ(0, opt.getReportDelay());
-        EXPECT_EQ(0, opt.getClientsNum());
-
-        // default mac
-        const uint8_t mac[6] = { 0x00, 0x0C, 0x01, 0x02, 0x03, 0x04 };
-        std::vector<uint8_t> v1 = opt.getMacTemplate();
-        ASSERT_EQ(6, v1.size());
-        EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
-
-        // Check if DUID is initialized. The DUID-LLT is expected
-        // to start with DUID_LLT value of 1 and hardware ethernet
-        // type equal to 1 (HWETHER_TYPE).
-        const uint8_t duid_llt_and_hw[4] = { 0x0, 0x1, 0x0, 0x1 };
-        // We assume DUID-LLT length 14. This includes 4 octets of
-        // DUID_LLT value, two octets of hardware type, 4 octets
-        // of time value and 6 octets of variable link layer (MAC)
-        // address.
-        const int duid_llt_size = 14;
-        // DUID is not given from the command line but it is supposed
-        // to be initialized by the CommandOptions private method
-        // generateDuidTemplate().
-        std::vector<uint8_t> v2 = opt.getDuidTemplate();
-        ASSERT_EQ(duid_llt_size, opt.getDuidTemplate().size());
-        EXPECT_TRUE(std::equal(v2.begin(), v2.begin() + 4,
-                               duid_llt_and_hw));
-        // Check time field contents.
-        ptime now = microsec_clock::universal_time();
-        ptime duid_epoch(from_iso_string("20000101T000000"));
-        time_period period(duid_epoch, now);
-        uint32_t duration_sec = period.length().total_seconds();
-        // Read time from the template generated.
-        uint32_t duration_from_template = 0;
-        memcpy(&duration_from_template, &v2[4], 4);
-        duration_from_template = htonl(duration_from_template);
-        // In special cases, we may have overflow in time field
-        // so we give ourselves the margin of 10 seconds here.
-        // If time value has been set more then 10 seconds back
-        // it is safe to compare it with the time value generated
-        // from now.
-        if (duration_from_template > 10) {
-            EXPECT_GE(duration_sec, duration_from_template);
-        }
-
-        EXPECT_EQ(0, opt.getBase().size());
-        EXPECT_EQ(0, opt.getNumRequests().size());
-        EXPECT_EQ(0, opt.getPeriod());
-        for (size_t i = 0; i < opt.getDropTime().size(); ++i) {
-            EXPECT_DOUBLE_EQ(1, opt.getDropTime()[i]);
-        }
-        ASSERT_EQ(opt.getMaxDrop().size(), opt.getMaxDropPercentage().size());
-        for (size_t i = 0; i < opt.getMaxDrop().size(); ++i) {
-            EXPECT_EQ(0, opt.getMaxDrop()[i]);
-            EXPECT_EQ(0, opt.getMaxDropPercentage()[i]);
-        }
-        EXPECT_EQ("", opt.getLocalName());
-        EXPECT_FALSE(opt.isInterface());
-        EXPECT_EQ(0, opt.getPreload());
-        EXPECT_EQ(0, opt.getLocalPort());
-        EXPECT_FALSE(opt.isSeeded());
-        EXPECT_EQ(0, opt.getSeed());
-        EXPECT_FALSE(opt.isBroadcast());
-        EXPECT_FALSE(opt.isRapidCommit());
-        EXPECT_FALSE(opt.isUseFirst());
-        EXPECT_EQ(0, opt.getTemplateFiles().size());
-        EXPECT_EQ(0, opt.getTransactionIdOffset().size());
-        EXPECT_EQ(0, opt.getRandomOffset().size());
-        EXPECT_GT(0, opt.getElapsedTimeOffset());
-        EXPECT_GT(0, opt.getServerIdOffset());
-        EXPECT_GT(0, opt.getRequestedIpOffset());
-        EXPECT_EQ("", opt.getDiags());
-        EXPECT_EQ("", opt.getWrapped());
-        EXPECT_EQ("192.168.0.1", opt.getServerName());
+    // Check if DUID is initialized. The DUID-LLT is expected
+    // to start with DUID_LLT value of 1 and hardware ethernet
+    // type equal to 1 (HWETHER_TYPE).
+    const uint8_t duid_llt_and_hw[4] = { 0x0, 0x1, 0x0, 0x1 };
+    // We assume DUID-LLT length 14. This includes 4 octets of
+    // DUID_LLT value, two octets of hardware type, 4 octets
+    // of time value and 6 octets of variable link layer (MAC)
+    // address.
+    const int duid_llt_size = 14;
+    // DUID is not given from the command line but it is supposed
+    // to be initialized by the CommandOptions private method
+    // generateDuidTemplate().
+    std::vector<uint8_t> v2 = opt.getDuidTemplate();
+    ASSERT_EQ(duid_llt_size, opt.getDuidTemplate().size());
+    EXPECT_TRUE(std::equal(v2.begin(), v2.begin() + 4,
+                           duid_llt_and_hw));
+    // Check time field contents.
+    ptime now = microsec_clock::universal_time();
+    ptime duid_epoch(from_iso_string("20000101T000000"));
+    time_period period(duid_epoch, now);
+    uint32_t duration_sec = period.length().total_seconds();
+    // Read time from the template generated.
+    uint32_t duration_from_template = 0;
+    memcpy(&duration_from_template, &v2[4], 4);
+    duration_from_template = htonl(duration_from_template);
+    // In special cases, we may have overflow in time field
+    // so we give ourselves the margin of 10 seconds here.
+    // If time value has been set more then 10 seconds back
+    // it is safe to compare it with the time value generated
+    // from now.
+    if (duration_from_template > 10) {
+        EXPECT_GE(duration_sec, duration_from_template);
     }
-};
 
-TEST_F(CommandOptionsTest, Defaults) {
-    EXPECT_NO_THROW(process("perfdhcp all"));
-    checkDefaults();
+    EXPECT_EQ(0, opt.getBase().size());
+    EXPECT_EQ(0, opt.getNumRequests().size());
+    EXPECT_EQ(0, opt.getPeriod());
+    for (size_t i = 0; i < opt.getDropTime().size(); ++i) {
+        EXPECT_DOUBLE_EQ(1, opt.getDropTime()[i]);
+    }
+    ASSERT_EQ(opt.getMaxDrop().size(), opt.getMaxDropPercentage().size());
+    for (size_t i = 0; i < opt.getMaxDrop().size(); ++i) {
+        EXPECT_EQ(0, opt.getMaxDrop()[i]);
+        EXPECT_EQ(0, opt.getMaxDropPercentage()[i]);
+    }
+    EXPECT_EQ("", opt.getLocalName());
+    EXPECT_FALSE(opt.isInterface());
+    EXPECT_EQ(0, opt.getPreload());
+    EXPECT_EQ(0, opt.getLocalPort());
+    EXPECT_FALSE(opt.isSeeded());
+    EXPECT_EQ(0, opt.getSeed());
+    EXPECT_FALSE(opt.isBroadcast());
+    EXPECT_FALSE(opt.isRapidCommit());
+    EXPECT_FALSE(opt.isUseFirst());
+    EXPECT_EQ(0, opt.getTemplateFiles().size());
+    EXPECT_EQ(0, opt.getTransactionIdOffset().size());
+    EXPECT_EQ(0, opt.getRandomOffset().size());
+    EXPECT_GT(0, opt.getElapsedTimeOffset());
+    EXPECT_GT(0, opt.getServerIdOffset());
+    EXPECT_GT(0, opt.getRequestedIpOffset());
+    EXPECT_EQ("", opt.getDiags());
+    EXPECT_EQ("", opt.getWrapped());
+    EXPECT_EQ("192.168.0.1", opt.getServerName());
 }
 
 TEST_F(CommandOptionsTest, HelpVersion) {
     // The parser is supposed to return true if 'h' or 'v' options
     // are specified.
-    EXPECT_TRUE(process("perfdhcp -h"));
-    EXPECT_TRUE(process("perfdhcp -v"));
-    EXPECT_TRUE(process("perfdhcp -h -v"));
-    EXPECT_TRUE(process("perfdhcp -6 -l ethx -h all"));
-    EXPECT_TRUE(process("perfdhcp -l ethx -v all"));
+    CommandOptions opt;
+    EXPECT_TRUE(process(opt, "perfdhcp -h"));
+    EXPECT_TRUE(process(opt, "perfdhcp -v"));
+    EXPECT_TRUE(process(opt, "perfdhcp -h -v"));
+    EXPECT_TRUE(process(opt, "perfdhcp -6 -l ethx -h all"));
+    EXPECT_TRUE(process(opt, "perfdhcp -l ethx -v all"));
     // No 'h' or 'v' option specified. The false value
     // should be returned.
-    EXPECT_FALSE(process("perfdhcp -l ethx all"));
+    EXPECT_FALSE(process(opt, "perfdhcp -l ethx all"));
 }
 
 TEST_F(CommandOptionsTest, UseFirst) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -1 -B -l ethx all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -1 -B -l ethx all"));
     EXPECT_TRUE(opt.isUseFirst());
 }
 
 TEST_F(CommandOptionsTest, UseRelayV6) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -6 -A1 -l ethx all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -6 -A1 -l ethx all"));
     EXPECT_TRUE(opt.isUseRelayedV6());
     // -4 and -A must not coexist
-    EXPECT_THROW(process("perfdhcp -4 -A1 -l ethx all"), isc::InvalidParameter);
+    EXPECT_THROW(process(opt, "perfdhcp -4 -A1 -l ethx all"), isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, IpVersion) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -6 -l ethx -c -i all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -6 -l ethx -c -i all"));
     EXPECT_EQ(6, opt.getIpVersion());
     EXPECT_EQ("ethx", opt.getLocalName());
     EXPECT_TRUE(opt.isRapidCommit());
     EXPECT_FALSE(opt.isBroadcast());
-    process("perfdhcp -4 -B -l ethx all");
+    process(opt, "perfdhcp -4 -B -l ethx all");
     EXPECT_EQ(4, opt.getIpVersion());
     EXPECT_TRUE(opt.isBroadcast());
     EXPECT_FALSE(opt.isRapidCommit());
 
     // Negative test cases
     // -4 and -6 must not coexist
-    EXPECT_THROW(process("perfdhcp -4 -6 -l ethx all"), isc::InvalidParameter);
+    EXPECT_THROW(process(opt, "perfdhcp -4 -6 -l ethx all"), isc::InvalidParameter);
     // -6 and -B must not coexist
-    EXPECT_THROW(process("perfdhcp -6 -B -l ethx all"), isc::InvalidParameter);
+    EXPECT_THROW(process(opt, "perfdhcp -6 -B -l ethx all"), isc::InvalidParameter);
     // -c and -4 (default) must not coexist
-    EXPECT_THROW(process("perfdhcp -c -l ethx all"), isc::InvalidParameter);
+    EXPECT_THROW(process(opt, "perfdhcp -c -l ethx all"), isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, LeaseType) {
-    CommandOptions& opt = CommandOptions::instance();
+    CommandOptions opt;
     // Check that the -e address-only works for IPv6.
-    ASSERT_NO_THROW(process("perfdhcp -6 -l etx -e address-only all"));
+    ASSERT_NO_THROW(process(opt, "perfdhcp -6 -l etx -e address-only all"));
     EXPECT_EQ(6, opt.getIpVersion());
     EXPECT_EQ("etx", opt.getLocalName());
     EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS));
     // Check that the -e address-only works for IPv4.
-    ASSERT_NO_THROW(process("perfdhcp -4 -l etx -e address-only all"));
+    ASSERT_NO_THROW(process(opt, "perfdhcp -4 -l etx -e address-only all"));
     EXPECT_EQ(4, opt.getIpVersion());
     EXPECT_EQ("etx", opt.getLocalName());
     EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::ADDRESS));
     // Check that the -e prefix-only works.
-    ASSERT_NO_THROW(process("perfdhcp -6 -l etx -e prefix-only all"));
+    ASSERT_NO_THROW(process(opt, "perfdhcp -6 -l etx -e prefix-only all"));
     EXPECT_EQ(6, opt.getIpVersion());
     EXPECT_EQ("etx", opt.getLocalName());
     EXPECT_TRUE(opt.getLeaseType().is(CommandOptions::LeaseType::PREFIX));
     // Check that -e prefix-only must not coexist with -4 option.
-    EXPECT_THROW(process("perfdhcp -4 -l ethx -e prefix-only all"),
+    EXPECT_THROW(process(opt, "perfdhcp -4 -l ethx -e prefix-only all"),
                  InvalidParameter);
     // Check that -e prefix-only must not coexist with -T options.
-    EXPECT_THROW(process("perfdhcp -6 -l ethx -e prefix-only -T file1.hex"
+    EXPECT_THROW(process(opt, "perfdhcp -6 -l ethx -e prefix-only -T file1.hex"
                          " -T file2.hex -E 4 all"), InvalidParameter);
 
 }
 
 TEST_F(CommandOptionsTest, Rate) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -4 -r 10 -l ethx all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -4 -r 10 -l ethx all"));
     EXPECT_EQ(10, opt.getRate());
 
     // Negative test cases
     // Rate must not be 0
-    EXPECT_THROW(process("perfdhcp -4 -r 0 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -4 -r 0 -l ethx all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, RenewRate) {
-    CommandOptions& opt = CommandOptions::instance();
+    CommandOptions opt;
     // If -f is specified together with -r the command line should
     // be accepted and the renew rate should be set.
-    EXPECT_NO_THROW(process("perfdhcp -6 -r 10 -f 10 -l ethx all"));
+    EXPECT_NO_THROW(process(opt, "perfdhcp -6 -r 10 -f 10 -l ethx all"));
     EXPECT_EQ(10, opt.getRenewRate());
     // Check that the release rate can be set to different value than
     // rate specified as -r<rate>. Also, swap -f and -r to make sure
     // that order doesn't matter.
-    EXPECT_NO_THROW(process("perfdhcp -6 -f 5 -r 10 -l ethx all"));
+    EXPECT_NO_THROW(process(opt, "perfdhcp -6 -f 5 -r 10 -l ethx all"));
     EXPECT_EQ(5, opt.getRenewRate());
     // Renew rate should also be accepted for DHCPv4 case.
-    EXPECT_NO_THROW(process("perfdhcp -4 -f 5 -r 10 -l ethx all"));
+    EXPECT_NO_THROW(process(opt, "perfdhcp -4 -f 5 -r 10 -l ethx all"));
     EXPECT_EQ(5, opt.getRenewRate());
     // The renew rate should not be greater than the rate.
-    EXPECT_THROW(process("perfdhcp -6 -r 10 -f 11 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -f 11 -l ethx all"),
                  isc::InvalidParameter);
     // The renew-rate of 0 is invalid.
-    EXPECT_THROW(process("perfdhcp -6 -r 10 -f 0 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -f 0 -l ethx all"),
                  isc::InvalidParameter);
     // The negative renew-rate is invalid.
-    EXPECT_THROW(process("perfdhcp -6 -r 10 -f -5 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -f -5 -l ethx all"),
                  isc::InvalidParameter);
     // If -r<rate> is not specified the -f<renew-rate> should not
     // be accepted.
-    EXPECT_THROW(process("perfdhcp -6 -f 10 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -f 10 -l ethx all"),
                  isc::InvalidParameter);
     // Renew rate should be specified.
-    EXPECT_THROW(process("perfdhcp -6 -r 10 -f -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -f -l ethx all"),
                  isc::InvalidParameter);
 
     // -f and -i are mutually exclusive
-    EXPECT_THROW(process("perfdhcp -6 -r 10 -f 10 -l ethx -i all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -f 10 -l ethx -i all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, ReleaseRate) {
-    CommandOptions& opt = CommandOptions::instance();
+    CommandOptions opt;
     // If -F is specified together with -r the command line should
     // be accepted and the release rate should be set.
-    EXPECT_NO_THROW(process("perfdhcp -6 -r 10 -F 10 -l ethx all"));
+    EXPECT_NO_THROW(process(opt, "perfdhcp -6 -r 10 -F 10 -l ethx all"));
     EXPECT_EQ(10, opt.getReleaseRate());
     // Check that the release rate can be set to different value than
     // rate specified as -r<rate>. Also, swap -F and -r to make sure
     // that order doesn't matter.
-    EXPECT_NO_THROW(process("perfdhcp -6 -F 5 -r 10 -l ethx all"));
+    EXPECT_NO_THROW(process(opt, "perfdhcp -6 -F 5 -r 10 -l ethx all"));
     EXPECT_EQ(5, opt.getReleaseRate());
     // The release rate should not be greater than the rate.
-    EXPECT_THROW(process("perfdhcp -6 -r 10 -F 11 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -F 11 -l ethx all"),
                  isc::InvalidParameter);
     // The release-rate of 0 is invalid.
-    EXPECT_THROW(process("perfdhcp -6 -r 10 -F 0 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -F 0 -l ethx all"),
                  isc::InvalidParameter);
     // The negative release-rate is invalid.
-    EXPECT_THROW(process("perfdhcp -6 -r 10 -F -5 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -F -5 -l ethx all"),
                  isc::InvalidParameter);
     // If -r<rate> is not specified the -F<release-rate> should not
     // be accepted.
-    EXPECT_THROW(process("perfdhcp -6 -F 10 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -F 10 -l ethx all"),
                  isc::InvalidParameter);
     // Currently the -F<release-rate> can be specified for IPv6 mode
     // only.
-    EXPECT_THROW(process("perfdhcp -4 -r 10 -F 10 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -4 -r 10 -F 10 -l ethx all"),
                  isc::InvalidParameter);
     // Release rate should be specified.
-    EXPECT_THROW(process("perfdhcp -6 -r 10 -F -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -F -l ethx all"),
                  isc::InvalidParameter);
 
     // -F and -i are mutually exclusive
-    EXPECT_THROW(process("perfdhcp -6 -r 10 -F 10 -l ethx -i all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -r 10 -F 10 -l ethx -i all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, ReleaseRenew) {
-    CommandOptions& opt = CommandOptions::instance();
+    CommandOptions opt;
     // It should be possible to specify the -F, -f and -r options.
-    EXPECT_NO_THROW(process("perfdhcp -6 -r 10 -F 3 -f 5 -l ethx all"));
+    EXPECT_NO_THROW(process(opt, "perfdhcp -6 -r 10 -F 3 -f 5 -l ethx all"));
     EXPECT_EQ(10, opt.getRate());
     EXPECT_EQ(3, opt.getReleaseRate());
     EXPECT_EQ(5, opt.getRenewRate());
     // It should be possible to specify the -F and -f with the values which
     // sum is equal to the rate specified as -r<rate>.
-    EXPECT_NO_THROW(process("perfdhcp -6 -r 8 -F 3 -f 5 -l ethx all"));
+    EXPECT_NO_THROW(process(opt, "perfdhcp -6 -r 8 -F 3 -f 5 -l ethx all"));
     EXPECT_EQ(8, opt.getRate());
     EXPECT_EQ(3, opt.getReleaseRate());
     EXPECT_EQ(5, opt.getRenewRate());
     // Check that the sum of the release and renew rate is not greater
     // than the rate specified as -r<rate>.
-    EXPECT_THROW(process("perfdhcp -6 -F 6 -f 5 -r 10 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -F 6 -f 5 -r 10 -l ethx all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, ReportDelay) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -t 17 -l ethx all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -t 17 -l ethx all"));
     EXPECT_EQ(17, opt.getReportDelay());
 
     // Negative test cases
     // -t must be positive integer
-    EXPECT_THROW(process("perfdhcp -t -8 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -t -8 -l ethx all"),
                  isc::InvalidParameter);
-    EXPECT_THROW(process("perfdhcp -t 0 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -t 0 -l ethx all"),
                  isc::InvalidParameter);
-    EXPECT_THROW(process("perfdhcp -t s -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -t s -l ethx all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, ClientsNum) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -R 200 -l ethx all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -R 200 -l ethx all"));
     EXPECT_EQ(200, opt.getClientsNum());
-    process("perfdhcp -R 0 -l ethx all");
+    process(opt, "perfdhcp -R 0 -l ethx all");
     EXPECT_EQ(0, opt.getClientsNum());
 
     // Negative test cases
     // Number of clients must be non-negative integer
-    EXPECT_THROW(process("perfdhcp -R -5 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -R -5 -l ethx all"),
                  isc::InvalidParameter);
-    EXPECT_THROW(process("perfdhcp -R gs -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -R gs -l ethx all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, Base) {
-    CommandOptions& opt = CommandOptions::instance();
+    CommandOptions opt;
     uint8_t mac[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60 };
     uint8_t duid[14] = {  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
                           0x01, 0x01, 0x01, 0x10, 0x11, 0x1F, 0x14 };
     // Test DUID and MAC together.
-    EXPECT_NO_THROW(process("perfdhcp -b DUID=0101010101010101010110111F14"
+    EXPECT_NO_THROW(process(opt, "perfdhcp -b DUID=0101010101010101010110111F14"
                             " -b MAC=10::20::30::40::50::60"
                             " -l 127.0.0.1 all"));
     std::vector<uint8_t> v1 = opt.getMacTemplate();
@@ -480,13 +472,13 @@ TEST_F(CommandOptionsTest, Base) {
     EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid));
     // Test valid DUID.
     EXPECT_NO_THROW(
-        process("perfdhcp -b duid=0101010101010101010110111F14 -l 127.0.0.1 all")
+        process(opt, "perfdhcp -b duid=0101010101010101010110111F14 -l 127.0.0.1 all")
     );
 
     ASSERT_EQ(sizeof(duid) / sizeof(uint8_t), v2.size());
     EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid));
     // Test mix of upper/lower case letters.
-    EXPECT_NO_THROW(process("perfdhcp -b DuiD=0101010101010101010110111F14"
+    EXPECT_NO_THROW(process(opt, "perfdhcp -b DuiD=0101010101010101010110111F14"
                             " -b Mac=10::20::30::40::50::60"
                             " -l 127.0.0.1 all"));
     v1 = opt.getMacTemplate();
@@ -494,99 +486,99 @@ TEST_F(CommandOptionsTest, Base) {
     EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
     EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid));
     // Use "ether" instead of "mac".
-    EXPECT_NO_THROW(process("perfdhcp -b ether=10::20::30::40::50::60"
+    EXPECT_NO_THROW(process(opt, "perfdhcp -b ether=10::20::30::40::50::60"
                             " -l 127.0.0.1 all"));
     v1 = opt.getMacTemplate();
     EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
     // Use "ETHER" in upper case.
-    EXPECT_NO_THROW(process("perfdhcp -b ETHER=10::20::30::40::50::60"
+    EXPECT_NO_THROW(process(opt, "perfdhcp -b ETHER=10::20::30::40::50::60"
                             " -l 127.0.0.1 all"));
     v1 = opt.getMacTemplate();
     EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
     // "t" is invalid character in DUID
-    EXPECT_THROW(process("perfdhcp -6 -l ethx -b "
+    EXPECT_THROW(process(opt, "perfdhcp -6 -l ethx -b "
                          "duid=010101010101010101t110111F14 all"),
                  isc::InvalidParameter);
     // "3x" is invalid value in MAC address
-    EXPECT_THROW(process("perfdhcp -b mac=10::2::3x::4::5::6 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -b mac=10::2::3x::4::5::6 -l ethx all"),
                  isc::InvalidParameter);
     // Base is not specified
-    EXPECT_THROW(process("perfdhcp -b -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -b -l ethx all"),
                  isc::InvalidParameter);
     // Typo: should be mac= instead of mc=
-    EXPECT_THROW(process("perfdhcp -l ethx -b mc=00:01:02:03::04:05 all"),
+    EXPECT_THROW(process(opt, "perfdhcp -l ethx -b mc=00:01:02:03::04:05 all"),
                  isc::InvalidParameter);
     // Too short DUID (< 6).
-    EXPECT_THROW(process("perfdhcp -l ethx -b duid=00010203 all"),
+    EXPECT_THROW(process(opt, "perfdhcp -l ethx -b duid=00010203 all"),
                  isc::InvalidParameter);
     // Odd number of digits.
-    EXPECT_THROW(process("perfdhcp -l ethx -b duid=000102030405060 all"),
+    EXPECT_THROW(process(opt, "perfdhcp -l ethx -b duid=000102030405060 all"),
                  isc::InvalidParameter);
     // Too short MAC (!= 6).
-    EXPECT_THROW(process("perfdhcp -l ethx -b mac=00:01:02:04 all"),
+    EXPECT_THROW(process(opt, "perfdhcp -l ethx -b mac=00:01:02:04 all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, DropTime) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -l ethx -d 12 all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -l ethx -d 12 all"));
     ASSERT_EQ(2, opt.getDropTime().size());
     EXPECT_DOUBLE_EQ(12, opt.getDropTime()[0]);
     EXPECT_DOUBLE_EQ(1, opt.getDropTime()[1]);
 
-    EXPECT_NO_THROW(process("perfdhcp -l ethx -d 2 -d 4.7 all"));
+    EXPECT_NO_THROW(process(opt, "perfdhcp -l ethx -d 2 -d 4.7 all"));
     ASSERT_EQ(2, opt.getDropTime().size());
     EXPECT_DOUBLE_EQ(2, opt.getDropTime()[0]);
     EXPECT_DOUBLE_EQ(4.7, opt.getDropTime()[1]);
 
     // Negative test cases
     // Drop time must not be negative
-    EXPECT_THROW(process("perfdhcp -l ethx -d -2 -d 4.7 all"),
+    EXPECT_THROW(process(opt, "perfdhcp -l ethx -d -2 -d 4.7 all"),
                  isc::InvalidParameter);
-    EXPECT_THROW(process("perfdhcp -l ethx -d -9.1 -d 0 all"),
+    EXPECT_THROW(process(opt, "perfdhcp -l ethx -d -9.1 -d 0 all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, TimeOffset) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -l ethx -T file1.x -T file2.x -E 4 all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -l ethx -T file1.x -T file2.x -E 4 all"));
     EXPECT_EQ(4, opt.getElapsedTimeOffset());
 
     // Negative test cases
     // Argument -E must be used with -T
-    EXPECT_THROW(process("perfdhcp -l ethx -E 3 -i all"),
+    EXPECT_THROW(process(opt, "perfdhcp -l ethx -E 3 -i all"),
                  isc::InvalidParameter);
     // Value in -E not specified
-    EXPECT_THROW(process("perfdhcp -l ethx -T file.x -E -i all"),
+    EXPECT_THROW(process(opt, "perfdhcp -l ethx -T file.x -E -i all"),
                  isc::InvalidParameter);
     // Value for -E must not be negative
-    EXPECT_THROW(process("perfdhcp -l ethx -E -3 -T file.x all"),
+    EXPECT_THROW(process(opt, "perfdhcp -l ethx -E -3 -T file.x all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, ExchangeMode) {
-    CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -l ethx -i all");
+    CommandOptions opt;
+    process(opt, "perfdhcp -l ethx -i all");
     EXPECT_EQ(CommandOptions::DO_SA, opt.getExchangeMode());
 
     // Negative test cases
     // No template file specified
-    EXPECT_THROW(process("perfdhcp -i -l ethx -X 3 all"),
+    EXPECT_THROW(process(opt, "perfdhcp -i -l ethx -X 3 all"),
                  isc::InvalidParameter);
     // Offsets can't be used in simple exchanges (-i)
-    EXPECT_THROW(process("perfdhcp -i -l ethx -O 2 -T file.x all"),
+    EXPECT_THROW(process(opt, "perfdhcp -i -l ethx -O 2 -T file.x all"),
                  isc::InvalidParameter);
-    EXPECT_THROW(process("perfdhcp -i -l ethx -E 3 -T file.x all"),
+    EXPECT_THROW(process(opt, "perfdhcp -i -l ethx -E 3 -T file.x all"),
                  isc::InvalidParameter);
-    EXPECT_THROW(process("perfdhcp -i -l ethx -S 1 -T file.x all"),
+    EXPECT_THROW(process(opt, "perfdhcp -i -l ethx -S 1 -T file.x all"),
                  isc::InvalidParameter);
-    EXPECT_THROW(process("perfdhcp -i -l ethx -I 2 -T file.x all"),
+    EXPECT_THROW(process(opt, "perfdhcp -i -l ethx -I 2 -T file.x all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, Offsets) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx "
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx "
                             "-X3 -T file1.x -T file2.x all"));
     EXPECT_EQ(2, opt.getRequestedIpOffset());
     EXPECT_EQ(5, opt.getElapsedTimeOffset());
@@ -600,163 +592,163 @@ TEST_F(CommandOptionsTest, Offsets) {
 
     // Negative test cases
     // IP offset/IA_NA offset must be positive
-    EXPECT_THROW(process("perfdhcp -6 -I 0 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -I 0 -l ethx all"),
                  isc::InvalidParameter);
-    EXPECT_THROW(process("perfdhcp -6 -I -4 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -I -4 -l ethx all"),
                  isc::InvalidParameter);
 
     // TODO - other negative cases
 }
 
 TEST_F(CommandOptionsTest, LocalPort) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -l ethx -L 2000 all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -l ethx -L 2000 all"));
     EXPECT_EQ(2000, opt.getLocalPort());
 
     // Negative test cases
     // Local port must be between 0..65535
-    EXPECT_THROW(process("perfdhcp -l ethx -L -2 all"),
+    EXPECT_THROW(process(opt, "perfdhcp -l ethx -L -2 all"),
                  isc::InvalidParameter);
-    EXPECT_THROW(process("perfdhcp -l ethx -L all"),
+    EXPECT_THROW(process(opt, "perfdhcp -l ethx -L all"),
                  isc::InvalidParameter);
-    EXPECT_THROW(process("perfdhcp -l ethx -L 65540 all"),
+    EXPECT_THROW(process(opt, "perfdhcp -l ethx -L 65540 all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, Preload) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -1 -P 3 -l ethx all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -1 -P 3 -l ethx all"));
     EXPECT_EQ(3, opt.getPreload());
 
     // Negative test cases
     // Number of preload packages must not be negative integer
-    EXPECT_THROW(process("perfdhcp -P -1 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -P -1 -l ethx all"),
                  isc::InvalidParameter);
-    EXPECT_THROW(process("perfdhcp -P -3 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -P -3 -l ethx all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, Seed) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -6 -P 2 -s 23 -l ethx all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -6 -P 2 -s 23 -l ethx all"));
     EXPECT_EQ(23, opt.getSeed());
     EXPECT_TRUE(opt.isSeeded());
 
-    EXPECT_NO_THROW(process("perfdhcp -6 -P 2 -s 0 -l ethx all"));
+    EXPECT_NO_THROW(process(opt, "perfdhcp -6 -P 2 -s 0 -l ethx all"));
     EXPECT_EQ(0, opt.getSeed());
     EXPECT_FALSE(opt.isSeeded());
 
     // Negative test cases
     // Seed must be non-negative integer
-    EXPECT_THROW(process("perfdhcp -6 -P 2 -s -5 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -P 2 -s -5 -l ethx all"),
                  isc::InvalidParameter);
-    EXPECT_THROW(process("perfdhcp -6 -P 2 -s -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -6 -P 2 -s -l ethx all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, TemplateFiles) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -T file1.x -l ethx all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -T file1.x -l ethx all"));
     ASSERT_EQ(1, opt.getTemplateFiles().size());
     EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]);
 
-    EXPECT_NO_THROW(process("perfdhcp -T file1.x -s 12 -w start -T file2.x -4 -l ethx all"));
+    EXPECT_NO_THROW(process(opt, "perfdhcp -T file1.x -s 12 -w start -T file2.x -4 -l ethx all"));
     ASSERT_EQ(2, opt.getTemplateFiles().size());
     EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]);
     EXPECT_EQ("file2.x", opt.getTemplateFiles()[1]);
 
     // Negative test cases
     // No template file specified
-    EXPECT_THROW(process("perfdhcp -s 12 -T -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -s 12 -T -l ethx all"),
                  isc::InvalidParameter);
     // Too many template files specified
-    EXPECT_THROW(process("perfdhcp -s 12 -l ethx -T file.x "
+    EXPECT_THROW(process(opt, "perfdhcp -s 12 -l ethx -T file.x "
                          "-T file.x -T file.x all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, Wrapped) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -B -w start -i -l ethx all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -B -w start -i -l ethx all"));
     EXPECT_EQ("start", opt.getWrapped());
 
     // Negative test cases
     // Missing command after -w, expected start/stop
-    EXPECT_THROW(process("perfdhcp -B -i -l ethx -w all"),
+    EXPECT_THROW(process(opt, "perfdhcp -B -i -l ethx -w all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, Diagnostics) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -l ethx -i -x asTe all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -l ethx -i -x asTe all"));
     EXPECT_EQ("asTe", opt.getDiags());
 
     // Negative test cases
     // No diagnostics string specified
-    EXPECT_THROW(process("perfdhcp -l ethx -i -x all"),
+    EXPECT_THROW(process(opt, "perfdhcp -l ethx -i -x all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, MaxDrop) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -D 25 -l ethx all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -D 25 -l ethx all"));
     EXPECT_EQ(25, opt.getMaxDrop()[0]);
-    EXPECT_NO_THROW(process("perfdhcp -D 25 -l ethx -D 15 all"));
+    EXPECT_NO_THROW(process(opt, "perfdhcp -D 25 -l ethx -D 15 all"));
     EXPECT_EQ(25, opt.getMaxDrop()[0]);
     EXPECT_EQ(15, opt.getMaxDrop()[1]);
 
-    EXPECT_NO_THROW(process("perfdhcp -D 15% -l ethx all"));
+    EXPECT_NO_THROW(process(opt, "perfdhcp -D 15% -l ethx all"));
     EXPECT_EQ(15, opt.getMaxDropPercentage()[0]);
-    EXPECT_NO_THROW(process("perfdhcp -D 15% -D25% -l ethx all"));
+    EXPECT_NO_THROW(process(opt, "perfdhcp -D 15% -D25% -l ethx all"));
     EXPECT_EQ(15, opt.getMaxDropPercentage()[0]);
     EXPECT_EQ(25, opt.getMaxDropPercentage()[1]);
-    EXPECT_NO_THROW(process("perfdhcp -D 1% -D 99% -l ethx all"));
+    EXPECT_NO_THROW(process(opt, "perfdhcp -D 1% -D 99% -l ethx all"));
     EXPECT_EQ(1, opt.getMaxDropPercentage()[0]);
     EXPECT_EQ(99, opt.getMaxDropPercentage()[1]);
 
     // Negative test cases
     // Too many -D<value> options
-    EXPECT_THROW(process("perfdhcp -D 0% -D 1 -l ethx -D 3 all"),
+    EXPECT_THROW(process(opt, "perfdhcp -D 0% -D 1 -l ethx -D 3 all"),
                  isc::InvalidParameter);
     // Too many -D<value%> options
-    EXPECT_THROW(process("perfdhcp -D 99% -D 13% -l ethx -D 10% all"),
+    EXPECT_THROW(process(opt, "perfdhcp -D 99% -D 13% -l ethx -D 10% all"),
                  isc::InvalidParameter);
     // Percentage is out of bounds
-    EXPECT_THROW(process("perfdhcp -D101% -D 13% -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -D101% -D 13% -l ethx all"),
                  isc::InvalidParameter);
-    EXPECT_THROW(process("perfdhcp -D0% -D 13% -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -D0% -D 13% -l ethx all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, NumRequest) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -n 1000 -l ethx all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -n 1000 -l ethx all"));
     EXPECT_EQ(1000, opt.getNumRequests()[0]);
-    EXPECT_NO_THROW(process("perfdhcp -n 5 -n 500 -l ethx all"));
+    EXPECT_NO_THROW(process(opt, "perfdhcp -n 5 -n 500 -l ethx all"));
     EXPECT_EQ(5, opt.getNumRequests()[0]);
     EXPECT_EQ(500, opt.getNumRequests()[1]);
 
     // Negative test cases
     // Too many -n<value> parameters, expected maximum 2
-    EXPECT_THROW(process("perfdhcp -n 1 -n 2 -l ethx -n3 all"),
+    EXPECT_THROW(process(opt, "perfdhcp -n 1 -n 2 -l ethx -n3 all"),
                  isc::InvalidParameter);
     // Num request must be positive integer
-    EXPECT_THROW(process("perfdhcp -n 1 -n -22 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -n 1 -n -22 -l ethx all"),
                  isc::InvalidParameter);
-    EXPECT_THROW(process("perfdhcp -n 0 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -n 0 -l ethx all"),
                  isc::InvalidParameter);
 }
 
 TEST_F(CommandOptionsTest, Period) {
-    CommandOptions& opt = CommandOptions::instance();
-    EXPECT_NO_THROW(process("perfdhcp -p 120 -l ethx all"));
+    CommandOptions opt;
+    EXPECT_NO_THROW(process(opt, "perfdhcp -p 120 -l ethx all"));
     EXPECT_EQ(120, opt.getPeriod());
 
     // Negative test cases
     // Test period must be positive integer
-    EXPECT_THROW(process("perfdhcp -p 0 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -p 0 -l ethx all"),
                  isc::InvalidParameter);
-    EXPECT_THROW(process("perfdhcp -p -3 -l ethx all"),
+    EXPECT_THROW(process(opt, "perfdhcp -p -3 -l ethx all"),
                  isc::InvalidParameter);
 }
 
@@ -770,7 +762,7 @@ TEST_F(CommandOptionsTest, Interface) {
     dhcp::IfaceMgr& iface_mgr = dhcp::IfaceMgr::instance();
     const dhcp::IfaceMgr::IfaceCollection& ifaces = iface_mgr.getIfaces();
     std::string iface_name;
-    CommandOptions& opt = CommandOptions::instance();
+    CommandOptions opt;
     // The local loopback interface should be available.
     // If no interface have been found for any reason we should
     // not fail this test.
@@ -778,23 +770,23 @@ TEST_F(CommandOptionsTest, Interface) {
         // Get the name of the interface we detected.
         iface_name = (*ifaces.begin())->getName();
         // Use the name in the command parser.
-        ASSERT_NO_THROW(process("perfdhcp -4 -l " + iface_name + " abc"));
+        ASSERT_NO_THROW(process(opt, "perfdhcp -4 -l " + iface_name + " abc"));
         // We expect that command parser will detect that argument
         // specified along with '-l' is the interface name.
         EXPECT_TRUE(opt.isInterface());
 
         // If neither interface nor server is specified then
         // exception is expected to be thrown.
-        EXPECT_THROW(process("perfdhcp -4"), isc::InvalidParameter);
+        EXPECT_THROW(process(opt, "perfdhcp -4"), isc::InvalidParameter);
     }
 }
 
 TEST_F(CommandOptionsTest, Server) {
-    CommandOptions& opt = CommandOptions::instance();
+    CommandOptions opt;
     // There is at least server parameter needed. If server is not
     // specified the local interface must be specified.
     // The server value equal to 'all' means use broadcast.
-    ASSERT_NO_THROW(process("perfdhcp all"));
+    ASSERT_NO_THROW(process(opt, "perfdhcp all"));
     // Once command line is parsed we expect that server name is
     // set to broadcast address because 'all' was specified.
     EXPECT_TRUE(opt.isBroadcast());
@@ -804,40 +796,41 @@ TEST_F(CommandOptionsTest, Server) {
     // When all is specified for DHCPv6 mode we expect
     // FF02::1:2 as a server name which means All DHCP
     // servers and relay agents in local network segment
-    ASSERT_NO_THROW(process("perfdhcp -6 all"));
+    ASSERT_NO_THROW(process(opt, "perfdhcp -6 all"));
     EXPECT_EQ(ALL_DHCP_RELAY_AGENTS_AND_SERVERS, opt.getServerName());
 
     // When server='servers' in DHCPv6 mode we expect
     // FF05::1:3 as server name which means All DHCP
     // servers in local network.
-    ASSERT_NO_THROW(process("perfdhcp -6 servers"));
+    ASSERT_NO_THROW(process(opt, "perfdhcp -6 servers"));
     EXPECT_EQ(ALL_DHCP_SERVERS, opt.getServerName());
 
     // If server name is neither 'all' nor 'servers'
     // the given argument value is expected to be
     // returned.
-    ASSERT_NO_THROW(process("perfdhcp -6 abc"));
+    ASSERT_NO_THROW(process(opt, "perfdhcp -6 abc"));
     EXPECT_EQ("abc", opt.getServerName());
 }
 
 TEST_F(CommandOptionsTest, LoadMacsFromFile) {
-  CommandOptions &opt = CommandOptions::instance();
+    CommandOptions opt;
 
-  std::string mac_list_full_path = getFullPath("mac-list.txt");
-  std::ostringstream cmd;
-  cmd << "perfdhcp -M " << mac_list_full_path << " abc";
-  EXPECT_NO_THROW(process(cmd.str()));
-  EXPECT_EQ(mac_list_full_path, opt.getMacListFile());
+    std::string mac_list_full_path = getFullPath("mac-list.txt");
+    std::ostringstream cmd;
+    cmd << "perfdhcp -M " << mac_list_full_path << " abc";
+    EXPECT_NO_THROW(process(opt, cmd.str()));
+    EXPECT_EQ(mac_list_full_path, opt.getMacListFile());
 
-  const CommandOptions::MacAddrsVector& m = opt.getMacsFromFile();
-  EXPECT_EQ(4, m.size());
+    const CommandOptions::MacAddrsVector& m = opt.getMacsFromFile();
+    EXPECT_EQ(4, m.size());
 }
 
 TEST_F(CommandOptionsTest, LoadMacsFromFileNegativeCases) {
-  // Negative test cases
-  // Too many -M parameters, expected only 1
-  EXPECT_THROW(process("perfdhcp -M foo -M foo1 all"), isc::InvalidParameter);
-  // -M option can't use with -b option
-  EXPECT_THROW(process("perfdhcp -M foo -b mac=1234 all"),
-               isc::InvalidParameter);
+    CommandOptions opt;
+    // Negative test cases
+    // Too many -M parameters, expected only 1
+    EXPECT_THROW(process(opt, "perfdhcp -M foo -M foo1 all"), isc::InvalidParameter);
+    // -M option can't use with -b option
+    EXPECT_THROW(process(opt, "perfdhcp -M foo -b mac=1234 all"),
+                 isc::InvalidParameter);
 }
diff --git a/src/bin/perfdhcp/tests/perf_socket_unittest.cc b/src/bin/perfdhcp/tests/perf_socket_unittest.cc
new file mode 100644 (file)
index 0000000..5db34f7
--- /dev/null
@@ -0,0 +1,56 @@
+// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include "command_options_helper.h"
+#include "../perf_socket.h"
+
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/iface_mgr.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/foreach.hpp>
+
+#include <algorithm>
+#include <cstddef>
+#include <stdint.h>
+#include <string>
+#include <fstream>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace boost::posix_time;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+
+/// \brief Test Fixture Class
+///
+/// This test fixture class is used to perform
+/// unit tests on perfdhcp PerfSocketTest class.
+class PerfSocketTest : public virtual ::testing::Test
+{
+public:
+    PerfSocketTest() { }
+};
+
+
+TEST_F(PerfSocketTest, Basic) {
+    CommandOptions opt;
+
+    // make sure we catch -6 paired with v4 address
+    CommandOptionsHelper::process(opt, "perfdhcp -l 127.0.0.1 -6 192.168.1.1");
+    EXPECT_THROW(PerfSocket sock(opt), isc::InvalidParameter);
+
+    // make sure we catch -4 paired with v6 address
+    CommandOptionsHelper::process(opt, "perfdhcp -l 127.0.0.1 -4 ff02::1:2");
+    EXPECT_THROW(PerfSocket sock(opt), isc::InvalidParameter);
+}
index 0217b8e5426310fe1a9206006e572db4b19a16f6..940505cb5e389caed57e9f9a5405b57bec68fa49 100644 (file)
@@ -17,12 +17,12 @@ using namespace isc;
 using namespace isc::perfdhcp;
 
 TEST(Receiver, singleThreaded) {
-    CommandOptionsHelper::process("perfdhcp -g single -l 127.0.0.1 all");
-    ASSERT_TRUE(CommandOptions::instance().isSingleThreaded());
+    CommandOptions opt;
+    CommandOptionsHelper::process(opt, "perfdhcp -g single -l 127.0.0.1 all");
+    ASSERT_TRUE(opt.isSingleThreaded());
 
-    PerfSocket sock(123);
-
-    Receiver receiver(sock);
+    PerfSocket socket(opt);
+    Receiver receiver(socket, opt.isSingleThreaded(), opt.getIpVersion());
 
     ASSERT_NO_THROW(receiver.start());
 
@@ -34,12 +34,12 @@ TEST(Receiver, singleThreaded) {
 
 
 TEST(Receiver, multiThreaded) {
-    CommandOptionsHelper::process("perfdhcp -g multi -l 127.0.0.1 all");
-    ASSERT_FALSE(CommandOptions::instance().isSingleThreaded());
-
-    PerfSocket sock(123);
+    CommandOptions opt;
+    CommandOptionsHelper::process(opt, "perfdhcp -g multi -l 127.0.0.1 all");
+    ASSERT_FALSE(opt.isSingleThreaded());
 
-    Receiver receiver(sock);
+    PerfSocket socket(opt);
+    Receiver receiver(socket, opt.isSingleThreaded(), opt.getIpVersion());
 
     ASSERT_NO_THROW(receiver.start());
 
index 41de29527fd5222292e14707e6bf29fa9a912587..2759a2e129ff7c6f44f6dd57ffd2eccde88c76d1 100644 (file)
@@ -6,19 +6,21 @@
 
 #include <config.h>
 
-#include <boost/shared_ptr.hpp>
+#include "command_options_helper.h"
+
+#include <perfdhcp/stats_mgr.h>
 
 #include <exceptions/exceptions.h>
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt6.h>
-#include "../stats_mgr.h"
 
 #include <gtest/gtest.h>
 
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
 
 using namespace std;
 using namespace isc;
@@ -27,9 +29,6 @@ using namespace isc::perfdhcp;
 
 namespace {
 
-typedef StatsMgr<dhcp::Pkt4> StatsMgr4;
-typedef StatsMgr<dhcp::Pkt6> StatsMgr6;
-
 const uint32_t common_transid = 123;
 
 /// @brief Number of packets to be used for testing packets collecting.
@@ -118,8 +117,8 @@ public:
     /// \param num_packets packets to be passed to Statistics Manager.
     /// \param receive simulated packets are received (if true)
     /// or sent (if false)
-    void passMultiplePackets6(const boost::shared_ptr<StatsMgr6> stats_mgr,
-                              StatsMgr6::ExchangeType xchg_type,
+    void passMultiplePackets6(const boost::shared_ptr<StatsMgr> stats_mgr,
+                              ExchangeType xchg_type,
                               const uint8_t packet_type,
                               const int num_packets,
                               const bool receive = false) {
@@ -148,30 +147,23 @@ public:
     ///
     /// \param stats_mgr Statistics Manager instance.
     /// \param delay delay in seconds between DISCOVER and OFFER packets.
-    void passDOPacketsWithDelay(const boost::shared_ptr<StatsMgr4> stats_mgr,
+    void passDOPacketsWithDelay(const boost::shared_ptr<StatsMgr> stats_mgr,
                                 unsigned int delay,
                                 uint32_t transid) {
         boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
                                                       transid));
         ASSERT_NO_THROW(
-            stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
+            stats_mgr->passSentPacket(ExchangeType::DO, sent_packet)
         );
 
-        // There is way to differentiate timestamps of two packets other than
-        // sleep for before we create another packet. Packet is using current
-        // time to update its timestamp.
-        // Sleeping for X seconds will guarantee that delay between packets
-        // will be greater than 1 second. Note that posix time value is
-        // transformed to double value and it makes it hard to determine
-        // actual value to expect.
-        std::cout << "Sleeping for " << delay << "s to test packet delays"
-                  << std::endl;
-        sleep(delay);
+        // Simulate time difference by changing time of sent packet
+        auto ts = sent_packet->getTimestamp() - boost::posix_time::seconds(delay);
+        sent_packet->setTimestamp(ts);
 
         boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
                                                       transid));
         ASSERT_NO_THROW(
-            stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
+            stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet);
         );
 
         // Calculate period between packets.
@@ -187,10 +179,11 @@ public:
     /// @param transid_index Index in the table of transaction ids which
     /// points to the transaction id to be selected for the DHCPOFFER.
     void testSendReceiveCollected(const size_t transid_index) {
-        boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
+        CommandOptions opt;
+        boost::scoped_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
         // The second parameter indicates that transactions older than
         // 2 seconds should be removed and respective packets collected.
-        stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO, 2);
+        stats_mgr->addExchangeStats(ExchangeType::DO, 2);
 
         // Transaction ids of packets to be sent. All transaction ids
         // belong to the same bucket (match the transid & 1023 hashing
@@ -209,7 +202,7 @@ public:
                 sent_packet->modifyTimestamp(-10);
             }
             ASSERT_NO_THROW(
-                stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
+                stats_mgr->passSentPacket(ExchangeType::DO, sent_packet)
             )  << "failure for transaction id " << transid[i];
         }
 
@@ -217,18 +210,18 @@ public:
         Pkt4ModifiablePtr rcvd_packet(createPacket4(DHCPOFFER,
                                                     transid[transid_index]));
         ASSERT_NO_THROW(
-            stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
+            stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet);
         );
 
         // There is exactly one case (transaction id) for which perfdhcp
         // will find a packet using ordered lookup. In this case, no
         // packets will be collected. Otherwise, for any unordered lookup
         // all packets from a bucket should be collected.
-        if (stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO) > 0) {
+        if (stats_mgr->getUnorderedLookups(ExchangeType::DO) > 0) {
             // All packets in the bucket having transaction id
             // index below TEST_COLLECTED_PKT_NUM / 2 should be removed.
             EXPECT_EQ(TEST_COLLECTED_PKT_NUM / 2,
-                      stats_mgr->getCollectedNum(StatsMgr4::XCHG_DO));
+                      stats_mgr->getCollectedNum(ExchangeType::DO));
         }
 
         // Make sure that we can still use the StatsMgr. It is possible
@@ -242,11 +235,11 @@ public:
             Pkt4ModifiablePtr rcvd_packet(createPacket4(DHCPOFFER,
                                                         transid[i] + 1));
             ASSERT_NO_THROW(
-                stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
+                stats_mgr->passSentPacket(ExchangeType::DO, sent_packet)
             ) << "failure for transaction id " << transid[i];
 
             ASSERT_NO_THROW(
-                stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
+                stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet);
             ) << "failure for transaction id " << transid[i];
         }
 
@@ -256,86 +249,89 @@ public:
         // timeout. Therefore, we have to count both received packets and
         // orphans.
         EXPECT_EQ(TEST_COLLECTED_PKT_NUM + 1,
-                  stats_mgr->getRcvdPacketsNum(StatsMgr4::XCHG_DO) +
-                  stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
+                  stats_mgr->getRcvdPacketsNum(ExchangeType::DO) +
+                  stats_mgr->getOrphans(ExchangeType::DO));
     }
 };
 
 TEST_F(StatsMgrTest, Constructor) {
-    boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
-    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
+    CommandOptions opt;
+    boost::scoped_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+    stats_mgr->addExchangeStats(ExchangeType::DO);
     EXPECT_DOUBLE_EQ(
         std::numeric_limits<double>::max(),
-        stats_mgr->getMinDelay(StatsMgr4::XCHG_DO)
+        stats_mgr->getMinDelay(ExchangeType::DO)
     );
-    EXPECT_DOUBLE_EQ(0, stats_mgr->getMaxDelay(StatsMgr4::XCHG_DO));
-    EXPECT_EQ(0, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
-    EXPECT_EQ(0, stats_mgr->getOrderedLookups(StatsMgr4::XCHG_DO));
-    EXPECT_EQ(0, stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO));
-    EXPECT_EQ(0, stats_mgr->getSentPacketsNum(StatsMgr4::XCHG_DO));
-    EXPECT_EQ(0, stats_mgr->getRcvdPacketsNum(StatsMgr4::XCHG_DO));
-    EXPECT_EQ(0, stats_mgr->getCollectedNum(StatsMgr4::XCHG_DO));
-
-    EXPECT_THROW(stats_mgr->getAvgDelay(StatsMgr4::XCHG_DO), InvalidOperation);
-    EXPECT_THROW(stats_mgr->getStdDevDelay(StatsMgr4::XCHG_DO),
+    EXPECT_DOUBLE_EQ(0, stats_mgr->getMaxDelay(ExchangeType::DO));
+    EXPECT_EQ(0, stats_mgr->getOrphans(ExchangeType::DO));
+    EXPECT_EQ(0, stats_mgr->getOrderedLookups(ExchangeType::DO));
+    EXPECT_EQ(0, stats_mgr->getUnorderedLookups(ExchangeType::DO));
+    EXPECT_EQ(0, stats_mgr->getSentPacketsNum(ExchangeType::DO));
+    EXPECT_EQ(0, stats_mgr->getRcvdPacketsNum(ExchangeType::DO));
+    EXPECT_EQ(0, stats_mgr->getCollectedNum(ExchangeType::DO));
+
+    EXPECT_THROW(stats_mgr->getAvgDelay(ExchangeType::DO), InvalidOperation);
+    EXPECT_THROW(stats_mgr->getStdDevDelay(ExchangeType::DO),
                  InvalidOperation);
-    EXPECT_THROW(stats_mgr->getAvgUnorderedLookupSetSize(StatsMgr4::XCHG_DO),
+    EXPECT_THROW(stats_mgr->getAvgUnorderedLookupSetSize(ExchangeType::DO),
                  InvalidOperation);
 }
 
 TEST_F(StatsMgrTest, Exchange) {
-    boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
+    CommandOptions opt;
+    boost::scoped_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
     boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
                                                       common_transid));
     boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
                                                       common_transid));
     // This is expected to throw because XCHG_DO was not yet
     // added to Stats Manager for tracking.
-    ASSERT_FALSE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_DO));
-    ASSERT_FALSE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_RA));
+    ASSERT_FALSE(stats_mgr->hasExchangeStats(ExchangeType::DO));
+    ASSERT_FALSE(stats_mgr->hasExchangeStats(ExchangeType::RA));
     EXPECT_THROW(
-        stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet),
+        stats_mgr->passSentPacket(ExchangeType::DO, sent_packet),
         BadValue
     );
     EXPECT_THROW(
-        stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet),
+        stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet),
         BadValue
     );
 
 
     // Adding DISCOVER-OFFER exchanges to be tracked by Stats Manager.
-    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
-    ASSERT_TRUE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_DO));
-    ASSERT_FALSE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_RA));
+    stats_mgr->addExchangeStats(ExchangeType::DO);
+    ASSERT_TRUE(stats_mgr->hasExchangeStats(ExchangeType::DO));
+    ASSERT_FALSE(stats_mgr->hasExchangeStats(ExchangeType::RA));
     // The following two attempts are expected to throw because
     // invalid exchange types are passed (XCHG_RA instead of XCHG_DO)
     EXPECT_THROW(
-        stats_mgr->passSentPacket(StatsMgr4::XCHG_RA, sent_packet),
+        stats_mgr->passSentPacket(ExchangeType::RA, sent_packet),
         BadValue
     );
     EXPECT_THROW(
-        stats_mgr->passRcvdPacket(StatsMgr4::XCHG_RA, rcvd_packet),
+        stats_mgr->passRcvdPacket(ExchangeType::RA, rcvd_packet),
         BadValue
     );
 
     // The following two attempts are expected to run fine because
     // right exchange type is specified.
     EXPECT_NO_THROW(
-        stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
+        stats_mgr->passSentPacket(ExchangeType::DO, sent_packet)
     );
     EXPECT_NO_THROW(
-        stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet)
+        stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet)
     );
 }
 
 TEST_F(StatsMgrTest, MultipleExchanges) {
-    boost::shared_ptr<StatsMgr6> stats_mgr(new StatsMgr6());
-    stats_mgr->addExchangeStats(StatsMgr6::XCHG_SA);
-    stats_mgr->addExchangeStats(StatsMgr6::XCHG_RR);
+    CommandOptions opt;
+    boost::shared_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+    stats_mgr->addExchangeStats(ExchangeType::SA);
+    stats_mgr->addExchangeStats(ExchangeType::RR);
 
     // Simulate sending number of solicit packets.
     const int solicit_packets_num = 10;
-    passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_SOLICIT,
+    passMultiplePackets6(stats_mgr, ExchangeType::SA, DHCPV6_SOLICIT,
                          solicit_packets_num);
 
     // Simulate sending number of request packets. It is important that
@@ -343,60 +339,62 @@ TEST_F(StatsMgrTest, MultipleExchanges) {
     // packets. We can now check if right number packets went to
     // the right exchange type group.
     const int request_packets_num = 5;
-    passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_RR, DHCPV6_REQUEST,
+    passMultiplePackets6(stats_mgr, ExchangeType::RR, DHCPV6_REQUEST,
                          request_packets_num);
 
     // Check if all packets are successfully passed to packet lists.
     EXPECT_EQ(solicit_packets_num,
-              stats_mgr->getSentPacketsNum(StatsMgr6::XCHG_SA));
+              stats_mgr->getSentPacketsNum(ExchangeType::SA));
     EXPECT_EQ(request_packets_num,
-              stats_mgr->getSentPacketsNum(StatsMgr6::XCHG_RR));
+              stats_mgr->getSentPacketsNum(ExchangeType::RR));
 
     // Simulate reception of multiple packets for both SOLICIT-ADVERTISE
     // and REQUEST-REPLY exchanges. Assume no packet drops.
     const bool receive_packets = true;
-    passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_ADVERTISE,
+    passMultiplePackets6(stats_mgr, ExchangeType::SA, DHCPV6_ADVERTISE,
                          solicit_packets_num, receive_packets);
 
-    passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_RR, DHCPV6_REPLY,
+    passMultiplePackets6(stats_mgr, ExchangeType::RR, DHCPV6_REPLY,
                          request_packets_num, receive_packets);
 
     // Verify that all received packets are counted.
     EXPECT_EQ(solicit_packets_num,
-              stats_mgr->getRcvdPacketsNum(StatsMgr6::XCHG_SA));
+              stats_mgr->getRcvdPacketsNum(ExchangeType::SA));
     EXPECT_EQ(request_packets_num,
-              stats_mgr->getRcvdPacketsNum(StatsMgr6::XCHG_RR));
+              stats_mgr->getRcvdPacketsNum(ExchangeType::RR));
 }
 
 TEST_F(StatsMgrTest, SendReceiveSimple) {
-    boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
+    CommandOptions opt;
+    boost::scoped_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
     boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
                                                       common_transid));
     boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
                                                       common_transid));
-    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
+    stats_mgr->addExchangeStats(ExchangeType::DO);
     // The following attempt is expected to pass because the right
     // exchange type is used.
     ASSERT_NO_THROW(
-        stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
+        stats_mgr->passSentPacket(ExchangeType::DO, sent_packet)
     );
     // It is ok, to pass to received packets here. First one will
     // be matched with sent packet. The latter one will not be
     // matched with sent packet but orphans counter will simply
     // increase.
     ASSERT_NO_THROW(
-        stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet)
+        stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet)
     );
     ASSERT_NO_THROW(
-        stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet)
+        stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet)
     );
-    EXPECT_EQ(1, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
+    EXPECT_EQ(1, stats_mgr->getOrphans(ExchangeType::DO));
 }
 
 TEST_F(StatsMgrTest, SendReceiveUnordered) {
+    CommandOptions opt;
     const int packets_num = 10;
-    boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
-    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
+    boost::scoped_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+    stats_mgr->addExchangeStats(ExchangeType::DO);
 
     // Transaction ids of 10 packets to be sent and received.
     uint32_t transid[packets_num] =
@@ -405,7 +403,7 @@ TEST_F(StatsMgrTest, SendReceiveUnordered) {
         boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
                                                           transid[i]));
         ASSERT_NO_THROW(
-            stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
+            stats_mgr->passSentPacket(ExchangeType::DO, sent_packet)
         );
     }
 
@@ -416,17 +414,17 @@ TEST_F(StatsMgrTest, SendReceiveUnordered) {
             rcvd_packet(createPacket4(DHCPDISCOVER,
                                       transid[packets_num - 1 - i]));
         ASSERT_NO_THROW(
-            stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
+            stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet);
         );
     }
     // All packets are expected to match (we did not drop any)
-    EXPECT_EQ(0, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
+    EXPECT_EQ(0, stats_mgr->getOrphans(ExchangeType::DO));
     // Most of the time we have to do unordered lookups except for the last
     // one. Packets are removed from the sent list every time we have a match
     // so eventually we come up with the single packet that caching iterator
     // is pointing to. This is counted as ordered lookup.
-    EXPECT_EQ(1, stats_mgr->getOrderedLookups(StatsMgr4::XCHG_DO));
-    EXPECT_EQ(9, stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO));
+    EXPECT_EQ(1, stats_mgr->getOrderedLookups(ExchangeType::DO));
+    EXPECT_EQ(9, stats_mgr->getUnorderedLookups(ExchangeType::DO));
 }
 
 TEST_F(StatsMgrTest, SendReceiveCollected) {
@@ -438,33 +436,34 @@ TEST_F(StatsMgrTest, SendReceiveCollected) {
 }
 
 TEST_F(StatsMgrTest, Orphans) {
+    CommandOptions opt;
     const int packets_num = 6;
-    boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
-    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
+    boost::scoped_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+    stats_mgr->addExchangeStats(ExchangeType::DO);
 
     // We skip every second packet to simulate drops.
     for (int i = 0; i < packets_num; i += 2) {
         boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER, i));
         ASSERT_NO_THROW(
-            stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
+            stats_mgr->passSentPacket(ExchangeType::DO, sent_packet)
         );
     }
     // We pass all received packets.
     for (int i = 0; i < packets_num; ++i) {
         boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER, i));
         ASSERT_NO_THROW(
-            stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
+            stats_mgr->passRcvdPacket(ExchangeType::DO, rcvd_packet);
         );
     }
     // The half of received packets are expected not to have matching
     // sent packet.
-    EXPECT_EQ(packets_num / 2, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
+    EXPECT_EQ(packets_num / 2, stats_mgr->getOrphans(ExchangeType::DO));
 }
 
 TEST_F(StatsMgrTest, Delays) {
-
-    boost::shared_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
-    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO, 5);
+    CommandOptions opt;
+    boost::shared_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+    stats_mgr->addExchangeStats(ExchangeType::DO, 5);
 
     // Send DISCOVER, wait 2s and receive OFFER. This will affect
     // counters in Stats Manager.
@@ -472,16 +471,16 @@ TEST_F(StatsMgrTest, Delays) {
 
     // Initially min delay is equal to MAX_DOUBLE. After first packets
     // are passed, it is expected to set to actual value.
-    EXPECT_LT(stats_mgr->getMinDelay(StatsMgr4::XCHG_DO),
+    EXPECT_LT(stats_mgr->getMinDelay(ExchangeType::DO),
               std::numeric_limits<double>::max());
-    EXPECT_GT(stats_mgr->getMinDelay(StatsMgr4::XCHG_DO), 1);
+    EXPECT_GT(stats_mgr->getMinDelay(ExchangeType::DO), 1);
 
     // Max delay is supposed to the same value as minimum
     // or maximum delay.
-    EXPECT_GT(stats_mgr->getMaxDelay(StatsMgr4::XCHG_DO), 1);
+    EXPECT_GT(stats_mgr->getMaxDelay(ExchangeType::DO), 1);
 
     // Delay sums are now the same as minimum or maximum delay.
-    EXPECT_GT(stats_mgr->getAvgDelay(StatsMgr4::XCHG_DO), 1);
+    EXPECT_GT(stats_mgr->getAvgDelay(ExchangeType::DO), 1);
 
     // Simulate another DISCOVER-OFFER exchange with delay between
     // sent and received packets. Delay is now shorter than earlier
@@ -489,11 +488,12 @@ TEST_F(StatsMgrTest, Delays) {
     const unsigned int delay2 = 1;
     passDOPacketsWithDelay(stats_mgr, delay2, common_transid + 1);
     // Standard deviation is expected to be non-zero.
-    EXPECT_GT(stats_mgr->getStdDevDelay(StatsMgr4::XCHG_DO), 0);
+    EXPECT_GT(stats_mgr->getStdDevDelay(ExchangeType::DO), 0);
 }
 
 TEST_F(StatsMgrTest, CustomCounters) {
-    boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
+    CommandOptions opt;
+    boost::scoped_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
 
     // Specify counter keys and names.
     const std::string too_short_key("tooshort");
@@ -518,13 +518,13 @@ TEST_F(StatsMgrTest, CustomCounters) {
     }
 
     // Check counter's current value and name.
-    StatsMgr4::CustomCounterPtr tooshort_counter =
+    CustomCounterPtr tooshort_counter =
         stats_mgr->getCounter(too_short_key);
     EXPECT_EQ(too_short_name, tooshort_counter->getName());
     EXPECT_EQ(tooshort_num, tooshort_counter->getValue());
 
     // Check counter's current value and name.
-    StatsMgr4::CustomCounterPtr toolate_counter =
+    CustomCounterPtr toolate_counter =
         stats_mgr->getCounter(too_late_key);
     EXPECT_EQ(too_late_name, toolate_counter->getName());
     EXPECT_EQ(toolate_num, toolate_counter->getValue());
@@ -536,15 +536,16 @@ TEST_F(StatsMgrTest, PrintStats) {
               << "capabilities. It is expected that some counters "
               << "will be printed during this test. It may also "
               << "cause spurious errors." << std::endl;
-    boost::shared_ptr<StatsMgr6> stats_mgr(new StatsMgr6());
-    stats_mgr->addExchangeStats(StatsMgr6::XCHG_SA);
+    CommandOptions opt;
+    boost::shared_ptr<StatsMgr> stats_mgr(new StatsMgr(opt));
+    stats_mgr->addExchangeStats(ExchangeType::SA);
 
     // Simulate sending and receiving one packet. Otherwise printing
     // functions will complain about lack of packets.
     const int packets_num = 1;
-    passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_SOLICIT,
+    passMultiplePackets6(stats_mgr, ExchangeType::SA, DHCPV6_SOLICIT,
                          packets_num);
-    passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_ADVERTISE,
+    passMultiplePackets6(stats_mgr, ExchangeType::SA, DHCPV6_ADVERTISE,
                          packets_num, true);
 
     // This function will print statistics even if packets are not
@@ -559,9 +560,9 @@ TEST_F(StatsMgrTest, PrintStats) {
 
     // Now, we create another statistics manager instance and enable
     // packets archiving mode.
-    const bool archive_packets = true;
-    boost::shared_ptr<StatsMgr6> stats_mgr2(new StatsMgr6(archive_packets));
-    stats_mgr2->addExchangeStats(StatsMgr6::XCHG_SA);
+    CommandOptionsHelper::process(opt, "perfdhcp -x t 127.0.0.1");
+    boost::shared_ptr<StatsMgr> stats_mgr2(new StatsMgr(opt));
+    stats_mgr2->addExchangeStats(ExchangeType::SA);
 
     // Timestamps should now get printed because packets have been preserved.
     EXPECT_NO_THROW(stats_mgr2->printTimestamps());
index fc9393bb5cb246ac7c6ded4908ceb990f0805762..a3215a4814393a5a88bf16397f947443787f746b 100644 (file)
@@ -31,6 +31,62 @@ using namespace isc;
 using namespace isc::dhcp;
 using namespace isc::perfdhcp;
 
+/// \brief FakePerfSocket class that mocks PerfSocket.
+///
+/// It stubs send and receive operations and collects statistics.
+class FakePerfSocket: public BasePerfSocket {
+public:
+    /// \brief Default constructor for FakePerfSocket.
+    FakePerfSocket() :
+        iface_(boost::make_shared<Iface>("fake", 0)),
+        sent_cnt_(0),
+        recv_cnt_(0) {};
+
+    IfacePtr iface_;  ///< Local fake interface.
+
+    int sent_cnt_;  ///< Counter of sent packets
+    int recv_cnt_;  ///< Counter of received packets.
+
+    /// \brief Simulate receiving DHCPv4 packet.
+    virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) override {
+        (void)timeout_sec; // silence compile 'unused parameter' warning;
+        (void)timeout_usec; // silence compile 'unused parameter' warning;
+        recv_cnt_++;
+        return(dhcp::Pkt4Ptr());
+    };
+
+    /// \brief Simulate receiving DHCPv6 packet.
+    virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) override {
+        (void)timeout_sec; // silence compile 'unused parameter' warning;
+        (void)timeout_usec; // silence compile 'unused parameter' warning;
+        recv_cnt_++;
+        return(dhcp::Pkt6Ptr());
+    };
+
+    /// \brief Simulate sending DHCPv4 packet.
+    virtual bool send(const dhcp::Pkt4Ptr& pkt) override {
+        sent_cnt_++;
+        pkt->updateTimestamp();
+        return true;
+    };
+
+    /// \brief Simulate sending DHCPv6 packet.
+    virtual bool send(const dhcp::Pkt6Ptr& pkt) override {
+        sent_cnt_++;
+        pkt->updateTimestamp();
+        return true;
+    };
+
+    /// \brief Override getting interface.
+    virtual IfacePtr getIface() override { return iface_; }
+
+    void reset() {
+        sent_cnt_ = 0;
+        recv_cnt_ = 0;
+    }
+};
+
+
 /// \brief Test Control class with protected members made public.
 ///
 /// This class makes protected TestControl class's members public
@@ -74,7 +130,6 @@ public:
     /// \brief Pointer to incremental generator.
     typedef boost::shared_ptr<IncrementalGenerator> IncrementalGeneratorPtr;
 
-    using TestControl::checkExitConditions;
     using TestControl::createMessageFromReply;
     using TestControl::createRequestFromAck;
     using TestControl::factoryElapsedTime6;
@@ -88,8 +143,6 @@ public:
     using TestControl::generateMacAddress;
     using TestControl::getTemplateBuffer;
     using TestControl::initPacketTemplates;
-    using TestControl::initializeStatsMgr;
-    using TestControl::openSocket;
     using TestControl::processReceivedPacket4;
     using TestControl::processReceivedPacket6;
     using TestControl::registerOptionFactories;
@@ -103,9 +156,7 @@ public:
     using TestControl::sendSolicit6;
     using TestControl::setDefaults4;
     using TestControl::setDefaults6;
-    using TestControl::basic_rate_control_;
-    using TestControl::renew_rate_control_;
-    using TestControl::release_rate_control_;
+    using TestControl::socket_;
     using TestControl::last_report_;
     using TestControl::transid_gen_;
     using TestControl::macaddr_gen_;
@@ -115,15 +166,20 @@ public:
     using TestControl::template_packets_v6_;
     using TestControl::ack_storage_;
     using TestControl::sendRequestFromAck;
+    using TestControl::options_;
+    using TestControl::stats_mgr_;
+
+    FakePerfSocket fake_sock_;
 
-    NakedTestControl() : TestControl() {
-        uint32_t clients_num = CommandOptions::instance().getClientsNum() == 0 ?
-            1 : CommandOptions::instance().getClientsNum();
+    NakedTestControl(CommandOptions &opt) : TestControl(opt, fake_sock_) {
+        uint32_t clients_num = opt.getClientsNum() == 0 ?
+            1 : opt.getClientsNum();
         setMacAddrGenerator(NumberGeneratorPtr(new TestControl::SequentialGenerator(clients_num)));
     };
 
 };
 
+
 /// \brief Test Fixture Class
 ///
 /// This test fixture class is used to perform
@@ -189,25 +245,6 @@ public:
         return (true);
     }
 
-    /// \brief Get local loopback interface name.
-    ///
-    /// Scan available network interfaces for local loopback
-    /// interface and get its name. On Linux this interface is
-    /// usually called 'lo' but on other systems, e.g. BSD
-    /// it will have slightly different name. Local loopback
-    /// interface is required for unit tests that require
-    /// socket creation.
-    ///
-    /// \return local loopback interface name.
-    std::string getLocalLoopback() const {
-        BOOST_FOREACH(IfacePtr iface, IfaceMgr::instance().getIfaces()) {
-            if (iface->flag_loopback_) {
-                return (iface->getName());
-            }
-        }
-        return ("");
-    }
-
     /// \brief Get full path to a file in testdata directory.
     ///
     /// \param filename filename being appended to absolute
@@ -316,10 +353,10 @@ public:
     /// as the number of seconds since DUID time epoch. The parts
     /// of MAC address has to change if multiple clients are simulated
     /// and do not change if single client is simulated.
-    void testDuid() const {
-        int clients_num = CommandOptions::instance().getClientsNum();
+    void testDuid(CommandOptions &opt) const {
+        int clients_num = opt.getClientsNum();
         // Initialize Test Control class.
-        NakedTestControl tc;
+        NakedTestControl tc(opt);
         // The old duid will be holding the previously generated DUID.
         // It will be used to compare against the new one. If we have
         // multiple clients we want to make sure that duids differ.
@@ -439,21 +476,18 @@ public:
     ///
     /// \param iterations_num number of exchanges to simulate.
     /// \param receive_num number of received OFFER packets.
-    /// \param iterations_performed actual number of iterations.
     /// \param tc test control instance
     void testPkt4Exchange(int iterations_num,
                           int receive_num,
                           bool use_templates,
-                          int& iterations_performed,
                           NakedTestControl& tc) const {
-        int sock_handle = 0;
-        tc.initializeStatsMgr();
+        //int sock_handle = 0;
 
         // Use templates files to crate packets.
         if (use_templates) {
             tc.initPacketTemplates();
-            ASSERT_NO_THROW(tc.getTemplateBuffer(0));
-            ASSERT_NO_THROW(tc.getTemplateBuffer(1));
+            tc.getTemplateBuffer(0);
+            tc.getTemplateBuffer(1);
         }
 
         // Incremental transaction id generator will generate
@@ -464,17 +498,14 @@ public:
         NakedTestControl::IncrementalGeneratorPtr
             generator(new NakedTestControl::IncrementalGenerator());
         tc.setTransidGenerator(generator);
-        // Socket is needed to send packets through the interface.
-        ASSERT_NO_THROW(sock_handle = tc.openSocket());
-        PerfSocket sock(sock_handle);
         for (int i = 0; i < iterations_num; ++i) {
             // Get next transaction id, without actually using it. The same
             // id wll be used by the TestControl class for DHCPDISCOVER.
             uint32_t transid = generator->getNext();
             if (use_templates) {
-                ASSERT_NO_THROW(tc.sendDiscover4(sock, tc.getTemplateBuffer(0)));
+                tc.sendDiscover4(tc.getTemplateBuffer(0));
             } else {
-                ASSERT_NO_THROW(tc.sendDiscover4(sock));
+                tc.sendDiscover4();
             }
 
             // Do not simulate responses for packets later
@@ -482,13 +513,8 @@ public:
             // packet drops.
             if (i < receive_num) {
                 boost::shared_ptr<Pkt4> offer_pkt4(createOfferPkt4(transid));
-                ASSERT_NO_THROW(tc.processReceivedPacket4(sock, offer_pkt4));
+                tc.processReceivedPacket4(offer_pkt4);
             }
-            if (tc.checkExitConditions()) {
-                iterations_performed = i + 1;
-                break;
-            }
-            iterations_performed = i + 1;
         }
     }
 
@@ -504,21 +530,18 @@ public:
     ///
     /// \param iterations_num number of exchanges to simulate.
     /// \param receive_num number of received OFFER packets.
-    /// \param iterations_performed actual number of iterations.
     /// \param tc test control instance
     void testPkt6Exchange(int iterations_num,
                           int receive_num,
                           bool use_templates,
-                          int& iterations_performed,
                           NakedTestControl& tc) const {
-        int sock_handle = 0;
-        tc.initializeStatsMgr();
+        //int sock_handle = 0;
 
         // Use templates files to crate packets.
         if (use_templates) {
             tc.initPacketTemplates();
-            ASSERT_NO_THROW(tc.getTemplateBuffer(0));
-            ASSERT_NO_THROW(tc.getTemplateBuffer(1));
+            tc.getTemplateBuffer(0);
+            tc.getTemplateBuffer(1);
         }
 
         // Incremental transaction id generator will generate
@@ -529,33 +552,24 @@ public:
         TestControl::NumberGeneratorPtr
             generator(new NakedTestControl::IncrementalGenerator());
         tc.setTransidGenerator(generator);
-        // Socket is needed to send packets through the interface.
-        ASSERT_NO_THROW(sock_handle = tc.openSocket());
-        PerfSocket sock(sock_handle);
         uint32_t transid = 0;
         for (int i = 0; i < iterations_num; ++i) {
             // Do not simulate responses for packets later
             // that specified as receive_num. This simulates
             // packet drops.
             if (use_templates) {
-                ASSERT_NO_THROW(tc.sendSolicit6(sock, tc.getTemplateBuffer(0)));
+                tc.sendSolicit6(tc.getTemplateBuffer(0));
             } else {
-                ASSERT_NO_THROW(tc.sendSolicit6(sock));
+                tc.sendSolicit6();
             }
             ++transid;
             if (i < receive_num) {
                 boost::shared_ptr<Pkt6>
-                    advertise_pkt6(createAdvertisePkt6(transid));
+                    advertise_pkt6(createAdvertisePkt6(tc, transid));
                 // Receive ADVERTISE and send REQUEST.
-                ASSERT_NO_THROW(tc.processReceivedPacket6(sock,
-                                                          advertise_pkt6));
+                tc.processReceivedPacket6(advertise_pkt6);
                 ++transid;
             }
-            if (tc.checkExitConditions()) {
-                iterations_performed = i + 1;
-                break;
-            }
-            iterations_performed = i + 1;
         }
     }
 
@@ -567,13 +581,13 @@ public:
     /// addresses are generated if number of simulated clients is
     /// greater than 1. It also checks if the same MAC addresses is
     /// generated if only 1 client is simulated.
-    void testMacAddress() const {
-        int clients_num = CommandOptions::instance().getClientsNum();
+    void testMacAddress(CommandOptions &opt) const {
+        int clients_num = opt.getClientsNum();
         // The old_mac will be holding the value of previously generated
         // MAC address. We will be comparing the newly generated one with it
         // to see if it changes when multiple clients are simulated or if it
         // does not change when single client is simulated.
-        MacAddress old_mac(CommandOptions::instance().getMacTemplate());
+        MacAddress old_mac(opt.getMacTemplate());
         // Holds the position if the octet on which two MAC addresses can
         // be different. If number of clients is 256 or less it is last MAC
         // octet (except for single client when subsequent MAC addresses
@@ -583,7 +597,7 @@ public:
         // Number of unique MACs.
         size_t unique_macs = 0;
         // Initialize Test Controller.
-        NakedTestControl tc;
+        NakedTestControl tc(opt);
         size_t total_dist = 0;
         // Keep generated MACs in this container.
         std::list<std::vector<uint8_t> > macs;
@@ -650,34 +664,24 @@ public:
     /// attempt to send more renew messages than the number of leases acquired
     /// will fail.
     void testSendRenew4() {
-        std::string loopback_iface(getLocalLoopback());
-        if (loopback_iface.empty()) {
-            std::cout << "Skipping the test because loopback interface could"
-                " not be detected" << std::endl;
-            return;
-        }
         // Build a command line. Depending on the message type, we will use
         // -f<renew-rate> or -F<release-rate> parameter.
+        CommandOptions opt;
         std::ostringstream s;
-        s << "perfdhcp -4 -l " << loopback_iface << " -r 10 -f";
+        s << "perfdhcp -4 -l fake -r 10 -f";
         s << " 10 -R 10 -L 10067 -n 10 127.0.0.1";
-        ASSERT_NO_THROW(processCmdLine(s.str()));
+        processCmdLine(opt, s.str());
         // Create a test controller class.
-        NakedTestControl tc;
-        tc.initializeStatsMgr();
+        NakedTestControl tc(opt);
         // Set the transaction id generator to sequential to control to
         // guarantee that transaction ids are predictable.
         boost::shared_ptr<NakedTestControl::IncrementalGenerator>
             generator(new NakedTestControl::IncrementalGenerator());
         tc.setTransidGenerator(generator);
-        // Socket has to be created so as we can actually send packets.
-        int sock_handle = 0;
-        ASSERT_NO_THROW(sock_handle = tc.openSocket());
-        PerfSocket sock(sock_handle);
 
         // Send a number of DHCPDISCOVER messages. Each generated message will
         // be assigned a different transaction id, starting from 1 to 10.
-        tc.sendPackets(sock, 10);
+        tc.sendPackets(10);
 
         // Simulate DHCPOFFER responses from the server. Each DHCPOFFER is
         // assigned a transaction id from the range of 1 to 10, so as they
@@ -689,7 +693,7 @@ public:
             // will trigger a corresponding DHCPREQUEST. They will be assigned
             // transaction ids from the range from 11 to 20 (the range of
             // 1 to 10 has been used by DHCPDISCOVER-DHCPOFFER).
-            ASSERT_NO_THROW(tc.processReceivedPacket4(sock, offer));
+            tc.processReceivedPacket4(offer);
     }
 
         // Requests have been sent, so now let's simulate responses from the
@@ -702,32 +706,26 @@ public:
             // -f<renew-rate> option has been specified, received Reply
             // messages are held so as renew messages can be sent for
             // existing leases.
-            ASSERT_NO_THROW(tc.processReceivedPacket4(sock, ack));
+            tc.processReceivedPacket4(ack);
         }
 
         uint64_t msg_num;
         // Try to send 5 messages. It should be successful because 10
         // DHCPREQUEST messages has been received. For each of them we
         // should be able to send renewal.
-        ASSERT_NO_THROW(
-            msg_num = tc.sendMultipleRequests(sock, 5)
-        );
+        msg_num = tc.sendMultipleRequests(5);
         // Make sure that we have sent 5 messages.
         EXPECT_EQ(5, msg_num);
 
         // Try to do it again. We should still have 5 Reply packets for
         // which renews haven't been sent yet.
-        ASSERT_NO_THROW(
-            msg_num = tc.sendMultipleRequests(sock, 5)
-        );
+        msg_num = tc.sendMultipleRequests(5);
         EXPECT_EQ(5, msg_num);
 
         // We used all the DHCPACK packets (we sent renew or release for each of
         // them already). Therefore, no further renew messages should be sent
         // before we acquire new leases.
-        ASSERT_NO_THROW(
-            msg_num = tc.sendMultipleRequests(sock, 5)
-        );
+        msg_num = tc.sendMultipleRequests(5);
         // Make sure that no message has been sent.
         EXPECT_EQ(0, msg_num);
     }
@@ -737,12 +735,13 @@ public:
     void testCreateRequest() {
         // This command line specifies that the Release/Renew messages should
         // be sent with the same rate as the Solicit messages.
+        CommandOptions opt;
         std::ostringstream s;
         s << "perfdhcp -4 -l lo -r 10 -f 10";
         s << " -R 10 -L 10067 -n 10 127.0.0.1";
-        ASSERT_NO_THROW(processCmdLine(s.str()));
+        processCmdLine(opt, s.str());
         // Create a test controller class.
-        NakedTestControl tc;
+        NakedTestControl tc(opt);
         // Set the transaction id generator which will be used by the
         // createRenew or createRelease function to generate transaction id.
         boost::shared_ptr<NakedTestControl::IncrementalGenerator>
@@ -753,7 +752,7 @@ public:
 
         // Create DHCPREQUEST from DHCPACK.
         Pkt4Ptr request;
-        ASSERT_NO_THROW(request = tc.createRequestFromAck(ack));
+        request = tc.createRequestFromAck(ack);
 
         // Make sure that the DHCPACK has been successfully created and that
         // it holds expected data.
@@ -785,13 +784,14 @@ public:
     void testCreateRenewRelease(const uint16_t msg_type) {
         // This command line specifies that the Release/Renew messages should
         // be sent with the same rate as the Solicit messages.
+        CommandOptions opt;
         std::ostringstream s;
         s << "perfdhcp -6 -l lo -r 10 ";
         s << (msg_type == DHCPV6_RELEASE ? "-F" : "-f") << " 10 ";
         s << "-R 10 -L 10547 -n 10 -e address-and-prefix ::1";
-        ASSERT_NO_THROW(processCmdLine(s.str()));
+        processCmdLine(opt, s.str());
         // Create a test controller class.
-        NakedTestControl tc;
+        NakedTestControl tc(opt);
         // Set the transaction id generator which will be used by the
         // createRenew or createRelease function to generate transaction id.
         boost::shared_ptr<NakedTestControl::IncrementalGenerator>
@@ -800,11 +800,11 @@ public:
 
         // Create a Reply packet. The createRelease or createReply function will
         // need Reply packet to create a corresponding Release or Reply.
-        Pkt6Ptr reply = createReplyPkt6(1);
+        Pkt6Ptr reply = createReplyPkt6(tc, 1);
 
         Pkt6Ptr msg;
         // Check that the message is created.
-        ASSERT_NO_THROW(msg = tc.createMessageFromReply(msg_type, reply));
+        msg = tc.createMessageFromReply(msg_type, reply);
 
         ASSERT_TRUE(msg);
         // Check that the message type and transaction id is correct.
@@ -856,47 +856,37 @@ public:
     /// \param msg_type A type of the message which is simulated to be sent
     /// (DHCPV6_RENEW or DHCPV6_RELEASE).
     void testSendRenewRelease(const uint16_t msg_type) {
-        std::string loopback_iface(getLocalLoopback());
-        if (loopback_iface.empty()) {
-            std::cout << "Skipping the test because loopback interface could"
-                " not be detected" << std::endl;
-            return;
-        }
         // Build a command line. Depending on the message type, we will use
         // -f<renew-rate> or -F<release-rate> parameter.
+        CommandOptions opt;
         std::ostringstream s;
-        s << "perfdhcp -6 -l " << loopback_iface << " -r 10 ";
+        s << "perfdhcp -6 -l fake -r 10 ";
         s << (msg_type == DHCPV6_RENEW ? "-f" : "-F");
         s << " 10 -R 10 -L 10547 -n 10 ::1";
-        ASSERT_NO_THROW(processCmdLine(s.str()));
+        processCmdLine(opt, s.str());
         // Create a test controller class.
-        NakedTestControl tc;
-        tc.initializeStatsMgr();
+        NakedTestControl tc(opt);
         // Set the transaction id generator to sequential to control to
         // guarantee that transaction ids are predictable.
         boost::shared_ptr<NakedTestControl::IncrementalGenerator>
             generator(new NakedTestControl::IncrementalGenerator());
         tc.setTransidGenerator(generator);
-        // Socket has to be created so as we can actually send packets.
-        int sock_handle = 0;
-        ASSERT_NO_THROW(sock_handle = tc.openSocket());
-        PerfSocket sock(sock_handle);
 
         // Send a number of Solicit messages. Each generated Solicit will be
         // assigned a different transaction id, starting from 1 to 10.
-        tc.sendPackets(sock, 10);
+        tc.sendPackets(10);
 
         // Simulate Advertise responses from the server. Each advertise is
         // assigned a transaction id from the range of 1 to 10, so as they
         // match the transaction ids from the Solicit messages.
         for (unsigned i = generator->getNext() - 10;
              i < generator->getNext(); ++i) {
-            Pkt6Ptr advertise(createAdvertisePkt6(i));
+            Pkt6Ptr advertise(createAdvertisePkt6(tc, i));
             // If Advertise is matched with the Solicit the call below will
             // trigger a corresponding Request. They will be assigned
             // transaction ids from the range from 11 to 20 (the range of
             // 1 to 10 has been used by Solicit-Advertise).
-            ASSERT_NO_THROW(tc.processReceivedPacket6(sock, advertise));
+            tc.processReceivedPacket6(advertise);
     }
 
         // Requests have been sent, so now let's simulate responses from the
@@ -904,37 +894,31 @@ public:
         // ids from the range from 11 to 20.
         for (unsigned i = generator->getNext() - 10;
              i < generator->getNext(); ++i) {
-            Pkt6Ptr reply(createReplyPkt6(i));
+            Pkt6Ptr reply(createReplyPkt6(tc, i));
             // Each Reply packet corresponds to the new lease acquired. Since
             // -f<renew-rate> option has been specified, received Reply
             // messages are held so as Renew messages can be sent for
             // existing leases.
-            ASSERT_NO_THROW(tc.processReceivedPacket6(sock, reply));
+            tc.processReceivedPacket6(reply);
         }
 
         uint64_t msg_num;
         // Try to send 5 messages. It should be successful because 10 Reply
         // messages has been received. For each of them we should be able to
         // send Renew or Release.
-        ASSERT_NO_THROW(
-            msg_num = tc.sendMultipleMessages6(sock, msg_type, 5)
-        );
+        msg_num = tc.sendMultipleMessages6(msg_type, 5);
         // Make sure that we have sent 5 messages.
         EXPECT_EQ(5, msg_num);
 
         // Try to do it again. We should still have 5 Reply packets for
         // which Renews or Releases haven't been sent yet.
-        ASSERT_NO_THROW(
-            msg_num = tc.sendMultipleMessages6(sock, msg_type, 5)
-        );
+        msg_num = tc.sendMultipleMessages6(msg_type, 5);
         EXPECT_EQ(5, msg_num);
 
         // We used all the Reply packets (we sent Renew or Release for each of
         // them already). Therefore, no further Renew or Release messages should
         // be sent before we acquire new leases.
-        ASSERT_NO_THROW(
-            msg_num = tc.sendMultipleMessages6(sock, msg_type, 5)
-        );
+        msg_num = tc.sendMultipleMessages6(msg_type, 5);
         // Make sure that no message has been sent.
         EXPECT_EQ(0, msg_num);
 
@@ -945,8 +929,8 @@ public:
     /// \param cmdline command line string to be parsed.
     /// \throw isc::Unexpected if unexpected error occurred.
     /// \throw isc::InvalidParameter if command line is invalid.
-    void processCmdLine(const std::string& cmdline) const {
-        CommandOptionsHelper::process(cmdline);
+    void processCmdLine(CommandOptions &opt, const std::string& cmdline) const {
+        CommandOptionsHelper::process(opt, cmdline);
     }
 
     /// \brief Create DHCPOFFER or DHCPACK packet.
@@ -991,22 +975,19 @@ public:
     /// \param transid transaction id.
     /// \return instance of the packet.
     Pkt6Ptr
-    createAdvertisePkt6(const uint32_t transid) const {
+    createAdvertisePkt6(NakedTestControl &tc, const uint32_t transid) const {
         boost::shared_ptr<Pkt6> advertise(new Pkt6(DHCPV6_ADVERTISE, transid));
         // Add IA_NA if requested by the client.
-        if (CommandOptions::instance().getLeaseType()
-            .includes(CommandOptions::LeaseType::ADDRESS)) {
+        if (tc.options_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) {
             OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
             advertise->addOption(opt_ia_na);
         }
         // Add IA_PD if requested by the client.
-        if (CommandOptions::instance().getLeaseType()
-            .includes(CommandOptions::LeaseType::PREFIX)) {
+        if (tc.options_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) {
             OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD);
             advertise->addOption(opt_ia_pd);
         }
         OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
-        NakedTestControl tc;
         uint8_t randomized = 0;
         std::vector<uint8_t> duid(tc.generateDuid(randomized));
         OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
@@ -1017,22 +998,19 @@ public:
     }
 
     Pkt6Ptr
-    createReplyPkt6(const uint32_t transid) const {
+    createReplyPkt6(NakedTestControl &tc, const uint32_t transid) const {
         Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, transid));
         // Add IA_NA if requested by the client.
-        if (CommandOptions::instance().getLeaseType()
-            .includes(CommandOptions::LeaseType::ADDRESS)) {
+        if (tc.options_.getLeaseType().includes(CommandOptions::LeaseType::ADDRESS)) {
             OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
             reply->addOption(opt_ia_na);
         }
         // Add IA_PD if requested by the client.
-        if (CommandOptions::instance().getLeaseType()
-            .includes(CommandOptions::LeaseType::PREFIX)) {
+        if (tc.options_.getLeaseType().includes(CommandOptions::LeaseType::PREFIX)) {
             OptionPtr opt_ia_pd = Option::factory(Option::V6, D6O_IA_PD);
             reply->addOption(opt_ia_pd);
         }
         OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
-        NakedTestControl tc;
         uint8_t randomized = 0;
         std::vector<uint8_t> duid(tc.generateDuid(randomized));
         OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
@@ -1079,12 +1057,10 @@ public:
 
 // This test verifies that the class members are reset to expected values.
 TEST_F(TestControlTest, reset) {
-    ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l ethx -r 50 -f 30 -F 10 all"));
-    NakedTestControl tc;
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -4 127.0.0.1");
+    NakedTestControl tc(opt);
     tc.reset();
-    EXPECT_EQ(50, tc.basic_rate_control_.getRate());
-    EXPECT_EQ(30, tc.renew_rate_control_.getRate());
-    EXPECT_EQ(10, tc.release_rate_control_.getRate());
     EXPECT_FALSE(tc.last_report_.is_not_a_date_time());
     EXPECT_FALSE(tc.transid_gen_);
     EXPECT_FALSE(tc.macaddr_gen_);
@@ -1103,9 +1079,11 @@ TEST_F(TestControlTest, generateClientId) {
     HWAddrPtr hwaddr_ptr(new HWAddr(hwaddr, 5));
 
     // Use generated HW address to generate client id.
-    NakedTestControl tc;
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -4 127.0.0.1");
+    NakedTestControl tc(opt);
     OptionPtr opt_client_id;
-    ASSERT_NO_THROW(opt_client_id = tc.generateClientId(hwaddr_ptr));
+    opt_client_id = tc.generateClientId(hwaddr_ptr);
     ASSERT_TRUE(opt_client_id);
 
     // Extract the client id data.
@@ -1124,21 +1102,22 @@ TEST_F(TestControlTest, generateClientId) {
 TEST_F(TestControlTest, GenerateDuid) {
     // Simple command line that simulates one client only. Always the
     // same DUID will be generated.
-    ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 all"));
-    testDuid();
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l 127.0.0.1 all");
+    testDuid(opt);
 
     // Simulate 50 clients. Different DUID will be generated.
-    ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -R 50 all"));
-    testDuid();
+    processCmdLine(opt, "perfdhcp -l 127.0.0.1 -R 50 all");
+    testDuid(opt);
 
     // Checks that the random mac address returned by generateDuid
     // is in the list of mac addresses in the mac-list.txt data file
     std::string mac_list_full_path = getFullPath("mac-list.txt");
     std::ostringstream cmd;
-    cmd << "perfdhcp -M " << mac_list_full_path << " abc";
-    ASSERT_NO_THROW(processCmdLine(cmd.str()));
+    cmd << "perfdhcp -M " << mac_list_full_path << " 127.0.0.1";
+    processCmdLine(opt, cmd.str());
     // Initialize Test Controller.
-    NakedTestControl tc;
+    NakedTestControl tc(opt);
     uint8_t randomized = 0;
     std::vector<uint8_t> generated_duid = tc.generateDuid(randomized);
 
@@ -1148,8 +1127,7 @@ TEST_F(TestControlTest, GenerateDuid) {
     ASSERT_EQ(duid->getType(), DUID::DUID_LL);
 
     // Make sure it's on the list
-    CommandOptions& options = CommandOptions::instance();
-    const CommandOptions::MacAddrsVector& macs = options.getMacsFromFile();
+    const CommandOptions::MacAddrsVector& macs = opt.getMacsFromFile();
     // DUID LL comprises 2 bytes of duid type, 2 bytes of hardware type,
     // then 6 bytes of HW address.
     vector<uint8_t> mac(6);
@@ -1159,50 +1137,40 @@ TEST_F(TestControlTest, GenerateDuid) {
     ASSERT_TRUE(std::find(macs.begin(), macs.end(), mac) != macs.end());
 }
 
-TEST_F(TestControlTest, MisMatchVersionServer) {
-    NakedTestControl tc;
-
-    // make sure we catch -6 paired with v4 address
-    ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -6 192.168.1.1"));
-    EXPECT_THROW(tc.openSocket(), isc::InvalidParameter);
-
-    // make sure we catch -4 paired with v6 address
-    ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -4 ff02::1:2"));
-    EXPECT_THROW(tc.openSocket(), isc::InvalidParameter);
-}
-
 TEST_F(TestControlTest, GenerateMacAddress) {
+    CommandOptions opt;
     // Simulate one client only. Always the same MAC address will be
     // generated.
-    ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 all"));
-    testMacAddress();
+    processCmdLine(opt, "perfdhcp -l 127.0.0.1 all");
+    testMacAddress(opt);
 
     // Simulate 50 clients. Different MAC addresses will be generated.
-    ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -R 50 all"));
-    testMacAddress();
+    processCmdLine(opt, "perfdhcp -l 127.0.0.1 -R 50 all");
+    testMacAddress(opt);
 
     // Checks that the random mac address returned by generateMacAddress
     // is in the list of mac addresses in the mac-list.txt data file
     std::string mac_list_full_path = getFullPath("mac-list.txt");
     std::ostringstream cmd;
-    cmd << "perfdhcp -M " << mac_list_full_path << " abc";
-    ASSERT_NO_THROW(processCmdLine(cmd.str()));
+    cmd << "perfdhcp -M " << mac_list_full_path << " 127.0.0.1";
+    processCmdLine(opt, cmd.str());
     // Initialize Test Controller.
-    NakedTestControl tc;
+    NakedTestControl tc(opt);
     uint8_t randomized = 0;
     // Generate MAC address and sanity check its size.
     std::vector<uint8_t> mac = tc.generateMacAddress(randomized);
     ASSERT_EQ(6, mac.size());
     // Make sure that the generated MAC address belongs to the MAC addresses
     // read from a file.
-    CommandOptions& options = CommandOptions::instance();
-    const CommandOptions::MacAddrsVector& macs = options.getMacsFromFile();
+    const CommandOptions::MacAddrsVector& macs = opt.getMacsFromFile();
     ASSERT_TRUE(std::find(macs.begin(), macs.end(), mac) !=  macs.end());
 }
 
 TEST_F(TestControlTest, Options4) {
     using namespace isc::dhcp;
-    NakedTestControl tc;
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -4 127.0.0.1");
+    NakedTestControl tc(opt);
     // By default the IP version mode is V4 so there is no need to
     // parse command line to override the IP version. Note that
     // registerOptionFactories is used for both V4 and V6.
@@ -1216,7 +1184,7 @@ TEST_F(TestControlTest, Options4) {
     EXPECT_EQ(DHO_DHCP_MESSAGE_TYPE, opt_msg_type->getType());
     // Validate the message type from the option we have now created.
     uint8_t msg_type = 0;
-    ASSERT_NO_THROW(msg_type = opt_msg_type->getUint8());
+    msg_type = opt_msg_type->getUint8();
     EXPECT_EQ(DHCPDISCOVER, msg_type);
 
     // Create another option: DHCP_PARAMETER_REQUEST_LIST
@@ -1254,11 +1222,12 @@ TEST_F(TestControlTest, Options4) {
 
 TEST_F(TestControlTest, Options6) {
     using namespace isc::dhcp;
+    CommandOptions opt;
 
     // Lets override the IP version to test V6 options (-6 parameter)
-    ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -6 all"));
+    processCmdLine(opt, "perfdhcp -l lo -6 ::1");
 
-    NakedTestControl tc;
+    NakedTestControl tc(opt);
     tc.registerOptionFactories();
 
     // Validate the D6O_ELAPSED_TIME option.
@@ -1268,7 +1237,7 @@ TEST_F(TestControlTest, Options6) {
     EXPECT_EQ(D6O_ELAPSED_TIME, opt_elapsed_time->getType());
     // The default value of elapsed time is zero.
     uint16_t elapsed_time;
-    ASSERT_NO_THROW(elapsed_time = opt_elapsed_time->getUint16());
+    elapsed_time = opt_elapsed_time->getUint16();
     EXPECT_EQ(0, elapsed_time);
 
     // With the factory function we may also specify the actual
@@ -1295,7 +1264,7 @@ TEST_F(TestControlTest, Options6) {
     EXPECT_EQ(D6O_ELAPSED_TIME, opt_elapsed_time2->getType());
     // Make sure the getUint16 does not throw exception. It wile throw
     // buffer is shorter than 2 octets.
-    ASSERT_NO_THROW(elapsed_time = opt_elapsed_time2->getUint16());
+    elapsed_time = opt_elapsed_time2->getUint16();
     // Check the expected value of elapsed time.
     EXPECT_EQ(0x0101, elapsed_time);
 
@@ -1308,7 +1277,7 @@ TEST_F(TestControlTest, Options6) {
     EXPECT_THROW(opt_rapid_commit->getUint8(), isc::OutOfRange);
 
     // Validate the D6O_CLIENTID option.
-    OptionBuffer duid(CommandOptions::instance().getDuidTemplate());
+    OptionBuffer duid(opt.getDuidTemplate());
     OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
     EXPECT_EQ(Option::V6, opt_clientid->getUniverse());
     EXPECT_EQ(D6O_CLIENTID, opt_clientid->getType());
@@ -1362,182 +1331,125 @@ TEST_F(TestControlTest, Options6) {
 }
 
 TEST_F(TestControlTest, Packet4) {
-    // Use Interface Manager to get the local loopback interface.
-    // If interface can't be found we don't want to fail test.
-    std::string loopback_iface(getLocalLoopback());
-    if (!loopback_iface.empty()) {
-        ASSERT_NO_THROW(processCmdLine("perfdhcp -l " + loopback_iface +
-                                       " -L 10547 all"));
-        NakedTestControl tc;
-        int sock_handle = 0;
-        // We have to create the socket to setup some parameters of
-        // outgoing packet.
-        ASSERT_NO_THROW(sock_handle = tc.openSocket());
-        PerfSocket sock(sock_handle);
-        uint32_t transid = 123;
-        boost::shared_ptr<Pkt4> pkt4(new Pkt4(DHCPDISCOVER, transid));
-        // Set parameters on outgoing packet.
-        ASSERT_NO_THROW(tc.setDefaults4(sock, pkt4));
-        // Validate that packet has been setup correctly.
-        EXPECT_EQ(loopback_iface, pkt4->getIface());
-        EXPECT_EQ(sock.ifindex_, pkt4->getIndex());
-        EXPECT_EQ(DHCP4_CLIENT_PORT, pkt4->getLocalPort());
-        EXPECT_EQ(DHCP4_SERVER_PORT, pkt4->getRemotePort());
-        EXPECT_EQ(1, pkt4->getHops());
-        EXPECT_EQ(asiolink::IOAddress("255.255.255.255"),
-                  pkt4->getRemoteAddr());
-        EXPECT_EQ(asiolink::IOAddress(sock.addr_), pkt4->getLocalAddr());
-        EXPECT_EQ(asiolink::IOAddress(sock.addr_), pkt4->getGiaddr());
-    } else {
-        std::cout << "Unable to find the loopback interface. Skip test. "
-                  << std::endl;
-    }
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l fake -L 10547 all");
+    NakedTestControl tc(opt);
+    uint32_t transid = 123;
+    boost::shared_ptr<Pkt4> pkt4(new Pkt4(DHCPDISCOVER, transid));
+    // Set parameters on outgoing packet.
+    tc.setDefaults4(pkt4);
+    // Validate that packet has been setup correctly.
+    EXPECT_EQ(tc.fake_sock_.iface_->getName(), pkt4->getIface());
+    EXPECT_EQ(tc.fake_sock_.ifindex_, pkt4->getIndex());
+    EXPECT_EQ(DHCP4_CLIENT_PORT, pkt4->getLocalPort());
+    EXPECT_EQ(DHCP4_SERVER_PORT, pkt4->getRemotePort());
+    EXPECT_EQ(1, pkt4->getHops());
+    EXPECT_EQ(asiolink::IOAddress("255.255.255.255"),
+              pkt4->getRemoteAddr());
+    EXPECT_EQ(asiolink::IOAddress(tc.socket_.addr_), pkt4->getLocalAddr());
+    EXPECT_EQ(asiolink::IOAddress(tc.socket_.addr_), pkt4->getGiaddr());
 }
 
 TEST_F(TestControlTest, Packet6) {
-    // Use Interface Manager to get the local loopback interface.
-    // If the interface can't be found we don't want to fail test.
-    std::string loopback_iface(getLocalLoopback());
-    if (!loopback_iface.empty()) {
-        ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l " + loopback_iface +
-                                       " -L 10547 servers"));
-        NakedTestControl tc;
-        int sock_handle = 0;
-        // Create the socket. It will be needed to set packet's
-        // parameters.
-        ASSERT_NO_THROW(sock_handle = tc.openSocket());
-        PerfSocket sock(sock_handle);
-        uint32_t transid = 123;
-        boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
-        // Set packet's parameters.
-        ASSERT_NO_THROW(tc.setDefaults6(sock, pkt6));
-        // Validate if parameters have been set correctly.
-        EXPECT_EQ(loopback_iface, pkt6->getIface());
-        EXPECT_EQ(sock.ifindex_, pkt6->getIndex());
-        EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort());
-        EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort());
-        EXPECT_EQ(sock.addr_, pkt6->getLocalAddr());
-        EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr());
-        // Packet must not be relayed.
-        EXPECT_TRUE(pkt6->relay_info_.empty());
-
-    } else {
-        std::cout << "Unable to find the loopback interface. Skip test. "
-                  << std::endl;
-    }
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -6 -l fake -L 10547 servers");
+    NakedTestControl tc(opt);
+    uint32_t transid = 123;
+    boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
+    // Set packet's parameters.
+    tc.setDefaults6(pkt6);
+    // Validate if parameters have been set correctly.
+    EXPECT_EQ(tc.fake_sock_.iface_->getName(), pkt6->getIface());
+    EXPECT_EQ(tc.socket_.ifindex_, pkt6->getIndex());
+    EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort());
+    EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort());
+    EXPECT_EQ(tc.socket_.addr_, pkt6->getLocalAddr());
+    EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr());
+    // Packet must not be relayed.
+    EXPECT_TRUE(pkt6->relay_info_.empty());
 }
 
 TEST_F(TestControlTest, Packet6Relayed) {
-    // Use Interface Manager to get the local loopback interface.
-    // If the interface can't be found we don't want to fail test.
-    std::string loopback_iface(getLocalLoopback());
-    if (!loopback_iface.empty()) {
-        ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l " + loopback_iface +
-                                       " -A1 -L 10547 servers"));
-        NakedTestControl tc;
-        int sock_handle = 0;
-        // Create the socket. It will be needed to set packet's
-        // parameters.
-        ASSERT_NO_THROW(sock_handle = tc.openSocket());
-        PerfSocket sock(sock_handle);
-        uint32_t transid = 123;
-        boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
-        // Set packet's parameters.
-        ASSERT_NO_THROW(tc.setDefaults6(sock, pkt6));
-        // Validate if parameters have been set correctly.
-        EXPECT_EQ(loopback_iface, pkt6->getIface());
-        EXPECT_EQ(sock.ifindex_, pkt6->getIndex());
-        EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort());
-        EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort());
-        EXPECT_EQ(sock.addr_, pkt6->getLocalAddr());
-        EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr());
-        // Packet should be relayed.
-        EXPECT_EQ(pkt6->relay_info_.size(), 1);
-        EXPECT_EQ(pkt6->relay_info_[0].hop_count_, 1);
-        EXPECT_EQ(pkt6->relay_info_[0].msg_type_, DHCPV6_RELAY_FORW);
-        EXPECT_EQ(pkt6->relay_info_[0].linkaddr_, sock.addr_);
-        EXPECT_EQ(pkt6->relay_info_[0].peeraddr_, sock.addr_);
-    } else {
-        std::cout << "Unable to find the loopback interface. Skip test. "
-                  << std::endl;
-    }
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -6 -l fake -A1 -L 10547 servers");
+    NakedTestControl tc(opt);
+    uint32_t transid = 123;
+    boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
+    // Set packet's parameters.
+    tc.setDefaults6(pkt6);
+    // Validate if parameters have been set correctly.
+    EXPECT_EQ(tc.fake_sock_.iface_->getName(), pkt6->getIface());
+    EXPECT_EQ(tc.socket_.ifindex_, pkt6->getIndex());
+    EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort());
+    EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort());
+    EXPECT_EQ(tc.socket_.addr_, pkt6->getLocalAddr());
+    EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr());
+    // Packet should be relayed.
+    EXPECT_EQ(pkt6->relay_info_.size(), 1);
+    EXPECT_EQ(pkt6->relay_info_[0].hop_count_, 1);
+    EXPECT_EQ(pkt6->relay_info_[0].msg_type_, DHCPV6_RELAY_FORW);
+    EXPECT_EQ(pkt6->relay_info_[0].linkaddr_, tc.socket_.addr_);
+    EXPECT_EQ(pkt6->relay_info_[0].peeraddr_, tc.socket_.addr_);
 }
 
 TEST_F(TestControlTest, Packet4Exchange) {
-    // Get the local loopback interface to open socket on
-    // it and test packets exchanges. We don't want to fail
-    // the test if interface is not available.
-    std::string loopback_iface(getLocalLoopback());
-    if (loopback_iface.empty()) {
-        std::cout << "Unable to find the loopback interface. Skip test."
-                  << std::endl;
-        return;
-    }
-
-    // Set number of iterations to some high value.
     const int iterations_num = 100;
-    processCmdLine("perfdhcp -l " + loopback_iface
-                   + " -r 100 -n 10 -R 20 -L 10547 127.0.0.1");
-    // The actual number of iterations will be stored in the
-    // following variable.
-    int iterations_performed = 0;
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l fake -r 100 -n 10 -R 20 -L 10547 127.0.0.1");
     bool use_templates = false;
-    NakedTestControl tc;
-    testPkt4Exchange(iterations_num, iterations_num, use_templates, iterations_performed, tc);
-    // The command line restricts the number of iterations to 10
-    // with -n 10 parameter.
-    EXPECT_EQ(10, iterations_performed);
-
-    // With the following command line we restrict the maximum
-    // number of dropped packets to 10% of all.
-    // Use templates for this test.
-    processCmdLine("perfdhcp -l " + loopback_iface
-                   + " -r 100 -R 20 -n 20 -D 10% -L 10547"
-                   + " -T " + getFullPath("discover-example.hex")
+    NakedTestControl tc(opt);
+    testPkt4Exchange(iterations_num, iterations_num, use_templates, tc);
+
+    EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Discovery + Request
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::DO), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::DO), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RA), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RA), 0);
+}
+
+TEST_F(TestControlTest, Packet4ExchangeFromTemplate) {
+    const int iterations_num = 100;
+    CommandOptions opt;
+
+    processCmdLine(opt, "perfdhcp -l fake -r 100 -R 20 -n 20 -L 10547"
+                   " -T " + getFullPath("discover-example.hex")
                    + " -T " + getFullPath("request4-example.hex")
                    + " 127.0.0.1");
-    // The number iterations is restricted by the percentage of
-    // dropped packets (-D 10%). We also have to bump up the number
-    // of iterations because the percentage limitation checks starts
-    // at packet #10. We expect that at packet #12 the 10% threshold
-    // will be reached.
     const int received_num = 10;
-    use_templates = true;
-    testPkt4Exchange(iterations_num, received_num, use_templates, iterations_performed, tc);
-    EXPECT_EQ(12, iterations_performed);
+    bool use_templates = true;
+    NakedTestControl tc(opt);
+    testPkt4Exchange(iterations_num, received_num, use_templates, tc);
+
+    EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num + received_num);  // Discovery + Request
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::DO), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::DO), received_num);
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RA), received_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RA), 0);
 }
 
-TEST_F(TestControlTest, Packet6ExchangeFromTemplate) {
-    // Get the local loopback interface to open socket on
-    // it and test packets exchanges. We don't want to fail
-    // the test if interface is not available.
-    std::string loopback_iface(getLocalLoopback());
-    if (loopback_iface.empty()) {
-        std::cout << "Unable to find the loopback interface. Skip test."
-                  << std::endl;
-        return;
-    }
-
+TEST_F(TestControlTest, Packet6Exchange) {
     const int iterations_num = 100;
-    // Set number of iterations to 10.
-    processCmdLine("perfdhcp -l " + loopback_iface
-                   + " -6 -r 100 -n 10 -R 20 -L 10547 ::1");
-    int iterations_performed = 0;
-    // Set number of received packets equal to number of iterations.
-    // This simulates no packet drops.
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l fake -6 -r 100 -n 10 -R 20 -L 10547 ::1");
     bool use_templates = false;
-    NakedTestControl tc;
-    testPkt6Exchange(iterations_num, iterations_num, use_templates,
-                     iterations_performed, tc);
-    // Actual number of iterations should be 10.
-    EXPECT_EQ(10, iterations_performed);
-
-    // The maximum number of dropped packets is 3 (because of -D 3).
-    use_templates = true;
-    processCmdLine("perfdhcp -l " + loopback_iface
-                   + " -6 -r 100 -n 10 -R 20 -D 3 -L 10547"
-                   + " -T " + getFullPath("solicit-example.hex")
+    NakedTestControl tc(opt);
+    testPkt6Exchange(iterations_num, iterations_num, use_templates, tc);
+
+    EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Solicit + Request
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0);
+}
+
+TEST_F(TestControlTest, Packet6ExchangeFromTemplate) {
+    const int iterations_num = 100;
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l fake -6 -r 100 -n 10 -R 20 -L 10547"
+                   " -T " + getFullPath("solicit-example.hex")
                    + " -T " + getFullPath("request6-example.hex ::1"));
+    NakedTestControl tc(opt);
+
     // For the first 3 packets we are simulating responses from server.
     // For other packets we don't so packet as 4,5,6 will be dropped and
     // then test should be interrupted and actual number of iterations will
@@ -1545,127 +1457,89 @@ TEST_F(TestControlTest, Packet6ExchangeFromTemplate) {
     const int received_num = 3;
     // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges.
     // The test function generates server's responses and passes it to the
-    // TestControl class methods for processing. The number of exchanges
-    // actually performed is returned in 'iterations_performed' argument. If
-    // processing is successful, the number of performed iterations should be
-    // equal to the number of exchanges specified with the '-n' command line
-    // parameter (10 in this case). All exchanged packets carry the IA_NA option
-    // to simulate the IPv6 address acquisition and to verify that the
-    // IA_NA options returned by the server are processed correctly.
-    testPkt6Exchange(iterations_num, received_num, use_templates,
-                     iterations_performed, tc);
-    EXPECT_EQ(6, iterations_performed);
+    // TestControl class methods for processing. All exchanged packets carry
+    // the IA_NA option to simulate the IPv6 address acquisition and to verify
+    // that the IA_NA options returned by the server are processed correctly.
+    bool use_templates = true;
+    testPkt6Exchange(iterations_num, received_num, use_templates, tc);
+
+    EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num + received_num); // Solicit + Advertise
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), received_num);
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), received_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0);
 }
 
-TEST_F(TestControlTest, Packet6Exchange) {
-    // Get the local loopback interface to open socket on
-    // it and test packets exchanges. We don't want to fail
-    // the test if interface is not available.
-    std::string loopback_iface(getLocalLoopback());
-    if (loopback_iface.empty()) {
-        std::cout << "Unable to find the loopback interface. Skip test."
-                  << std::endl;
-        return;
-    }
-
+TEST_F(TestControlTest, Packet6ExchangeAddressOnly) {
     const int iterations_num = 100;
-    // Set number of iterations to 10.
-    processCmdLine("perfdhcp -l " + loopback_iface
-                   + " -e address-only"
-                   + " -6 -r 100 -n 10 -R 20 -L 10547 ::1");
-    int iterations_performed = 0;
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l fake -e address-only"
+                   " -6 -r 100 -n 10 -R 20 -L 10547 ::1");
     // Set number of received packets equal to number of iterations.
     // This simulates no packet drops.
     bool use_templates = false;
 
     // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges.
     // The test function generates server's responses and passes it to the
-    // TestControl class methods for processing. The number of exchanges
-    // actually performed is returned in 'iterations_performed' argument. If
-    // processing is successful, the number of performed iterations should be
-    // equal to the number of exchanges specified with the '-n' command line
-    // parameter (10 in this case). All exchanged packets carry the IA_NA option
-    // to simulate the IPv6 address acquisition and to verify that the IA_NA
-    // options returned by the server are processed correctly.
-    NakedTestControl tc;
-    testPkt6Exchange(iterations_num, iterations_num, use_templates,
-                     iterations_performed, tc);
-    // Actual number of iterations should be 10.
-    EXPECT_EQ(10, iterations_performed);
+    // TestControl class methods for processing. All exchanged packets carry
+    // the IA_NA option to simulate the IPv6 address acquisition and to verify
+    // that the IA_NA options returned by the server are processed correctly.
+    NakedTestControl tc(opt);
+    testPkt6Exchange(iterations_num, iterations_num, use_templates, tc);
+
+    EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Solicit + Request
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0);
 }
 
 TEST_F(TestControlTest, Packet6ExchangePrefixDelegation) {
-    // Get the local loopback interface to open socket on
-    // it and test packets exchanges. We don't want to fail
-    // the test if interface is not available.
-    std::string loopback_iface(getLocalLoopback());
-    if (loopback_iface.empty()) {
-        std::cout << "Unable to find the loopback interface. Skip test."
-                  << std::endl;
-        return;
-    }
-
     const int iterations_num = 100;
-    // Set number of iterations to 10.
-    processCmdLine("perfdhcp -l " + loopback_iface
-                   + " -e prefix-only"
-                   + " -6 -r 100 -n 10 -R 20 -L 10547 ::1");
-    int iterations_performed = 0;
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l fake -e prefix-only"
+                   " -6 -r 100 -n 10 -R 20 -L 10547 ::1");
     // Set number of received packets equal to number of iterations.
     // This simulates no packet drops.
     bool use_templates = false;
 
     // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges.
     // The test function generates server's responses and passes it to the
-    // TestControl class methods for processing. The number of exchanges
-    // actually performed is returned in 'iterations_performed' argument. If
-    // processing is successful, the number of performed iterations should be
-    // equal to the number of exchanges specified with the '-n' command line
-    // parameter (10 in this case). All exchanged packets carry the IA_PD option
-    // to simulate the Prefix Delegation and to verify that the IA_PD options
-    // returned by the server are processed correctly.
-    NakedTestControl tc;
-    testPkt6Exchange(iterations_num, iterations_num, use_templates,
-                     iterations_performed, tc);
-    // Actual number of iterations should be 10.
-    EXPECT_EQ(10, iterations_performed);
+    // TestControl class methods for processing. All exchanged packets carry
+    // the IA_PD option to simulate the Prefix Delegation and to verify that
+    // the IA_PD options returned by the server are processed correctly.
+    NakedTestControl tc(opt);
+    testPkt6Exchange(iterations_num, iterations_num, use_templates, tc);
+
+    EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Discovery + Request
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0);
 }
 
 TEST_F(TestControlTest, Packet6ExchangeAddressAndPrefix) {
-    // Get the local loopback interface to open socket on
-    // it and test packets exchanges. We don't want to fail
-    // the test if interface is not available.
-    std::string loopback_iface(getLocalLoopback());
-    if (loopback_iface.empty()) {
-        std::cout << "Unable to find the loopback interface. Skip test."
-                  << std::endl;
-        return;
-    }
-
     const int iterations_num = 100;
-    // Set number of iterations to 10.
-    processCmdLine("perfdhcp -l " + loopback_iface
-                   + " -e address-and-prefix"
-                   + " -6 -r 100 -n 10 -R 20 -L 10547 ::1");
-    int iterations_performed = 0;
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l fake -e address-and-prefix"
+                   " -6 -r 100 -n 10 -R 20 -L 10547 ::1");
     // Set number of received packets equal to number of iterations.
     // This simulates no packet drops.
     bool use_templates = false;
     // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges.
     // The test function generates server's responses and passes it to the
-    // TestControl class methods for processing. The number of exchanges
-    // actually performed is returned in 'iterations_performed' argument. If
-    // processing is successful, the number of performed iterations should be
-    // equal to the number of exchanges specified with the '-n' command line
-    // parameter (10 in this case).  All exchanged packets carry either IA_NA
-    // or IA_PD options to simulate the address and prefix acquisition with
-    // the single message and to verify that the IA_NA and IA_PD options
-    // returned by the server are processed correctly.
-    NakedTestControl tc;
-    testPkt6Exchange(iterations_num, iterations_num, use_templates,
-                     iterations_performed, tc);
-    // Actual number of iterations should be 10.
-    EXPECT_EQ(10, iterations_performed);
+    // TestControl class methods for processing. All exchanged packets carry
+    // either IA_NA or IA_PD options to simulate the address and prefix
+    // acquisition with the single message and to verify that the IA_NA
+    // and IA_PD options returned by the server are processed correctly.
+    NakedTestControl tc(opt);
+    testPkt6Exchange(iterations_num, iterations_num, use_templates, tc);
+
+    EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Solicit + Request
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0);
 }
 
 TEST_F(TestControlTest, PacketTemplates) {
@@ -1683,17 +1557,16 @@ TEST_F(TestControlTest, PacketTemplates) {
     ASSERT_TRUE(createTemplateFile(file1, template1, template1.size() * 2));
     ASSERT_TRUE(createTemplateFile(file2, template2, template2.size() * 2));
 
-    NakedTestControl tc;
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l 127.0.0.1"
+                   " -T " + file1 + " -T " + file2 + " all");
+    NakedTestControl tc(opt);
 
-    ASSERT_NO_THROW(
-        processCmdLine("perfdhcp -l 127.0.0.1"
-                       " -T " + file1 + " -T " + file2 + " all")
-    );
-    ASSERT_NO_THROW(tc.initPacketTemplates());
+    tc.initPacketTemplates();
     TestControl::TemplateBuffer buf1;
     TestControl::TemplateBuffer buf2;
-    ASSERT_NO_THROW(buf1 = tc.getTemplateBuffer(0));
-    ASSERT_NO_THROW(buf2 = tc.getTemplateBuffer(1));
+    buf1 = tc.getTemplateBuffer(0);
+    buf2 = tc.getTemplateBuffer(1);
     ASSERT_EQ(template1.size(), buf1.size());
     ASSERT_EQ(template2.size(), buf2.size());
     EXPECT_TRUE(std::equal(template1.begin(), template1.end(), buf1.begin()));
@@ -1704,25 +1577,19 @@ TEST_F(TestControlTest, PacketTemplates) {
     // Size of the file is 2 times larger than binary data size and it is always
     // even number. Substracting 1 makes file size odd.
     ASSERT_TRUE(createTemplateFile(file3, template1, template1.size() * 2 - 1));
-    ASSERT_NO_THROW(
-        processCmdLine("perfdhcp -l 127.0.0.1 -T " + file3 + " all")
-    );
+    processCmdLine(opt, "perfdhcp -l 127.0.0.1 -T " + file3 + " all");
     EXPECT_THROW(tc.initPacketTemplates(), isc::OutOfRange);
 
     // Try to read empty file.
     std::string file4("test4.hex");
     ASSERT_TRUE(createTemplateFile(file4, template2, 0));
-    ASSERT_NO_THROW(
-        processCmdLine("perfdhcp -l 127.0.0.1 -T " + file4 + " all")
-    );
+    processCmdLine(opt, "perfdhcp -l 127.0.0.1 -T " + file4 + " all");
     EXPECT_THROW(tc.initPacketTemplates(), isc::OutOfRange);
 
     // Try reading file with non hexadecimal characters.
     std::string file5("test5.hex");
     ASSERT_TRUE(createTemplateFile(file5, template1, template1.size() * 2, true));
-    ASSERT_NO_THROW(
-        processCmdLine("perfdhcp -l 127.0.0.1 -T " + file5 + " all")
-    );
+    processCmdLine(opt, "perfdhcp -l 127.0.0.1 -T " + file5 + " all");
     EXPECT_THROW(tc.initPacketTemplates(), isc::BadValue);
 }
 
@@ -1769,24 +1636,18 @@ TEST_F(TestControlTest, sendDiscoverExtraOpts) {
     // -xT - save first packet of each type for templates (useful for packet inspection)
     // -o 200,abcdef1234 - send option 200 with hex content: ab:cd:ef:12:34
     // -o 201,00 - send option 201 with hex content: 00
-    std::string loopback(getLocalLoopback());
-    ASSERT_NO_THROW(processCmdLine("perfdhcp -4 -l " + loopback
-                    + " -xT -L 10068 -o 200,abcdef1234 -o 201,00 -r 1 127.0.0.1"));
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -4 -l fake -xT -L 10068"
+                   " -o 200,abcdef1234 -o 201,00 -r 1 127.0.0.1");
 
     // Create test control and set up some basic defaults.
-    NakedTestControl tc;
-    tc.initializeStatsMgr();
+    NakedTestControl tc(opt);
     tc.registerOptionFactories();
     NakedTestControl::IncrementalGeneratorPtr gen(new NakedTestControl::IncrementalGenerator());
     tc.setTransidGenerator(gen);
 
-    // Socket is needed to send packets through the interface.
-    int sock_handle = 0;
-    ASSERT_NO_THROW(sock_handle = tc.openSocket());
-    PerfSocket sock(sock_handle);
-
     // Make tc send the packet. The first packet of each type is saved in templates.
-    ASSERT_NO_THROW(tc.sendDiscover4(sock));
+    tc.sendDiscover4();
 
     // Let's find the packet and see if it includes the right option.
     auto pkt_it = tc.template_packets_v4_.find(DHCPDISCOVER);
@@ -1798,32 +1659,26 @@ TEST_F(TestControlTest, sendDiscoverExtraOpts) {
 // Test checks if regular packet exchange inserts the extra v4 options
 // specified on command line.
 TEST_F(TestControlTest, Packet4ExchangeExtraOpts) {
-
-    // Get the local loopback interface to open socket on
-    // it and test packets exchanges. We don't want to fail
-    // the test if interface is not available.
-    std::string loopback(getLocalLoopback());
-    if (loopback.empty()) {
-        std::cout << "Unable to find the loopback interface. Skip test." << std::endl;
-        return;
-    }
-
     // Important paramters here:
     // -xT - save first packet of each type for templates (useful for packet inspection)
     // -o 200,abcdef1234 - send option 200 with hex content: ab:cd:ef:12:34
     // -o 201,00 - send option 201 with hex content: 00
     const int iterations_num = 1;
-    processCmdLine("perfdhcp -l " + loopback + " -4 " +
-                   "-o 200,abcdef1234 -o 201,00 " +
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l fake -4 -o 200,abcdef1234 -o 201,00 "
                    "-r 100 -n 10 -R 20 -xT -L 10547 127.0.0.1");
 
-    int iterations_performed = 0;
-    NakedTestControl tc;
+    NakedTestControl tc(opt);
     tc.registerOptionFactories();
 
     // Do the actual exchange.
-    testPkt4Exchange(iterations_num, iterations_num, false, iterations_performed, tc);
-    EXPECT_EQ(1, iterations_performed);
+    testPkt4Exchange(iterations_num, iterations_num, false, tc);
+
+    EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Discovery + Request
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::DO), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::DO), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RA), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RA), 0);
 
     // Check if Discover was recored and if it contains options 200 and 201.
     auto disc = tc.template_packets_v4_.find(DHCPDISCOVER);
@@ -1839,38 +1694,30 @@ TEST_F(TestControlTest, Packet4ExchangeExtraOpts) {
 // Test checks if regular packet exchange inserts the extra v6 options
 // specified on command line.
 TEST_F(TestControlTest, Packet6ExchangeExtraOpts) {
-    // Get the local loopback interface to open socket on
-    // it and test packets exchanges. We don't want to fail
-    // the test if interface is not available.
-    std::string loopback(getLocalLoopback());
-    if (loopback.empty()) {
-        std::cout << "Unable to find the loopback interface. Skip test."
-                  << std::endl;
-        return;
-    }
-
     // Important paramters here:
     // -xT - save first packet of each type for templates (useful for packet inspection)
     // -o 200,abcdef1234 - send option 200 with hex content: ab:cd:ef:12:34
     // -o 201,00 - send option 201 with hex content: 00
     const int iterations_num = 1;
-    processCmdLine("perfdhcp -l " + loopback
-                   + " -6 -e address-only"
-                   + " -xT -o 200,abcdef1234 -o 201,00 "
-                   + " -r 100 -n 10 -R 20 -L 10547 ::1");
-    int iterations_performed = 0;
-    // Set number of received packets equal to number of iterations.
-    // This simulates no packet drops.
+    CommandOptions opt;
+    processCmdLine(opt, "perfdhcp -l fake"
+                   " -6 -e address-only"
+                   " -xT -o 200,abcdef1234 -o 201,00 "
+                   " -r 100 -n 10 -R 20 -L 10547 ::1");
 
     // Simulate the number of Solicit-Advertise-Request-Reply (SARR) exchanges.
     // The test function generates server's responses and passes it to the
-    // TestControl class methods for processing. The number of exchanges
-    // actually performed is returned in 'iterations_performed' argument.
+    // TestControl class methods for processing.
     // First packet of each type is recorded as a template packet. The check
     // inspects this template to see if the expected options are really there.
-    NakedTestControl tc;
-    testPkt6Exchange(iterations_num, iterations_num, false,
-                     iterations_performed, tc);
+    NakedTestControl tc(opt);
+    testPkt6Exchange(iterations_num, iterations_num, false, tc);
+
+    EXPECT_EQ(tc.fake_sock_.sent_cnt_, iterations_num * 2); // Solicit + Request
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::SA), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::SA), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getSentPacketsNum(ExchangeType::RR), iterations_num);
+    EXPECT_EQ(tc.stats_mgr_.getRcvdPacketsNum(ExchangeType::RR), 0);
 
     // Check if Solicit was recored and if it contains options 200 and 201.
     auto sol = tc.template_packets_v6_.find(DHCPV6_SOLICIT);
@@ -1881,5 +1728,4 @@ TEST_F(TestControlTest, Packet6ExchangeExtraOpts) {
     auto req = tc.template_packets_v6_.find(DHCPV6_REQUEST);
     ASSERT_TRUE(req != tc.template_packets_v6_.end());
     checkOptions20x(req->second);
-
 }
index 959eaf9a81ef16ca2cb1f3b320d563afcfa52998..4dcca2c912410bb376608af229bd3ba75cb34f68 100644 (file)
@@ -399,7 +399,7 @@ public:
     /// @brief Set packet timestamp.
     ///
     /// Sets packet timestamp to arbitrary value.
-    /// It is used by perfdhcp tool and should be used elsewhere.
+    /// It is used by perfdhcp tool and should not be used elsewhere.
     void setTimestamp(boost::posix_time::ptime& timestamp) {
         timestamp_ = timestamp;
     }