Moved CmdLineProxy

This commit is contained in:
Erik Frojdh
2019-11-05 11:07:07 +01:00
parent b3587bcee5
commit 2f2e4da628
10 changed files with 68 additions and 42 deletions

View File

@@ -6,6 +6,7 @@ set(SOURCES
src/slsDetector.cpp
src/Detector.cpp
src/CmdProxy.cpp
src/CmdLineParser.cpp
)
set(HEADERS

View File

@@ -0,0 +1,112 @@
#include "CmdLineParser.h"
#include "sls_detector_defs.h"
#include <cstdio>
#include <cstring>
#include <iostream>
#include <iterator>
#include <sstream>
namespace sls {
void CmdLineParser::Print() {
std::cout << "\nCmdLineParser::Print()\n";
std::cout << "\tmulti_id: " << multi_id_
<< ", detector_id: " << detector_id_ << std::endl;
std::cout << "\texecutable: " << executable_ << '\n';
std::cout << "\tcommand: " << command_ << '\n';
std::cout << "\tn_arguments: " << n_arguments() << '\n';
std::cout << "\targuments: ";
for (const auto &argument : arguments_) {
std::cout << argument << " ";
}
std::cout << "\n\n";
};
void CmdLineParser::Parse(int argc, const char *const argv[]) {
executable_ = argv[0]; // first arg is calling binary
if (argc > 1) {
std::string s = argv[1];
for (int i = 2; i < argc; ++i) {
s += " ";
s += argv[i];
}
Parse(s);
}
}
void CmdLineParser::Parse(const std::string &s) {
std::istringstream iss(s);
auto it = std::istream_iterator<std::string>(iss);
arguments_ =
std::vector<std::string>(it, std::istream_iterator<std::string>());
auto old_size = arguments_.size();
arguments_.erase(std::remove_if(begin(arguments_), end(arguments_),
[](const std::string &item) {
if (item == "-h" || item == "--help")
return true;
return false;
}),
end(arguments_));
if (old_size - arguments_.size() > 0)
help_ = true;
if (!arguments_.empty()) {
command_ = arguments_[0];
arguments_.erase(begin(arguments_));
}
DecodeIdAndPosition(command_.c_str());
}
void CmdLineParser::DecodeIdAndPosition(const char *c) {
bool contains_id = std::strchr(c, '-') != nullptr;
bool contains_pos = std::strchr(c, ':') != nullptr;
char tmp[100];
if (contains_id && contains_pos) {
int r = sscanf(c, "%d-%d:%s", &multi_id_, &detector_id_, tmp);
if (r != 3) {
throw(sls::RuntimeError(
"Cannot decode client or detector id from: \"" +
std::string(c) + "\"\n"));
}
command_ = tmp;
} else if (contains_id && !contains_pos) {
int r = sscanf(c, "%d-%s", &multi_id_, tmp);
if (r != 2) {
throw(sls::RuntimeError("Cannot decode client id from: \"" +
std::string(c) + "\"\n"));
}
command_ = tmp;
} else if (!contains_id && contains_pos) {
int r = sscanf(c, "%d:%s", &detector_id_, tmp);
if (r != 2) {
throw(sls::RuntimeError("Cannot decode detector id from: \"" +
std::string(c) + "\"\n"));
}
command_ = tmp;
} else {
command_ = c;
}
}
std::vector<const char *> CmdLineParser::argv() const {
std::vector<const char *> vec;
if (command_.empty() != true) {
vec.push_back(&command_.front());
}
for (auto &arg : arguments_) {
vec.push_back(&arg.front());
}
return vec;
}
std::string CmdLineParser::cli_line() const{
std::ostringstream os;
os << command_;
for (const auto & arg : arguments_)
os << " " << arg;
return os.str();
}
} // namespace sls

View File

@@ -0,0 +1,51 @@
/*
This class parses command line input or string input to extract the
multi_id, detector_id, command and arguments. It's used in the command
line binaries (sls_detector_get/put) to parse commands and in the
slsDetectorShared library to parse input from config files.
This class is fully internal to the project and NO guarantees are given
on the stability of the interface or implementation. This is also the
reason that the header file is not exposed.
*/
#ifndef CMD_LINE_PARSER_H
#define CMD_LINE_PARSER_H
#include <string>
#include <vector>
namespace sls {
class CmdLineParser {
public:
void Parse(int argc, const char *const argv[]);
void Parse(const std::string &s);
void Print();
int multi_id() const noexcept { return multi_id_; };
int detector_id() const noexcept { return detector_id_; };
int n_arguments() const noexcept { return arguments_.size(); }
const std::string &command() const noexcept{ return command_; }
void setCommand(std::string cmd) { command_ = cmd; }
bool isHelp() const noexcept { return help_; }
const std::string &executable() const noexcept{ return executable_; }
const std::vector<std::string> &arguments() const noexcept{ return arguments_; };
std::vector<const char *> argv() const;
std::string cli_line() const;
private:
void DecodeIdAndPosition(const char *c);
int multi_id_ = 0;
int detector_id_ = -1;
bool help_{false};
std::string command_;
std::string executable_;
std::vector<std::string> arguments_;
};
} // namespace sls
#endif // CMD_LINE_PARSER_H

View File

@@ -4,4 +4,7 @@ target_sources(tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/test-multiSlsDetector.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test-multiSlsDetectorClient.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test-Result.cpp
)
${CMAKE_CURRENT_SOURCE_DIR}/test-CmdLineParser.cpp
)
target_include_directories(tests PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../src>")

View File

@@ -0,0 +1,275 @@
#include "CmdLineParser.h"
#include "catch.hpp"
#include <exception>
#include <string>
// tests to add
// help for all docs
// command for all depreciated commands
using vs = std::vector<std::string>;
using sls::CmdLineParser;
SCENARIO("Construction", "[support]") {
GIVEN("A default constructed CmdLineParser") {
CmdLineParser p;
THEN("The state of the object is valid") {
REQUIRE(p.detector_id() == -1);
REQUIRE(p.multi_id() == 0);
REQUIRE(p.command().empty());
REQUIRE(p.arguments().empty());
REQUIRE(p.argv().empty());
REQUIRE(p.argv().data() == nullptr);
}
}
}
SCENARIO("Parsing a string with the command line parser", "[support]") {
GIVEN("A CmdLineParser") {
CmdLineParser p;
WHEN("Parsing an empty string") {
std::string s;
p.Parse(s);
THEN("command and arguments are empty") {
REQUIRE(p.detector_id() == -1);
REQUIRE(p.multi_id() == 0);
REQUIRE(p.command().empty());
REQUIRE(p.arguments().empty());
REQUIRE(p.argv().empty());
}
}
WHEN("Parsing a string with a single command") {
std::string s = "vrf";
p.Parse(s);
THEN("command is assigned and id's remain default") {
REQUIRE(p.command() == "vrf");
REQUIRE(p.detector_id() == -1);
REQUIRE(p.multi_id() == 0);
REQUIRE(p.arguments().empty());
REQUIRE(p.argv().size() == 1);
}
}
WHEN("Parsing a string with command and value") {
std::string s = "vthreshold 1500";
p.Parse(s);
THEN("cmd and value are assigned and id's remain default") {
REQUIRE(p.command() == "vthreshold");
REQUIRE(p.arguments()[0] == "1500");
REQUIRE(p.arguments().size() == 1);
REQUIRE(p.detector_id() == -1);
REQUIRE(p.multi_id() == 0);
}
}
WHEN("Parsing a string with detector id and command") {
vs arg{"9:vcp", "53:vthreshold", "128:vtrim", "5:threshold"};
std::vector<int> det_id{9, 53, 128, 5};
vs res{"vcp", "vthreshold", "vtrim", "threshold"};
THEN("Values are correctly decoded") {
for (size_t i = 0; i != arg.size(); ++i) {
p.Parse(arg[i]);
REQUIRE(p.detector_id() == det_id[i]);
REQUIRE(p.multi_id() == 0);
REQUIRE(p.command() == res[i]);
REQUIRE(p.arguments().empty());
REQUIRE(p.argv().size() == 1);
}
}
}
WHEN("Parsing a string with multi_id detector id and command") {
vs arg{"8-12:vrf", "0-52:vcmp", "19-10:vtrim", "31-127:threshold"};
std::vector<int> det_id{12, 52, 10, 127};
std::vector<int> multi_id{8, 0, 19, 31};
vs res{"vrf", "vcmp", "vtrim", "threshold"};
THEN("Values are correctly decoded") {
for (size_t i = 0; i != arg.size(); ++i) {
p.Parse(arg[i]);
REQUIRE(p.detector_id() == det_id[i]);
REQUIRE(p.multi_id() == multi_id[i]);
REQUIRE(p.command() == res[i]);
REQUIRE(p.arguments().empty());
REQUIRE(p.argv().size() == 1);
}
}
}
WHEN("Parsing string with cmd and multiple arguments") {
std::string s = "trimen 5000 6000 7000";
p.Parse(s);
THEN("cmd and args are correct") {
REQUIRE(p.command() == "trimen");
REQUIRE(p.arguments().size() == 3);
REQUIRE(p.arguments()[0] == "5000");
REQUIRE(p.arguments()[1] == "6000");
REQUIRE(p.arguments()[2] == "7000");
}
}
WHEN("Cliend id and or detector id cannot be decoded") {
vs arg{"o:cmd", "-5:cmd", "aedpva:cmd",
"5-svc:vrf", "asv-5:cmd", "savc-asa:cmd"};
THEN("Parsing Throws") {
for (size_t i = 0; i != arg.size(); ++i) {
REQUIRE_THROWS(p.Parse(arg[i]));
}
}
}
}
}
SCENARIO("Parsing strings with -h or --help", "[support]") {
GIVEN("A parser") {
CmdLineParser p;
WHEN("Parsing a string with a command and help ") {
std::string s = "-h list";
THEN("the command is correct and isHelp is set") {
p.Parse(s);
REQUIRE(p.detector_id() == -1);
REQUIRE(p.multi_id() == 0);
REQUIRE(p.command() == "list");
REQUIRE(p.isHelp());
REQUIRE(p.arguments().empty());
REQUIRE(p.argv().size() == 1);
}
}
WHEN("Parsing a string with -h at a different position"){
std::string s = "list -h something";
THEN("its also done right"){
p.Parse(s);
REQUIRE(p.isHelp());
REQUIRE(p.command() == "list");
REQUIRE(p.arguments().size() == 1);
REQUIRE(p.arguments().front() == "something");
}
}
WHEN("Parsing a string with -help at a different position"){
std::string s = "list --help something";
THEN("its also done right"){
p.Parse(s);
REQUIRE(p.isHelp());
REQUIRE(p.command() == "list");
REQUIRE(p.arguments().size() == 1);
REQUIRE(p.arguments().front() == "something");
}
}
}
}
TEST_CASE("Parse with no arguments results in no command and default id",
"[support]") {
// build up argc and argv
// first argument is the command used to call the binary
int argc = 1;
const char *const argv[]{"call"};
CmdLineParser p;
p.Parse(argc, argv);
REQUIRE(p.detector_id() == -1);
REQUIRE(p.multi_id() == 0);
REQUIRE(p.command().empty());
REQUIRE(p.arguments().empty());
}
TEST_CASE(
"Parse a command without client id and detector id results in default",
"[support]") {
int argc = 2;
const char *const argv[]{"caller", "vrf"};
CmdLineParser p;
p.Parse(argc, argv);
REQUIRE(p.detector_id() == -1);
REQUIRE(p.multi_id() == 0);
REQUIRE(p.command() == "vrf");
REQUIRE(p.arguments().empty());
}
TEST_CASE("Parse a command with value but without client or detector id",
"[support]") {
int argc = 3;
const char *const argv[]{"caller", "vrf", "3000"};
CmdLineParser p;
p.Parse(argc, argv);
REQUIRE(p.detector_id() == -1);
REQUIRE(p.multi_id() == 0);
REQUIRE(p.command() == "vrf");
REQUIRE(p.arguments().size() == 1);
REQUIRE(p.arguments()[0] == "3000");
}
TEST_CASE("Decodes position") {
int argc = 2;
const char *const argv[]{"caller", "7:vrf"};
CmdLineParser p;
p.Parse(argc, argv);
REQUIRE(p.detector_id() == 7);
REQUIRE(p.multi_id() == 0);
REQUIRE(p.command() == "vrf");
REQUIRE(p.arguments().empty());
}
TEST_CASE("Decodes double digit position", "[support]") {
int argc = 2;
const char *const argv[]{"caller", "73:vcmp"};
CmdLineParser p;
p.Parse(argc, argv);
REQUIRE(p.detector_id() == 73);
REQUIRE(p.multi_id() == 0);
REQUIRE(p.command() == "vcmp");
REQUIRE(p.arguments().empty());
}
TEST_CASE("Decodes position and id", "[support]") {
int argc = 2;
const char *const argv[]{"caller", "5-8:vrf"};
CmdLineParser p;
p.Parse(argc, argv);
REQUIRE(p.detector_id() == 8);
REQUIRE(p.multi_id() == 5);
REQUIRE(p.command() == "vrf");
REQUIRE(p.arguments().empty());
}
TEST_CASE("Double digit id", "[support]") {
int argc = 2;
const char *const argv[]{"caller", "56-8:vrf"};
CmdLineParser p;
p.Parse(argc, argv);
REQUIRE(p.detector_id() == 8);
REQUIRE(p.multi_id() == 56);
REQUIRE(p.command() == "vrf");
REQUIRE(p.arguments().empty());
}
TEST_CASE("Calling with wrong id throws invalid_argument", "[support]") {
int argc = 2;
const char *const argv[]{"caller", "asvldkn:vrf"};
CmdLineParser p;
CHECK_THROWS(p.Parse(argc, argv));
}
TEST_CASE("Calling with wrong client throws invalid_argument", "[support]") {
int argc = 2;
const char *const argv[]{"caller", "lki-3:vrf"};
CmdLineParser p;
CHECK_THROWS(p.Parse(argc, argv));
}
TEST_CASE("Build up argv", "[support]") {
CmdLineParser p;
REQUIRE(p.argv().empty());
REQUIRE(p.argv().data() == nullptr);
std::string s = "trimen 3000 4000\n";
p.Parse(s);
REQUIRE(p.argv().data() != nullptr);
REQUIRE(p.argv().size() == 3);
}