/** * 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 #include #include #include #include #include #define PVXS_ENABLE_EXPERT_API #include #include #include #include #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 args) { std::vector argv({"pvxput"}); { testCase cmdline; cmdline<<"exec: pvxput"; for(auto& arg : args) { cmdline<<" '"<out = out.str(); this->err = err.str(); if(!this->out.empty()) testShow()<<"stdout:\n"<out; if(!this->err.empty()) testShow()<<"stderr:\n"<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({"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()<<" "<(), 6); Run({"testI32", "value=7"}).success(); testEq(pvI32.fetch()["value"].as(), 7); Run({"testI32", R"({"value":8})"}).success(); testEq(pvI32.fetch()["value"].as(), 8); Run({"arrI32", R"({"value":8})"}).exitWith(1); Run({"arrI32", R"([3,4,5])"}).success(); testArrEq(pvI32arr.fetch()["value"].as>(), shared_array({3,4,5})); Run({"arrI32", R"(value=[1,4])"}).success(); testArrEq(pvI32arr.fetch()["value"].as>(), shared_array({1,4})); Run({"arrI32", R"({"value":[5,6]})"}).success(); testArrEq(pvI32arr.fetch()["value"].as>(), shared_array({5, 6})); Run({"testS", "hello"}).success(); testEq(pvS.fetch()["value"].as(), "hello"); Run({"testS", "value=world"}).success(); testEq(pvS.fetch()["value"].as(), "world"); Run({"testS", R"({"value":"baz"})"}).success(); testEq(pvS.fetch()["value"].as(), "baz"); Run({"testE", R"({"value":{"index":1, "choices":["A","B","C"]}})"}).success(); testEq(pvE.fetch()["value.index"].as(), 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(); }