re-write CLI argument parsing for testability
This commit is contained in:
1 parent
8cb8d4bf69
commit
5ab17ec375
7 files changed
+552
-39
No files matched your search
+2
-1
@@ -45,7 +45,7 @@ OUTDIR="$PWD"/coverage
|
||||
install -d "$OUTDIR"
|
||||
|
||||
# pre-process for each directory, correcting "file" relative to repo root
|
||||
for dir in src ioc
|
||||
for dir in src ioc tools
|
||||
do
|
||||
( cd "$TDIR"/$dir/O.linux-* \
|
||||
&& gcovr --gcov-ignore-parse-errors -v -r .. --json \
|
||||
@@ -57,6 +57,7 @@ done
|
||||
gcovr \
|
||||
--add-tracefile "$TDIR"/src.json \
|
||||
--add-tracefile "$TDIR"/ioc.json \
|
||||
--add-tracefile "$TDIR"/tools.json \
|
||||
--html --html-details -o "$OUTDIR"/coverage.html
|
||||
|
||||
cd "$OUTDIR"
|
||||
|
||||
@@ -12,6 +12,8 @@ USR_CPPFLAGS += -I$(TOP)/src
|
||||
|
||||
PROD_LIBS += pvxs Com
|
||||
|
||||
PROD_SRCS += cliutil.cpp
|
||||
|
||||
PROD += pvxvct
|
||||
pvxvct_SRCS += pvxvct.cpp
|
||||
|
||||
@@ -36,6 +38,18 @@ pvxlist_SRCS += list.cpp
|
||||
PROD += pvxmshim
|
||||
pvxmshim_SRCS += mshim.cpp
|
||||
|
||||
# tests of CLI tools
|
||||
|
||||
TESTPROD_HOST += testcliutil
|
||||
testcliutil_SRCS += testcliutil.cpp
|
||||
TESTS += testcliutil
|
||||
|
||||
TESTPROD_HOST += testxput
|
||||
testxput_SRCS += testxput.cpp
|
||||
TESTS += testxput
|
||||
|
||||
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
|
||||
endif # BASE_3_15
|
||||
|
||||
#===========================
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvxs is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#include "cliutil.h"
|
||||
|
||||
namespace pvxs {
|
||||
|
||||
bool operator==(const ArgVal& rhs, const ArgVal& lhs) {
|
||||
return rhs.defined==lhs.defined && rhs.value==lhs.value;
|
||||
}
|
||||
|
||||
GetOpt::GetOpt(int argc, char *argv[], const char *spec)
|
||||
:argv0("<program name>")
|
||||
{
|
||||
if(argc>=1)
|
||||
argv0 = argv[0];
|
||||
|
||||
bool allpos = false; // after "--", treat all remaining as positional
|
||||
for(int i=1; i<argc; i++) {
|
||||
const char * arg = argv[i];
|
||||
if(!allpos && arg[0]=='-') {
|
||||
arg++;
|
||||
|
||||
if(arg[0]=='-') {
|
||||
if(arg[1]=='\0') { // "--"
|
||||
allpos = true;
|
||||
continue;
|
||||
}
|
||||
// "--..." not supported
|
||||
arguments.emplace_back(-1, argv[i]);
|
||||
continue;
|
||||
}
|
||||
// process as short args
|
||||
|
||||
for(; *arg; arg++) {
|
||||
for(auto s=spec; *s; s++) {
|
||||
if(*s==*arg) { // match
|
||||
if(s[1]==':') { // need arg value
|
||||
if(arg[1]=='\0') { // "-a", "value"
|
||||
if(i+1==argc) {
|
||||
// oops. no value
|
||||
arguments.emplace_back('?', nullptr);
|
||||
return;
|
||||
}
|
||||
arguments.emplace_back(*arg, argv[i+1]);
|
||||
i++;
|
||||
|
||||
} else {
|
||||
// "-avalue"
|
||||
arguments.emplace_back(*arg, &arg[1]);
|
||||
}
|
||||
goto nextarg;
|
||||
|
||||
} else { // flag
|
||||
arguments.emplace_back(*s, nullptr);
|
||||
// continue scanning for more flags. eg. "-vv"
|
||||
goto nextchar;
|
||||
}
|
||||
} else {
|
||||
if(s[1]==':')
|
||||
s++;
|
||||
}
|
||||
}
|
||||
// unrecognized
|
||||
arguments.emplace_back(-1, nullptr);
|
||||
break;
|
||||
nextchar:
|
||||
(void)0; // need a statement after label...
|
||||
}
|
||||
nextarg:
|
||||
(void)0;
|
||||
|
||||
} else {
|
||||
positional.push_back(arg);
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pvxs
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvxs is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#ifndef CLIUTIL_H
|
||||
#define CLIUTIL_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <map> // for std::pair
|
||||
|
||||
#include <utilpvt.h>
|
||||
|
||||
namespace pvxs {
|
||||
|
||||
struct ArgVal {
|
||||
std::string value;
|
||||
bool defined = false;
|
||||
|
||||
ArgVal() = default;
|
||||
ArgVal(std::nullptr_t) {}
|
||||
ArgVal(const std::string& value) :value(value), defined(true) {}
|
||||
ArgVal(const char* value) :value(value), defined(true) {}
|
||||
|
||||
inline explicit
|
||||
operator bool() const { return defined; }
|
||||
|
||||
inline
|
||||
const std::string& operator*() const {
|
||||
if(defined)
|
||||
return value;
|
||||
throw std::logic_error("Undefined argument value");
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
inline
|
||||
V as() const {
|
||||
return parseTo<V>(**this);
|
||||
}
|
||||
};
|
||||
|
||||
bool operator==(const ArgVal& rhs, const ArgVal& lhs);
|
||||
|
||||
struct GetOpt {
|
||||
GetOpt(int argc, char *argv[], const char *spec);
|
||||
|
||||
const char *argv0;
|
||||
std::vector<std::string> positional;
|
||||
std::vector<std::pair<char, ArgVal>> arguments;
|
||||
bool success = false;
|
||||
};
|
||||
|
||||
} // namespace pvxs
|
||||
|
||||
#endif // CLIUTIL_H
|
||||
+40
-38
@@ -9,7 +9,6 @@
|
||||
#include <atomic>
|
||||
|
||||
#include <epicsVersion.h>
|
||||
#include <epicsGetopt.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#define PVXS_ENABLE_EXPERT_API
|
||||
@@ -19,6 +18,11 @@
|
||||
#include <pvxs/json.h>
|
||||
#include "utilpvt.h"
|
||||
#include "evhelper.h"
|
||||
#include "cliutil.h"
|
||||
|
||||
#ifndef REALMAIN
|
||||
# define REALMAIN main
|
||||
#endif
|
||||
|
||||
using namespace pvxs;
|
||||
|
||||
@@ -37,9 +41,9 @@ void usage(const char* argv0)
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
int REALMAIN(int argc, char *argv[])
|
||||
{
|
||||
try {
|
||||
logger_config_env(); // from $PVXS_LOG
|
||||
@@ -47,55 +51,53 @@ int main(int argc, char *argv[])
|
||||
bool verbose = false;
|
||||
std::string request;
|
||||
|
||||
{
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "hvVdw:r:")) != -1) {
|
||||
switch(opt) {
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
case 'V':
|
||||
std::cout<<pvxs::version_information;
|
||||
return 0;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
case 'd':
|
||||
logger_level_set("pvxs.*", Level::Debug);
|
||||
break;
|
||||
case 'w':
|
||||
timeout = parseTo<double>(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
request = optarg;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
std::cerr<<"\nUnknown argument: "<<char(opt)<<std::endl;
|
||||
return 1;
|
||||
}
|
||||
GetOpt opts(argc, argv, "hvVdw:r:");
|
||||
for(auto& pair : opts.arguments) {
|
||||
switch(pair.first) {
|
||||
case 'h':
|
||||
usage(opts.argv0);
|
||||
return 0;
|
||||
case 'V':
|
||||
std::cout<<pvxs::version_information;
|
||||
return 0;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
case 'd':
|
||||
logger_level_set("pvxs.*", Level::Debug);
|
||||
break;
|
||||
case 'w':
|
||||
timeout = pair.second.as<double>();
|
||||
break;
|
||||
case 'r':
|
||||
request = *pair.second;
|
||||
break;
|
||||
default:
|
||||
usage(opts.argv0);
|
||||
std::cerr<<"\nUnknown argument: "<<pair.first<<std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(optind==argc) {
|
||||
usage(argv[0]);
|
||||
std::cerr<<"\nExpected PV name\n";
|
||||
if(opts.positional.size()<2) {
|
||||
usage(opts.argv0);
|
||||
std::cerr<<"\nExpected PV name and at least one value\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string pvname(argv[optind++]);
|
||||
const auto& pvname = opts.positional.front();
|
||||
std::map<std::string, std::string> values;
|
||||
|
||||
if(argc-optind==1 && std::string(argv[optind]).find_first_of('=')==std::string::npos) {
|
||||
if(opts.positional.size()==2 && std::string(opts.positional[1]).find_first_of('=')==std::string::npos) {
|
||||
// only one field assignment, and field name omitted.
|
||||
// if JSON map, treat as entire struct. Others imply .value
|
||||
|
||||
auto sval(argv[optind]);
|
||||
const auto& sval = opts.positional[1];
|
||||
values[sval[0]=='{' ? "" : "value"] = sval;
|
||||
|
||||
} else {
|
||||
for(auto n : range(optind, argc)) {
|
||||
std::string fv(argv[n]);
|
||||
for(auto n : range(size_t(1), opts.positional.size())) {
|
||||
std::string fv(opts.positional[n]);
|
||||
auto sep = fv.find_first_of('=');
|
||||
|
||||
if(sep==std::string::npos) {
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvxs is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <initializer_list>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <pvxs/unittest.h>
|
||||
#include <pvxs/util.h>
|
||||
|
||||
#include <testMain.h>
|
||||
#include <epicsUnitTest.h>
|
||||
#include <dbDefs.h>
|
||||
|
||||
#include "cliutil.h"
|
||||
|
||||
namespace pvxs { namespace detail {
|
||||
|
||||
template<typename E>
|
||||
struct test_print<std::vector<E>> {
|
||||
template<class C>
|
||||
static inline void op(C& strm, const std::vector<E>& v) {
|
||||
bool first = true;
|
||||
for(auto& e : v) {
|
||||
if(first) {
|
||||
first = false;
|
||||
} else {
|
||||
strm<<' ';
|
||||
}
|
||||
test_print<E>::op(strm, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct test_print<std::pair<char, ArgVal>> {
|
||||
template<class C>
|
||||
static inline void op(C& strm, const std::pair<char, ArgVal>& v) {
|
||||
strm<<'-'<<v.first;
|
||||
if(v.second.defined)
|
||||
strm<<" "<<pvxs::escape(v.second.value);
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace::detail
|
||||
|
||||
MAIN(testcliutil) {
|
||||
testPlan(24);
|
||||
|
||||
{
|
||||
testDiag("case @%d", __LINE__);
|
||||
const char* argv[] = {"exe", "-v", "-a", "Aa", "hello"};
|
||||
pvxs::GetOpt opts(NELEMENTS(argv), const_cast<char**>(argv), "a:v");
|
||||
testOk(strcmp(opts.argv0, "exe")==0, "%s", opts.argv0);
|
||||
decltype (opts.arguments) arguments({{'v', nullptr}, {'a',"Aa"}});
|
||||
testArrEq(opts.arguments, arguments);
|
||||
decltype (opts.positional) positional({"hello"});
|
||||
testArrEq(opts.positional, positional);
|
||||
}
|
||||
|
||||
{
|
||||
testDiag("case @%d", __LINE__);
|
||||
const char* argv[] = {"exe", "-v", "-a", "Aa", "hello"};
|
||||
pvxs::GetOpt opts(NELEMENTS(argv), const_cast<char**>(argv), "va:");
|
||||
testOk(strcmp(opts.argv0, "exe")==0, "%s", opts.argv0);
|
||||
decltype (opts.arguments) arguments({{'v', nullptr}, {'a',"Aa"}});
|
||||
testArrEq(opts.arguments, arguments);
|
||||
decltype (opts.positional) positional({"hello"});
|
||||
testArrEq(opts.positional, positional);
|
||||
}
|
||||
|
||||
{
|
||||
testDiag("case @%d", __LINE__);
|
||||
const char* argv[] = {"exe", "-v", "hello", "-aAa"};
|
||||
pvxs::GetOpt opts(NELEMENTS(argv), const_cast<char**>(argv), "va:");
|
||||
testOk(strcmp(opts.argv0, "exe")==0, "%s", opts.argv0);
|
||||
decltype (opts.arguments) arguments({{'v', nullptr}, {'a',"Aa"}});
|
||||
testArrEq(opts.arguments, arguments);
|
||||
decltype (opts.positional) positional({"hello"});
|
||||
testArrEq(opts.positional, positional);
|
||||
}
|
||||
|
||||
{
|
||||
testDiag("case @%d", __LINE__);
|
||||
const char* argv[] = {"exe", "-v", "hello", "-a"}; // missing value
|
||||
pvxs::GetOpt opts(NELEMENTS(argv), const_cast<char**>(argv), "va:");
|
||||
testOk(strcmp(opts.argv0, "exe")==0, "%s", opts.argv0);
|
||||
decltype (opts.arguments) arguments({{'v', nullptr}, {'?',nullptr}});
|
||||
testArrEq(opts.arguments, arguments);
|
||||
decltype (opts.positional) positional({"hello"});
|
||||
testArrEq(opts.positional, positional);
|
||||
}
|
||||
|
||||
{
|
||||
testDiag("case @%d", __LINE__);
|
||||
const char* argv[] = {"exe", "-vvaTest", "hello"};
|
||||
pvxs::GetOpt opts(NELEMENTS(argv), const_cast<char**>(argv), "va:");
|
||||
testOk(strcmp(opts.argv0, "exe")==0, "%s", opts.argv0);
|
||||
decltype (opts.arguments) arguments({{'v', nullptr}, {'v', nullptr}, {'a', "Test"}});
|
||||
testArrEq(opts.arguments, arguments);
|
||||
decltype (opts.positional) positional({"hello"});
|
||||
testArrEq(opts.positional, positional);
|
||||
}
|
||||
|
||||
{
|
||||
testDiag("case @%d", __LINE__);
|
||||
const char* argv[] = {"exe", "-vvQQ", "hello"};
|
||||
pvxs::GetOpt opts(NELEMENTS(argv), const_cast<char**>(argv), "va:");
|
||||
testOk(strcmp(opts.argv0, "exe")==0, "%s", opts.argv0);
|
||||
decltype (opts.arguments) arguments({{'v', nullptr}, {'v', nullptr}, {-1, nullptr}});
|
||||
testArrEq(opts.arguments, arguments);
|
||||
decltype (opts.positional) positional({"hello"});
|
||||
testArrEq(opts.positional, positional);
|
||||
}
|
||||
|
||||
{
|
||||
testDiag("case @%d", __LINE__);
|
||||
const char* argv[] = {"exe", "-v", "--", "-vvaTest", "hello"};
|
||||
pvxs::GetOpt opts(NELEMENTS(argv), const_cast<char**>(argv), "va:");
|
||||
testOk(strcmp(opts.argv0, "exe")==0, "%s", opts.argv0);
|
||||
decltype (opts.arguments) arguments({{'v', nullptr}});
|
||||
testArrEq(opts.arguments, arguments);
|
||||
decltype (opts.positional) positional({"-vvaTest", "hello"});
|
||||
testArrEq(opts.positional, positional);
|
||||
}
|
||||
|
||||
{
|
||||
testDiag("case @%d", __LINE__);
|
||||
const char* argv[] = {"exe", "--vvQQ", "hello"};
|
||||
pvxs::GetOpt opts(NELEMENTS(argv), const_cast<char**>(argv), "va:");
|
||||
testOk(strcmp(opts.argv0, "exe")==0, "%s", opts.argv0);
|
||||
decltype (opts.arguments) arguments({{-1, "--vvQQ"}});
|
||||
testArrEq(opts.arguments, arguments);
|
||||
decltype (opts.positional) positional({"hello"});
|
||||
testArrEq(opts.positional, positional);
|
||||
}
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* pvxs is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include <functional>
|
||||
|
||||
#include <testMain.h>
|
||||
|
||||
#include <envDefs.h>
|
||||
#include <dbDefs.h>
|
||||
#include <epicsUnitTest.h>
|
||||
|
||||
#define PVXS_ENABLE_EXPERT_API
|
||||
|
||||
#include <pvxs/server.h>
|
||||
#include <pvxs/sharedpv.h>
|
||||
#include <pvxs/nt.h>
|
||||
#include <pvxs/unittest.h>
|
||||
|
||||
#define REALMAIN pvxput
|
||||
#include "put.cpp"
|
||||
|
||||
using namespace pvxs;
|
||||
|
||||
namespace {
|
||||
struct Redirect {
|
||||
std::ostream& ref;
|
||||
std::streambuf *prev;
|
||||
Redirect(std::ostream& ref, std::ostream& target)
|
||||
:ref(ref)
|
||||
,prev(ref.rdbuf(target.rdbuf()))
|
||||
{}
|
||||
~Redirect() {
|
||||
ref.rdbuf(prev);
|
||||
}
|
||||
};
|
||||
|
||||
struct Run {
|
||||
std::string out, err;
|
||||
int code;
|
||||
|
||||
Run(std::initializer_list<std::string> args) {
|
||||
std::vector<const char*> argv({"pvxput"});
|
||||
{
|
||||
testCase cmdline;
|
||||
cmdline<<"exec: pvxput";
|
||||
for(auto& arg : args) {
|
||||
cmdline<<" '"<<arg<<'\'';
|
||||
argv.push_back(arg.c_str());
|
||||
}
|
||||
}
|
||||
std::ostringstream out, err;
|
||||
{
|
||||
Redirect rd_cout(std::cout, out);
|
||||
Redirect rd_cerr(std::cerr, err);
|
||||
try{
|
||||
code = pvxput(argv.size(), (char**)argv.data());
|
||||
}catch(std::exception& e){
|
||||
testFail("Uncaught c++ exception: %s", e.what());
|
||||
code = -1;
|
||||
}
|
||||
}
|
||||
this->out = out.str();
|
||||
this->err = err.str();
|
||||
if(!this->out.empty())
|
||||
testShow()<<"stdout:\n"<<this->out;
|
||||
if(!this->err.empty())
|
||||
testShow()<<"stderr:\n"<<this->err;
|
||||
|
||||
}
|
||||
|
||||
Run& exitWith(int expect) {
|
||||
testOk(expect==code, "%d == %d", expect, code);
|
||||
return *this;
|
||||
}
|
||||
Run& success() {
|
||||
return exitWith(0);
|
||||
}
|
||||
|
||||
testCase _lineMatching(const std::string& inp, const std::string& expr) {
|
||||
std::istringstream strm(inp);
|
||||
std::string line;
|
||||
testCase c;
|
||||
while(std::getline(strm, line)) {
|
||||
if(c.setPassMatch(expr, line))
|
||||
break;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
inline
|
||||
testCase outMatch(const std::string& expr) {
|
||||
return _lineMatching(out, expr);
|
||||
}
|
||||
inline
|
||||
testCase errMatch(const std::string& expr) {
|
||||
return _lineMatching(err, expr);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
MAIN(testxput)
|
||||
{
|
||||
testPlan(28);
|
||||
|
||||
auto pvI32(server::SharedPV::buildMailbox());
|
||||
pvI32.open(nt::NTScalar{TypeCode::Int32}.create()
|
||||
.update("value", 5));
|
||||
|
||||
auto pvI32arr(server::SharedPV::buildMailbox());
|
||||
pvI32arr.open(nt::NTScalar{TypeCode::Int32A}.create());
|
||||
|
||||
auto pvS(server::SharedPV::buildMailbox());
|
||||
pvS.open(nt::NTScalar{TypeCode::String}.create()
|
||||
.update("value", "foo"));
|
||||
|
||||
auto pvE(server::SharedPV::buildMailbox());
|
||||
pvE.open(nt::NTEnum{}.create()
|
||||
.update("value.index", 0)
|
||||
.update("value.choices", shared_array<const std::string>({"one", "two"})));
|
||||
|
||||
// setup isolated server
|
||||
auto srv(server::Config::isolated()
|
||||
.build()
|
||||
.addPV("testI32", pvI32)
|
||||
.addPV("arrI32", pvI32arr)
|
||||
.addPV("testS", pvS)
|
||||
.addPV("testE", pvE)
|
||||
.start());
|
||||
|
||||
// setup environment for pvxput() to find only our isolated server
|
||||
{
|
||||
client::Config::defs_t envs;
|
||||
srv.clientConfig().updateDefs(envs);
|
||||
for(const auto& pair : envs) {
|
||||
testShow()<<" "<<pair.first<<"="<<pair.second;
|
||||
epicsEnvSet(pair.first.c_str(), pair.second.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Run run({"-v", "-d", "-w1", "-w", "2", "-r", "foo", "-h"});
|
||||
run.success();
|
||||
run.outMatch(".*Show this message.*");
|
||||
}
|
||||
|
||||
{
|
||||
Run run({"-V"});
|
||||
run.success();
|
||||
run.outMatch(".*PVXS.*");
|
||||
run.outMatch(".*libevent.*");
|
||||
}
|
||||
|
||||
{
|
||||
Run run({});
|
||||
run.exitWith(1);
|
||||
testStrEq(run.out, "");
|
||||
run.errMatch(".*Expected PV name.*");
|
||||
}
|
||||
|
||||
{
|
||||
Run run({"-w", "foo"});
|
||||
run.exitWith(1);
|
||||
testStrEq(run.out, "");
|
||||
run.errMatch(".*Invalid.*foo.*");
|
||||
}
|
||||
|
||||
Run({"testI32", "6"}).success();
|
||||
testEq(pvI32.fetch()["value"].as<int32_t>(), 6);
|
||||
|
||||
Run({"testI32", "value=7"}).success();
|
||||
testEq(pvI32.fetch()["value"].as<int32_t>(), 7);
|
||||
|
||||
Run({"testI32", R"({"value":8})"}).success();
|
||||
testEq(pvI32.fetch()["value"].as<int32_t>(), 8);
|
||||
|
||||
Run({"arrI32", R"({"value":8})"}).exitWith(1);
|
||||
|
||||
Run({"arrI32", R"([3,4,5])"}).success();
|
||||
testArrEq(pvI32arr.fetch()["value"].as<shared_array<const int32_t>>(),
|
||||
shared_array<const int32_t>({3,4,5}));
|
||||
|
||||
Run({"arrI32", R"(value=[1,4])"}).success();
|
||||
testArrEq(pvI32arr.fetch()["value"].as<shared_array<const int32_t>>(),
|
||||
shared_array<const int32_t>({1,4}));
|
||||
|
||||
Run({"arrI32", R"({"value":[5,6]})"}).success();
|
||||
testArrEq(pvI32arr.fetch()["value"].as<shared_array<const int32_t>>(),
|
||||
shared_array<const int32_t>({5, 6}));
|
||||
|
||||
Run({"testS", "hello"}).success();
|
||||
testEq(pvS.fetch()["value"].as<std::string>(), "hello");
|
||||
|
||||
Run({"testS", "value=world"}).success();
|
||||
testEq(pvS.fetch()["value"].as<std::string>(), "world");
|
||||
|
||||
Run({"testS", R"({"value":"baz"})"}).success();
|
||||
testEq(pvS.fetch()["value"].as<std::string>(), "baz");
|
||||
|
||||
|
||||
Run({"testE", R"({"value":{"index":1, "choices":["A","B","C"]}})"}).success();
|
||||
testEq(pvE.fetch()["value.index"].as<int32_t>(), 1);
|
||||
|
||||
// update choices and assign from string with one op. A place where order matters...
|
||||
Run({"testE", R"({"value":"d", "value.choices":["a","b","c","d"]})"}).exitWith(1);
|
||||
|
||||
return testDone();
|
||||
}
|
||||
Reference in new issue
Block a user