From: Marcin Siodelski Date: Wed, 23 May 2012 13:51:44 +0000 (+0200) Subject: [1954] Updated with suggestions from first code review X-Git-Tag: trac2351_base~226^2~76^2~3 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c65a54347a7bceee54531a6a81209d1ec6ceb82b;p=thirdparty%2Fkea.git [1954] Updated with suggestions from first code review --- diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc index 002f508503..305621c81c 100644 --- a/tests/tools/perfdhcp/command_options.cc +++ b/tests/tools/perfdhcp/command_options.cc @@ -31,37 +31,26 @@ using namespace isc; namespace isc { namespace perfdhcp { -/// CommandOptions is a singleton implementation -CommandOptions* CommandOptions::instance_ = 0; - -void -CommandOptions::instanceCreate() { - if (instance_) { - // no need to do anything. Instance is already created. - // Who called it again anyway? Uh oh. Had to be us, as - // this is private method. - return; - } - instance_ = new CommandOptions(); -} - CommandOptions& CommandOptions::instance() { - if (instance_ == 0) { - instanceCreate(); - } - return (*instance_); + static CommandOptions options; + return (options); } void CommandOptions::reset() { + // default mac address used in DHCP messages + // if -b mac= was not specified uint8_t mac[6] = { 0x0, 0xC, 0x1, 0x2, 0x3, 0x4 }; - double lt[2] = { 1., 1. }; + + // default packet drop time if -D parameter + // was not specified + double dt[2] = { 1., 1. }; // We don't use constructor initialization list because we // will need to reset all members many times to perform unit tests ipversion_ = 0; - exchange_mode_ = DORR_SARR; + exchange_mode_ = DORA_SARR; rate_ = 0; report_delay_ = 0; random_range_ = 0; @@ -70,12 +59,12 @@ CommandOptions::reset() { base_.resize(0); num_request_.resize(0); period_ = 0; - lost_time_set_ = 0; - lost_time_.assign(lt, lt + 2); + drop_time_set_ = 0; + drop_time_.assign(dt, dt + 2); max_drop_set_ = 0; - max_drop_.resize(0); - max_pdrop_.resize(0); - localname_.resize(0); + max_drop_.clear(); + max_pdrop_.clear(); + localname_.clear(); is_interface_ = false; preload_ = 0; aggressivity_ = 1; @@ -85,245 +74,234 @@ CommandOptions::reset() { broadcast_ = false; rapid_commit_ = false; use_first_ = false; - template_file_.resize(0); - rnd_offset_.resize(0); - xid_offset_.resize(0); + template_file_.clear(); + rnd_offset_.clear(); + xid_offset_.clear(); elp_offset_ = -1; sid_offset_ = -1; rip_offset_ = -1; - diags_.resize(0); - wrapped_.resize(0); - server_name_.resize(0); + diags_.clear(); + wrapped_.clear(); + server_name_.clear(); } void -CommandOptions::parse(int argc, char** const argv, bool force_reset /*=false */) { - if (force_reset) { - reset(); - } +CommandOptions::parse(int argc, char** const argv) { // Reset internal variables used by getopt // to eliminate undefined behavior when // parsing different command lines multiple times optind = 1; opterr = 0; - initialize(argc, argv); validate(); } void -CommandOptions::initialize(int argc, char** const argv) { - char opt = 0; - char* pc = NULL; - int nr = 0, of = 0; - int di = 0; - float dp = 0; - long long r = 0; +CommandOptions::initialize(int argc, char** argv) { + char opt = 0; // subsequent options returned by getopt() + std::string dropArg; // value of -Dargument + size_t percentLoc = 0; // location of % sign in -D + double dropPercent = 0; // % value (1..100) in -D<%value> + int numDrops = 0; // max number of drops specified in -D + int numReq = 0; // max number of dropped requests in -n + int offsetArg = 0; // temp variable holiding offset arguments + std::string sarg; // temp variable for string args // in this section we collect argument values from command line // they will be tuned and validated elsewhere while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:s:iBc1T:X:O:E:S:I:x:w:")) != -1) { - switch (opt) { - case 'h': - usage(); - break; + switch (opt) { + case 'h': + usage(); + return; + + case 'v': + version(); + return; + + case '4': + check(ipversion_ == 6, "IP version already set to 6"); + ipversion_ = 4; + break; - case 'v': - version(); - break; + case '6': + check(ipversion_ == 4, "IP version already set to 4"); + ipversion_ = 6; + break; - case '4': - check(ipversion_ == 6, "IP version already set to 6"); - ipversion_ = 4; - break; - - case '6': - check(ipversion_ == 4, "IP version already set to 4"); - ipversion_ = 6; - break; - - case 'r': - rate_ = boost::lexical_cast(optarg); - check(rate_ <= 0, "rate_ must be a positive integer"); - break; - - case 't': - report_delay_ = boost::lexical_cast(optarg); - check(report_delay_ <= 0, "report_delay_ must be a positive integer"); - break; - - case 'R': - r = boost::lexical_cast(optarg); - check(r < 0, "random_range_ must not be a negative integer"); - random_range_ = static_cast(r); - if ((random_range_ != 0) && (random_range_ != std::numeric_limits::max())) { - uint32_t s = random_range_ + 1; - uint64_t b = std::numeric_limits::max() + 1, m; - - m = (b / s) * s; - if (m == b) - max_random_ = 0; - else - max_random_ = static_cast (m); - } - break; - - case 'b': - check(base_.size() > 3, "too many bases"); - base_.push_back(optarg); - decodeBase(base_.back()); - break; - - case 'n': - nr = boost::lexical_cast(optarg); - check(nr <= 0, "num-request must be a positive integer"); - if (num_request_.size() >= 2) { - isc_throw(isc::InvalidParameter, - "too many num-request values"); - } - num_request_.push_back(nr); - break; - - case 'p': - period_ = boost::lexical_cast(optarg); - check(period_ <= 0, "test-period must be a positive integer"); - break; - - case 'd': - lost_time_[lost_time_set_] = boost::lexical_cast(optarg); - check(lost_time_[lost_time_set_] <= 0., "drop-time must be a positive number"); - lost_time_set_ = 1; - break; - - case 'D': - pc = strchr(optarg, '%'); - if (pc != NULL) { - *pc = '\0'; - dp = boost::lexical_cast(optarg); - max_pdrop_[max_drop_set_] = boost::lexical_cast(optarg); - check((dp <= 0) || (dp >= 100), "invalid drop-time percentage"); - max_pdrop_.push_back(dp); + case 'r': + rate_ = positiveInteger("rate_ must be a positive integer"); break; - } - di = boost::lexical_cast(optarg); - check(di <= 0, "max-drop must be a positive integer"); - max_drop_.push_back(di); - break; - - case 'l': - localname_ = optarg; - break; - - case 'P': - preload_ = boost::lexical_cast(optarg); - check(preload_ < 0, "preload must not be a negative integer"); - break; - - case 'a': - aggressivity_ = boost::lexical_cast(optarg); - check(aggressivity_ <= 0, "aggressivity must be a positive integer"); - break; - - case 'L': - local_port_ = boost::lexical_cast(optarg); - check(local_port_ < 0, "local-port must not be a negative integer"); - check(local_port_ > static_cast( std::numeric_limits::max()), - "local-port must be lower than " + - boost::lexical_cast(std::numeric_limits::max())); - break; - - case 's': - seeded_ = true; - seed_ = boost::lexical_cast(optarg); - break; - - case 'i': - exchange_mode_ = DO_SA; - break; - - case 'B': - broadcast_ = 1; - break; - - case 'c': - rapid_commit_ = 1; - break; - - case '1': - use_first_ = 1; - break; - - case 'T': - switch (template_file_.size()) { - case 0: - case 1: - template_file_.push_back(std::string(optarg)); + + case 't': + report_delay_ = positiveInteger("report_delay_ must be a positive integer"); break; - default: - isc_throw(isc::InvalidParameter, - "template-files are already set"); - } - break; + case 'R': + initRandomRange(optarg); + break; - case 'X': - of = boost::lexical_cast(optarg); - check(of <= 0, "xid-offset must be a positive integer"); - if (xid_offset_.size() >= 2) { - xid_offset_.resize(0); - } - xid_offset_.push_back(of); - break; - - case 'O': - of = boost::lexical_cast(optarg); - check(of < 3, "random-offset must be greater than 3"); - if (rnd_offset_.size() >= 2) { - rnd_offset_.resize(0); - } - rnd_offset_.push_back(of); - break; + case 'b': + check(base_.size() > 3, "too many bases"); + base_.push_back(optarg); + decodeBase(base_.back()); + break; + + case 'n': + numReq = positiveInteger("num-request must be a positive integer"); + if (num_request_.size() >= 2) { + isc_throw(isc::InvalidParameter, + "too many num-request values"); + } + num_request_.push_back(numReq); + break; - case 'E': - elp_offset_ = boost::lexical_cast(optarg); - check(elp_offset_ < 0, "time-offset must not be a negative integer"); - break; + case 'p': + period_ = positiveInteger("test-period must be a positive integer"); + break; - case 'S': - sid_offset_ = boost::lexical_cast(optarg); - check(sid_offset_ <= 0, "srvid-offset must be a positive integer"); - break; + case 'd': + try { + drop_time_[drop_time_set_] = boost::lexical_cast(optarg); + } catch (boost::bad_lexical_cast&) { + isc_throw(isc::InvalidParameter, + "value of drop time: -d must be positive number"); + } + check(drop_time_[drop_time_set_] <= 0., "drop-time must be a positive number"); + drop_time_set_ = 1; + break; - case 'I': - rip_offset_ = boost::lexical_cast(optarg); - check(rip_offset_ <= 0, "ip-offset must be a positive integer"); - break; + case 'D': + dropArg = std::string(optarg); + percentLoc = dropArg.find('%'); + if ((percentLoc) != std::string::npos) { + dropPercent = boost::lexical_cast(dropArg.substr(percentLoc + 1)); + check((dropPercent <= 0) || (dropPercent >= 100), + "value of drop percentage: -D<%value> must be 0..100"); + max_pdrop_.push_back(dropPercent); + break; + } + numDrops = positiveInteger("value of max drops number: -d must be a positive integer"); + max_drop_.push_back(numDrops); + break; - case 'x': - diags_.assign(optarg); - break; + case 'l': + localname_ = std::string(optarg); + break; - case 'w': - wrapped_.assign(optarg); - break; + case 'P': + preload_ = nonNegativeInteger("number of preload packets: -P must not be " + "a negative integer"); + break; - default: - isc_throw(isc::InvalidParameter, - "unknown command line option"); - } + case 'a': + aggressivity_ = positiveInteger("value of aggressivity: -a must be a positive integer"); + break; + + case 'L': + local_port_ = nonNegativeInteger("value of local port: -L must not be a negative integer"); + check(local_port_ > static_cast( std::numeric_limits::max()), + "local-port must be lower than " + + boost::lexical_cast(std::numeric_limits::max())); + break; + + case 's': + seed_ = static_cast + (nonNegativeInteger("value of -s must be non-negative integer")); + seeded_ = seed_ > 0 ? true : false; + break; + + case 'i': + exchange_mode_ = DO_SA; + break; + + case 'B': + broadcast_ = true; + break; + + case 'c': + rapid_commit_ = true; + break; + + case '1': + use_first_ = true; + break; + + case 'T': + switch (template_file_.size()) { + case 0: + case 1: + sarg = nonEmptyString("template file name not specified, expected -T"); + template_file_.push_back(sarg); + break; + default: + isc_throw(isc::InvalidParameter, + "template-files are already set"); + } + break; + + case 'X': + offsetArg = positiveInteger("value of transaction id: -X must be a positive integer"); + if (xid_offset_.size() >= 2) { + xid_offset_.clear(); + } + xid_offset_.push_back(offsetArg); + break; + + case 'O': + try { + offsetArg = boost::lexical_cast(optarg); + } catch (boost::bad_lexical_cast&) { + isc_throw(isc::InvalidParameter, "value of random random-offset: -O " + "must be greater than 3"); + } + check(offsetArg < 3, "value of random random-offset: -O must be greater than 3 "); + if (rnd_offset_.size() >= 2) { + rnd_offset_.clear(); + } + rnd_offset_.push_back(offsetArg); + break; + + case 'E': + elp_offset_ = nonNegativeInteger("value of time-offset: -E must not be a negative integer"); + break; + + case 'S': + sid_offset_ = positiveInteger("value of server id offset: -S must be a positive integer"); + break; + + case 'I': + rip_offset_ = positiveInteger("value of ip addr offset: -I must be a positive integer"); + break; + + case 'x': + diags_ = nonEmptyString("value of diagnostics selectors: -x must be specified"); + break; + + case 'w': + wrapped_ = nonEmptyString("command for wrapped mode: -w must be specified"); + break; + + default: + isc_throw(isc::InvalidParameter, + "unknown command line option"); + } } // Tune ip version as it still might be zero // if not specified in command line was used - if (ipversion_ == 0) + if (ipversion_ == 0) { ipversion_ = 4; + } // Tune up template file lists if (template_file_.size() > 1) { - if (xid_offset_.size() == 1) + if (xid_offset_.size() == 1) { xid_offset_.push_back(xid_offset_[0]); - if (rnd_offset_.size() == 1) + } + if (rnd_offset_.size() == 1) { rnd_offset_.push_back(rnd_offset_[0]); + } } // Get server argument @@ -331,15 +309,12 @@ CommandOptions::initialize(int argc, char** const argv) { if (optind == argc - 1) { server_name_ = argv[optind]; // Decode special cases - if ((ipversion_ == 4) && - (server_name_.compare("all") == 0)) { + if ((ipversion_ == 4) && (server_name_.compare("all") == 0)) { broadcast_ = 1; server_name_ = "255.255.255.255"; - } else if ((ipversion_ == 6) && - (server_name_.compare("all") == 0)) { + } else if ((ipversion_ == 6) && (server_name_.compare("all") == 0)) { server_name_ = "FF02::1:2"; - } else if ((ipversion_ == 6) && - (server_name_.compare("servers") == 0)) { + } else if ((ipversion_ == 6) && (server_name_.compare("servers") == 0)) { server_name_ = "FF05::1:3"; } } @@ -347,16 +322,54 @@ CommandOptions::initialize(int argc, char** const argv) { // TODO handle -l option with IfaceManager when it is created } +void +CommandOptions::initRandomRange(const std::string& opt) { + const std::string errmsg = "value of -R must be non-negative integer"; + long long randomRange = 0; + try { + randomRange = boost::lexical_cast(opt); + } catch (boost::bad_lexical_cast&) { + isc_throw(isc::InvalidParameter, errmsg.c_str()); + } + check(randomRange < 0, errmsg.c_str()); + try { + random_range_ = boost::lexical_cast(randomRange); + } catch (boost::bad_lexical_cast&) { + isc_throw(isc::InvalidParameter, errmsg); + } + + if ((random_range_ != 0) && (random_range_ != std::numeric_limits::max())) { + // randomization range is restricted by number of clients + uint32_t maxTargetRand = random_range_ + 1; + // randomization with random() function is within uint32_t range + uint64_t maxRandom = std::numeric_limits::max() + 1; + // restrict max randomized value to + // multiple of number of clients + uint64_t restrictedRandom = (maxRandom / maxTargetRand) * maxTargetRand; + if (restrictedRandom == maxRandom) { + // no need to restrict max random value + // uint32_t range is already multiple + // of number of clients range + max_random_ = 0; + } else { + max_random_ = static_cast (restrictedRandom); + } + } +} + void CommandOptions::decodeBase(const std::string& base) { std::string b(base); boost::algorithm::to_lower(b); - // Currently we only support MAC and DUID + // Currently we only support mac and duid if ((b.substr(0, 4) == "mac=") || (b.substr(0, 6) == "ether=")) { decodeMac(b); } else if (b.substr(0, 5) == "duid=") { decodeDuid(b); + } else { + isc_throw(isc::InvalidParameter, + "base value not provided as -b, expected -b mac= or -b duid="); } } @@ -364,33 +377,38 @@ void CommandOptions::decodeMac(const std::string& base) { // Strip string from mac= size_t found = base.find('='); - check(found == std::string::npos, "expected -b format for MAC address is -b MAC=00::0C::01::02::03::04"); + static const char* errmsg = "expected -b format for mac address is -b mac=00::0C::01::02::03::04"; + check(found == std::string::npos, errmsg); - // Decode MAC address to vector of uint8_t - std::istringstream s1(base.substr(found+1)); + // Decode mac address to vector of uint8_t + std::istringstream s1(base.substr(found + 1)); std::string token; - mac_prefix_.resize(0); + mac_prefix_.clear(); while (std::getline(s1, token, ':')) { unsigned int ui = 0; std::istringstream s2(token); if (token.length() > 0) { - s2 >> std::hex >> ui >> std::dec; - check(s2.fail() || (ui > 0xFF), - "expected -b format for MAC address is -b MAC=00::0C::01::02::03::04"); + try { + ui = convertHexString(token); + } catch (isc::InvalidParameter&) { + isc_throw(isc::InvalidParameter, + "invalid characters in MAC provided"); + + } mac_prefix_.push_back(ui); } } - check(mac_prefix_.size() != 6, "expected -b format for MAC address is -b MAC=00::0C::01::02::03::04"); + check(mac_prefix_.size() != 6, errmsg); } void CommandOptions::decodeDuid(const std::string& base) { // Strip argument from duid= size_t found = base.find('='); - check(found == std::string::npos, "expected -b format for DUID is -b DUID="); + check(found == std::string::npos, "expected -b format for duid is -b duid="); std::string b = base.substr(found + 1); - // DUID must have even number of digits and must not be longer than 64 bytes + // duid must have even number of digits and must not be longer than 64 bytes check(b.length() & 1, "odd number of hexadecimal digits in duid"); check(b.length() > 128, "duid too large"); check(b.length() == 0, "no duid specified"); @@ -398,13 +416,34 @@ CommandOptions::decodeDuid(const std::string& base) { // Turn pairs of hex digits into vector of bytes for (int i = 0; i < b.length(); i += 2) { unsigned int ui = 0; - std::istringstream ss(b.substr(i, 2)); - check(!(ss >> std::hex >> ui >> std::dec) || (ui > 0xFF), - "illegal characters " + b + " in duid"); + try { + ui = convertHexString(b.substr(i, 2)); + } catch (isc::InvalidParameter&) { + isc_throw(isc::InvalidParameter, + "invalid characters in DUID provided, exepected hex digits"); + } duid_prefix_.push_back(static_cast(ui)); } } +uint8_t +CommandOptions::convertHexString(const std::string& s) { + unsigned int ui = 0; + for (int i = 0; i < s.length(); ++i) { + if (!std::isxdigit(s[i])) { + isc_throw(isc::InvalidParameter, + "The following digit: " << s[i] << " in " + << s << "is not hexadecimal"); + } + } + std::istringstream ss(s); + ss >> std::hex >> ui >> std::dec; + if (ui > 0xFF) { + isc_throw(isc::InvalidParameter, "Can't convert more than two hex digits to byte"); + } + return ui; +} + void CommandOptions::validate() const { check((getIpVersion() != 4) && (isBroadcast() != 0), @@ -413,7 +452,7 @@ CommandOptions::validate() const { "-6 (IPv6) must be set to use -c"); check((getExchangeMode() == DO_SA) && (getNumRequests().size() > 1), "second -n is not compatible with -i"); - check((getExchangeMode() == DO_SA) && (getLostTime()[1] != 1.), + check((getExchangeMode() == DO_SA) && (getDropTime()[1] != 1.), "second -d is not compatible with -i"); check((getExchangeMode() == DO_SA) && ((getMaxDrop().size() > 1) || (getMaxDropPercentage().size() > 1)), @@ -422,15 +461,15 @@ CommandOptions::validate() const { "-1 is not compatible with -i\n"); check((getExchangeMode() == DO_SA) && (getTemplateFiles().size() > 1), "second -T is not compatible with -i\n"); - check((getExchangeMode() == DO_SA) && (getXidOffset().size() > 1), + check((getExchangeMode() == DO_SA) && (getTransactionIdOffset().size() > 1), "second -X is not compatible with -i\n"); - check((getExchangeMode() == DO_SA) && (getRndOffset().size() > 1), + check((getExchangeMode() == DO_SA) && (getRandomOffset().size() > 1), "second -O= 0), + check((getExchangeMode() == DO_SA) && (getElapsedTimeOffset() >= 0), "-E is not compatible with -i\n"); - check((getExchangeMode() == DO_SA) && (getSidOffset() >= 0), + check((getExchangeMode() == DO_SA) && (getServerIdOffset() >= 0), "-S is not compatible with -i\n"); - check((getExchangeMode() == DO_SA) && (getRipOffset() >= 0), + check((getExchangeMode() == DO_SA) && (getRequestedIpOffset() >= 0), "-I is not compatible with -i\n"); check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0), "-i must be set to use -c\n"); @@ -443,16 +482,16 @@ CommandOptions::validate() const { check((getRate() == 0) && ((getMaxDrop().size() > 0) || getMaxDropPercentage().size() > 0), "-r must be set to use -D\n"); - check((getTemplateFiles().size() < getXidOffset().size()), + check((getTemplateFiles().size() < getTransactionIdOffset().size()), "-T must be set to use -X\n"); - check((getTemplateFiles().size() < getRndOffset().size()), + check((getTemplateFiles().size() < getRandomOffset().size()), "-T must be set to use -O\n"); - check((getTemplateFiles().size() < 2) && (getElpOffset() >= 0), + check((getTemplateFiles().size() < 2) && (getElapsedTimeOffset() >= 0), "second/request -T must be set to use -E\n"); - check((getTemplateFiles().size() < 2) && (getSidOffset() >= 0), + check((getTemplateFiles().size() < 2) && (getServerIdOffset() >= 0), "second/request -T must be set to " "use -S\n"); - check((getTemplateFiles().size() < 2) && (getRipOffset() >= 0), + check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0), "second/request -T must be set to " "use -I\n"); @@ -467,6 +506,37 @@ CommandOptions::check(bool condition, const std::string& errmsg) const { } } +int +CommandOptions::positiveInteger(const std::string& errmsg) { + try { + int value = boost::lexical_cast(optarg); + check(value <= 0, errmsg); + return (value); + } catch (boost::bad_lexical_cast&) { + isc_throw(InvalidParameter, errmsg); + } +} + +int +CommandOptions::nonNegativeInteger(const std::string& errmsg) { + try { + int value = boost::lexical_cast(optarg); + check(value < 0, errmsg); + return (value); + } catch (boost::bad_lexical_cast&) { + isc_throw(InvalidParameter, errmsg); + } +} + +std::string +CommandOptions::nonEmptyString(const std::string& errmsg) { + std::string sarg = optarg; + if (sarg.length() == 0) { + isc_throw(isc::InvalidParameter, errmsg); + } + return sarg; +} + void CommandOptions::usage() const { fprintf(stdout, "%s", @@ -477,7 +547,7 @@ CommandOptions::usage() const { " [-T] [-X] [-O] [-S] [-I]\n" " [-x] [-w] [server]\n" -"\f\n" +"\n" "The [server] argument is the name/address of the DHCP server to\n" "contact. For DHCPv4 operation, exchanges are initiated by\n" "transmitting a DHCP DISCOVER to this address.\n" @@ -501,10 +571,10 @@ CommandOptions::usage() const { "-6: DHCPv6 operation. This is incompatible with the -4 option.\n" "-a: When the target sending rate is not yet reached,\n" " control how many exchanges are initiated before the next pause.\n" -"-b: The base MAC, DUID, IP, etc, used to simulate different\n" +"-b: The base mac, duid, IP, etc, used to simulate different\n" " clients. This can be specified multiple times, each instance is\n" " in the = form, for instance:\n" -" (and default) MAC=00:0c:01:02:03:04.\n" +" (and default) mac=00:0c:01:02:03:04.\n" "-d: Specify the time after which a request is treated as\n" " having been lost. The value is given in seconds and may contain a\n" " fractional component. The default is 1 second.\n" diff --git a/tests/tools/perfdhcp/command_options.h b/tests/tools/perfdhcp/command_options.h index e0e3ea91b5..6c7c4b6592 100644 --- a/tests/tools/perfdhcp/command_options.h +++ b/tests/tools/perfdhcp/command_options.h @@ -30,9 +30,10 @@ namespace perfdhcp { /// class CommandOptions : public boost::noncopyable { public: + /// 2-way (cmd line param -i) or 4-way exchanges enum ExchangeMode { DO_SA, - DORR_SARR + DORA_SARR }; /// CommandOptions is a singleton class. This method returns reference @@ -54,7 +55,7 @@ public: /// \param argv Argument value array passed to main(). /// \param force_reset Force reset of state variables /// \throws isc::InvalidParameter if parsing fails - void parse(int argc, char** const argv, bool force_reset = false); + void parse(int argc, char** const argv); /// \brief Returns IP version /// @@ -109,7 +110,7 @@ public: /// \brief Returns lost time /// /// \return return time before request is lost - std::vector getLostTime() const { return lost_time_; } + std::vector getDropTime() const { return drop_time_; } /// \brief Returns max drops number /// @@ -179,27 +180,27 @@ public: /// brief Returns template offsets for xid /// /// \return template offsets for xid - std::vector getXidOffset() const { return xid_offset_; } + std::vector getTransactionIdOffset() const { return xid_offset_; } /// \brief Returns template offsets for rnd /// /// \return template offsets for rnd - std::vector getRndOffset() const { return rnd_offset_; } + std::vector getRandomOffset() const { return rnd_offset_; } /// \brief Returns template offset for elapsed time /// /// \return template offset for elapsed time - int getElpOffset() const { return elp_offset_; } + int getElapsedTimeOffset() const { return elp_offset_; } /// \brief Returns template offset for server-ID /// /// \return template offset for server-ID - int getSidOffset() const { return sid_offset_; } + int getServerIdOffset() const { return sid_offset_; } /// \brief Returns template offset for requested IP /// /// \return template offset for requested IP - int getRipOffset() const { return rip_offset_; } + int getRequestedIpOffset() const { return rip_offset_; } /// \brief Returns diagnostic selectors /// @@ -226,7 +227,7 @@ public: /// Prints perfdhcp version void version() const; -protected: +private: /// \brief Default Constructor /// @@ -237,9 +238,6 @@ protected: } private: - /// \brief Create instance of the singleton class - static void instanceCreate(); - /// \brief Initializes class members based command line /// /// Reads each command line parameter and sets class member values @@ -247,53 +245,102 @@ private: /// \param argc Argument count passed to main(). /// \param argv Argument value array passed to main(). /// \throws isc::InvalidParameter if command line options initialization fails - void initialize(int argc, char** const argv); + void initialize(int argc, char** argv); /// \brief Validates initialized options /// - /// \throws isc::InvalidPrameter if command line validation fails + /// \throws isc::InvalidParameter if command line validation fails void validate() const; - /// \brief Checks given condition + /// \brief Throws !InvalidParameter exception if condition is true + /// + /// Convenience function that throws an !InvalidParameter exception if + /// the condition argument is true /// /// \param condition Condition to be checked /// \param errmsg Error message in exception - /// \throws isc::InvalidParameter if check fails + /// \throws isc::InvalidParameter if condition argument true inline void check(bool condition, const std::string& errmsg) const; - /// \brief Decodes base provided with -b + /// \brief Casts command line arg to positive int + /// + /// \param errmsg Error message if lexical cast fails + /// \throw InvalidParameter if lexical cast fails + int positiveInteger(const std::string& errmsg); + + /// \brief Casts command line arg to non-negative int + /// + /// \param errmsg Error message if lexical cast fails + /// \throw InvalidParameter if lexical cast fails + int nonNegativeInteger(const std::string& errmsg); + + /// \brief Returns command line string if it is non-empty + /// + /// \param errmsg Error message if string is empty + /// \throw InvalidParameter if string is empty + std::string nonEmptyString(const std::string& errmsg); + + /// \brief Calculates max_random_ and random_range_ + /// + /// \param opt Value of -R option + /// \throw InvalidParameter if -R is wrong + void initRandomRange(const std::string& opt); + + /// \brief Decodes base provided with -b + /// + /// Function decodes parameters given with -b + /// parameter. Currently it supports the following command line options e.g.: + /// -b mac=00:01:02:03:04:05 + /// -b duid=0F1234 (duid can be up to 128 hex digits) + // Function will decode 00:01:02:03:04:05 and/or + /// 0F1234 repsectively and initialize mac_prefix_ + /// and/or duid_prefix_ members /// /// \param base Base in string format /// \throws isc::InvalidParameter if base is invalid void decodeBase(const std::string& base); - /// \brief Decodes base MAC address provided with -b + /// \brief Decodes base MAC address provided with -b /// - /// \param base MAC address in string format - /// \throws isc::InvalidParameter if base is invalid + /// Function decodes parameter given as -b mac=00:01:02:03:04:05 + /// Function will decode 00:01:02:03:04:05 initialize mac_prefix_ + /// class member. + /// Provided MAC address is for example only + /// + /// \param base Base string given as -b mac=00:01:02:03:04:05 + /// \throws isc::InvalidParameter if mac address is invalid void decodeMac(const std::string& base); - /// \brief Decodes base DUID provided with -b + /// \brief Decodes base DUID provided with -b /// - /// \param base DUID in string format - /// \throws isc::InvalidParameter if base is invalid + /// Function decodes parameter given as -b duid=0F1234 + /// Function will decode 0F1234 and initialize duid_prefix_ + /// class member. + /// Provided DUID is for example only. + /// + /// \param base Base string given as -b mac=00:01:02:03:04:05 + /// \throws isc::InvalidParameter if DUID is invalid void decodeDuid(const std::string& base); - static CommandOptions * instance_; ///< A pointer to sole instance of this class + /// \brief converts two digit hex string to byte + /// + /// \param s hex string e.g. AF + /// \throw isc::InvalidParameter if string does not represent hex byte + uint8_t convertHexString(const std::string& s); uint8_t ipversion_; ///< IP version ExchangeMode exchange_mode_ ; ///< Packet exchange mode (e.g. DORR/SARR) int rate_; ///< rate in exchange per second int report_delay_; ///< delay between two reports - uint32_t random_range_; ///< randomization range + uint32_t random_range_; ///< number of simulated clients uint32_t max_random_; ///< maximum random value std::vector mac_prefix_; ///< MAC addr prefix std::vector duid_prefix_; ///< DUID prefix std::vector base_; ///< base values for xid std::vector num_request_; ///< number of exchanges int period_; ///< test period - int lost_time_set_; ///< losttime[0] was set - std::vector lost_time_; ///< time before a request is lost + int drop_time_set_; ///< losttime[0] was set + std::vector drop_time_; ///< time before a request is lost int max_drop_set_; ///< max{p}drop[0] was set std::vector max_drop_; ///< maximum number of lost requests std::vector max_pdrop_; ///< maximum percentage diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc index 4ea6900a2d..9119b3229b 100644 --- a/tests/tools/perfdhcp/tests/command_options_unittest.cc +++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc @@ -26,12 +26,11 @@ using namespace isc; using namespace isc::perfdhcp; /// \brief Test Fixture Class -class CommandOptionsTest : public virtual ::testing::Test, - public virtual CommandOptions +class CommandOptionsTest : public virtual ::testing::Test { public: /// \brief Default Constructor - CommandOptionsTest() : CommandOptions() { } + CommandOptionsTest() { } protected: @@ -43,9 +42,11 @@ protected: /// \param s Command line to parse /// \return non-zero if parsing failed void process(const std::string& s) { + CommandOptions& opt = CommandOptions::instance(); int argc = 0; char** argv = tokenizeString(s, &argc); - parse(argc, argv, true); + opt.reset(); + opt.parse(argc, argv); for(int i = 0; i < argc; ++i) { free(argv[i]); argv[i] = NULL; @@ -57,43 +58,50 @@ protected: /// /// Check if initialized values are correct void checkDefaults() { - EXPECT_EQ(4, getIpVersion()); - EXPECT_EQ(DORR_SARR, getExchangeMode()); - EXPECT_EQ(0, getRate()); - EXPECT_EQ(0, getReportDelay()); - EXPECT_EQ(0, getRandomRange()); - // EXPECT_EQ(0, max_random_); - // TODO MAC and DUID checks - EXPECT_EQ(0, getBase().size()); - EXPECT_EQ(0, getNumRequests().size()); - EXPECT_EQ(0, getPeriod()); - for (int i = 0; i < getLostTime().size(); ++i) { - EXPECT_DOUBLE_EQ(1, getLostTime()[i]); + CommandOptions& opt = CommandOptions::instance(); + process("perfdhcp"); + EXPECT_EQ(4, opt.getIpVersion()); + EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode()); + EXPECT_EQ(0, opt.getRate()); + EXPECT_EQ(0, opt.getReportDelay()); + EXPECT_EQ(0, opt.getRandomRange()); + + // default mac + uint8_t mac[6] = { 0x00, 0x0C, 0x01, 0x02, 0x03, 0x04 }; + std::vector v1 = opt.getMacPrefix(); + ASSERT_EQ(6, v1.size()); + EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); + + EXPECT_EQ(0, opt.getBase().size()); + EXPECT_EQ(0, opt.getNumRequests().size()); + EXPECT_EQ(0, opt.getPeriod()); + for (int i = 0; i < opt.getDropTime().size(); ++i) { + EXPECT_DOUBLE_EQ(1, opt.getDropTime()[i]); } - ASSERT_EQ(getMaxDrop().size(), getMaxDropPercentage().size()); - for (int i = 0; i < getMaxDrop().size(); ++i) { - EXPECT_EQ(0, getMaxDrop()[i]); - EXPECT_EQ(0, getMaxDropPercentage()[i]); + ASSERT_EQ(opt.getMaxDrop().size(), opt.getMaxDropPercentage().size()); + for (int i = 0; i < opt.getMaxDrop().size(); ++i) { + EXPECT_EQ(0, opt.getMaxDrop()[i]); + EXPECT_EQ(0, opt.getMaxDropPercentage()[i]); } - EXPECT_EQ("", getLocalName()); - EXPECT_FALSE(isInterface()); - EXPECT_EQ(0, getPreload()); - EXPECT_EQ(1, getAggressivity()); - EXPECT_EQ(0, getLocalPort()); - EXPECT_FALSE(isSeeded()); - EXPECT_EQ(0, getSeed()); - EXPECT_FALSE(isBroadcast()); - EXPECT_FALSE(isRapidCommit()); - EXPECT_FALSE(isUseFirst()); - EXPECT_EQ(0, getTemplateFiles().size()); - EXPECT_EQ(0, getXidOffset().size()); - EXPECT_EQ(0, getRndOffset().size()); - EXPECT_GT(0, getElpOffset()); - EXPECT_GT(0, getSidOffset()); - EXPECT_GT(0, getRipOffset()); - EXPECT_EQ("", getDiags()); - EXPECT_EQ("", getWrapped()); - EXPECT_EQ("", getServerName()); + EXPECT_EQ("", opt.getLocalName()); + EXPECT_FALSE(opt.isInterface()); + EXPECT_EQ(0, opt.getPreload()); + EXPECT_EQ(1, opt.getAggressivity()); + 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("", opt.getServerName()); } /// \brief Split string to array of C-strings @@ -134,27 +142,33 @@ TEST_F(CommandOptionsTest, Defaults) { } TEST_F(CommandOptionsTest, UseFirst) { - process("perfdhcp -l ethx -1 -B"); - EXPECT_TRUE(isUseFirst()); + CommandOptions& opt = CommandOptions::instance(); + process("perfdhcp -1 -B -l ethx"); + EXPECT_TRUE(opt.isUseFirst()); } - TEST_F(CommandOptionsTest, IpVersion) { + CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -6 -l ethx -c -i"); - EXPECT_EQ(6, getIpVersion()); - EXPECT_EQ("ethx", getLocalName()); - EXPECT_TRUE(isRapidCommit()); - EXPECT_FALSE(isBroadcast()); + EXPECT_EQ(6, opt.getIpVersion()); + EXPECT_EQ("ethx", opt.getLocalName()); + EXPECT_TRUE(opt.isRapidCommit()); + EXPECT_FALSE(opt.isBroadcast()); process("perfdhcp -4 -B -l ethx"); - EXPECT_EQ(4, getIpVersion()); - EXPECT_TRUE(isBroadcast()); - EXPECT_FALSE(isRapidCommit()); + EXPECT_EQ(4, opt.getIpVersion()); + EXPECT_TRUE(opt.isBroadcast()); + EXPECT_FALSE(opt.isRapidCommit()); + + // Negative test cases EXPECT_THROW(process("perfdhcp -6 -B -l ethx"), isc::InvalidParameter); EXPECT_THROW(process("perfdhcp -c -l ethx"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Rate) { + CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -4 -r 10 -l ethx"); - EXPECT_EQ(10, getRate()); + EXPECT_EQ(10, opt.getRate()); + + // Negative test cases EXPECT_THROW(process("perfdhcp -4 -r 0 -l ethx"), isc::InvalidParameter); EXPECT_THROW(process("perfdhcp -6 -t 5 -l ethx"), isc::InvalidParameter); EXPECT_THROW(process("perfdhcp -4 -n 150 -l ethx"), isc::InvalidParameter); @@ -163,59 +177,85 @@ TEST_F(CommandOptionsTest, Rate) { } TEST_F(CommandOptionsTest, ReportDelay) { + CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -r 100 -t 17 -l ethx"); - EXPECT_EQ(17, getReportDelay()); + EXPECT_EQ(17, opt.getReportDelay()); + + // Negative test cases + EXPECT_THROW(process("perfdhcp -r -3 -t 17 -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -r 0 -t 17 -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -r s -t 17 -l ethx"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, RandomRange) { + CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -R 200 -l ethx"); - EXPECT_EQ(200, getRandomRange()); + EXPECT_EQ(200, opt.getRandomRange()); + process("perfdhcp -R 0 -l ethx"); + EXPECT_EQ(0, opt.getRandomRange()); + + // Negative test cases + EXPECT_THROW(process("perfdhcp -R -5 -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -R gs -l ethx"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Base) { + CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -6 -b MAC=10::20::30::40::50::60 -l ethx -b duiD=1AB7F5670901FF"); uint8_t mac[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60 }; uint8_t duid[7] = { 0x1A, 0xB7, 0xF5, 0x67, 0x09, 0x01, 0xFF }; - // We will be iterating through vector's elements - // because attempt to compare vectors directly with - // EXPECT_EQ fails on FreeBSD - // Test Mac - std::vector v1 = getMacPrefix(); + std::vector v1 = opt.getMacPrefix(); ASSERT_EQ(6, v1.size()); - for (int i = 0; i < v1.size(); i++) { - EXPECT_EQ(mac[i], v1[i]); - } + EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); + EXPECT_THROW(process("perfdhcp -b mac=10::2::3x::4::5::6 -l ethx"), isc::InvalidParameter); + // Test DUID - std::vector v2 = getDuidPrefix(); + std::vector v2 = opt.getDuidPrefix(); ASSERT_EQ(sizeof(duid) / sizeof(uint8_t), v2.size()); - for (int i = 0; i < v1.size(); i++) { - EXPECT_EQ(duid[i], v2[i]); - } + EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid)); + EXPECT_THROW(process("perfdhcp -6 -l ethx -b duiD=1AB7Ft670901FF"), isc::InvalidParameter); + + // Some more negative test cases + EXPECT_THROW(process("perfdhcp -b -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -b mc=00:01:02:03::04:05"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, DropTime) { + CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -l ethx -d 12"); - ASSERT_EQ(2, getLostTime().size()); - EXPECT_DOUBLE_EQ(12, getLostTime()[0]); - EXPECT_DOUBLE_EQ(1, getLostTime()[1]); + ASSERT_EQ(2, opt.getDropTime().size()); + EXPECT_DOUBLE_EQ(12, opt.getDropTime()[0]); + EXPECT_DOUBLE_EQ(1, opt.getDropTime()[1]); process("perfdhcp -l ethx -d 2 -d 4.7"); - ASSERT_EQ(2, getLostTime().size()); - EXPECT_DOUBLE_EQ(2, getLostTime()[0]); - EXPECT_DOUBLE_EQ(4.7, getLostTime()[1]); + ASSERT_EQ(2, opt.getDropTime().size()); + EXPECT_DOUBLE_EQ(2, opt.getDropTime()[0]); + EXPECT_DOUBLE_EQ(4.7, opt.getDropTime()[1]); + + // Negative test cases + EXPECT_THROW(process("perfdhcp -l ethx -d -2 -d 4.7"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -d -9.1 -d 0"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, TimeOffset) { + CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -l ethx -T file1.x -T file2.x -E 4"); - EXPECT_EQ(4, getElpOffset()); + EXPECT_EQ(4, opt.getElapsedTimeOffset()); + + // Negative test cases EXPECT_THROW(process("perfdhcp -l ethx -E 3 -i"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -E -i"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -E -3"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, ExchangeMode) { + CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -l ethx -i"); - EXPECT_EQ(CommandOptions::DO_SA, getExchangeMode()); + EXPECT_EQ(CommandOptions::DO_SA, opt.getExchangeMode()); + + // Negative test cases EXPECT_THROW(process("perfdhcp -i -l ethx -X 3"), isc::InvalidParameter); EXPECT_THROW(process("perfdhcp -i -l ethx -O 2"), isc::InvalidParameter); EXPECT_THROW(process("perfdhcp -i -l ethx -E 3"), isc::InvalidParameter); @@ -224,16 +264,19 @@ TEST_F(CommandOptionsTest, ExchangeMode) { } TEST_F(CommandOptionsTest, Offsets) { + CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx -X3 -T file1.x -T file2.x"); - EXPECT_EQ(2, getRipOffset()); - EXPECT_EQ(5, getElpOffset()); - EXPECT_EQ(3, getSidOffset()); - ASSERT_EQ(2, getRndOffset().size()); - EXPECT_EQ(30, getRndOffset()[0]); - EXPECT_EQ(30, getRndOffset()[1]); - ASSERT_EQ(2, getXidOffset().size()); - EXPECT_EQ(7, getXidOffset()[0]); - EXPECT_EQ(3, getXidOffset()[1]); + EXPECT_EQ(2, opt.getRequestedIpOffset()); + EXPECT_EQ(5, opt.getElapsedTimeOffset()); + EXPECT_EQ(3, opt.getServerIdOffset()); + ASSERT_EQ(2, opt.getRandomOffset().size()); + EXPECT_EQ(30, opt.getRandomOffset()[0]); + EXPECT_EQ(30, opt.getRandomOffset()[1]); + ASSERT_EQ(2, opt.getTransactionIdOffset().size()); + EXPECT_EQ(7, opt.getTransactionIdOffset()[0]); + EXPECT_EQ(3, opt.getTransactionIdOffset()[1]); + + // Negative test cases EXPECT_THROW(process("perfdhcp -6 -I 0 -l ethx"), isc::InvalidParameter); EXPECT_THROW(process("perfdhcp -6 -I -4 -l ethx"), isc::InvalidParameter); @@ -241,39 +284,66 @@ TEST_F(CommandOptionsTest, Offsets) { } TEST_F(CommandOptionsTest, LocalPort) { + CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -l ethx -L 2000"); - EXPECT_EQ(2000, getLocalPort()); + EXPECT_EQ(2000, opt.getLocalPort()); + + // Negative test cases + EXPECT_THROW(process("perfdhcp -l ethx -L -2"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -L"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Preload) { + CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -1 -P 3 -l ethx"); - EXPECT_EQ(3, getPreload()); + EXPECT_EQ(3, opt.getPreload()); + + // Negative test cases EXPECT_THROW(process("perfdhcp -P -1 -l ethx"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Seed) { + CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -6 -P 2 -s 23 -l ethx"); - EXPECT_EQ(23, getSeed()); - EXPECT_TRUE(isSeeded()); + EXPECT_EQ(23, opt.getSeed()); + EXPECT_TRUE(opt.isSeeded()); + + process("perfdhcp -6 -P 2 -s 0 -l ethx"); + EXPECT_EQ(0, opt.getSeed()); + EXPECT_FALSE(opt.isSeeded()); + + // Negtaive test cases + EXPECT_THROW(process("perfdhcp -6 -P 2 -s -5 -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -6 -P 2 -s -l ethx"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, TemplateFiles) { + CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -T file1.x -l ethx"); - ASSERT_EQ(1, getTemplateFiles().size()); - EXPECT_EQ("file1.x", getTemplateFiles()[0]); + ASSERT_EQ(1, opt.getTemplateFiles().size()); + EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]); + process("perfdhcp -T file1.x -s 12 -w start -T file2.x -4 -l ethx"); - ASSERT_EQ(2, getTemplateFiles().size()); - EXPECT_EQ("file1.x", getTemplateFiles()[0]); - EXPECT_EQ("file2.x", getTemplateFiles()[1]); + ASSERT_EQ(2, opt.getTemplateFiles().size()); + EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]); + EXPECT_EQ("file2.x", opt.getTemplateFiles()[1]); + + // Negative test cases + EXPECT_THROW(process("perfdhcp -s 12 -l ethx -T"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Wrapped) { + CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -B -w start -i -l ethx"); - EXPECT_EQ("start", getWrapped()); + EXPECT_EQ("start", opt.getWrapped()); + + // Negative test cases + EXPECT_THROW(process("perfdhcp -B -i -l ethx -w"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Diagnostics) { + CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -l ethx -i -x asTe"); - EXPECT_EQ("asTe", getDiags()); + EXPECT_EQ("asTe", opt.getDiags()); }