From 8c40929e5c45dfb7987634b2b9d6d4f88d344f69 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 23 Oct 2019 13:13:52 -0700 Subject: [PATCH] all sorts of changes --- COPYRIGHT | 1 + LICENSE | 26 +++++++++++ src/Makefile | 9 ++++ src/evhelper.cpp | 18 +++++--- src/evhelper.h | 5 +- src/log.cpp | 2 +- src/pvaproto.h | 6 ++- src/pvxs/log.h | 8 ++-- src/pvxs/unittest.h | 105 ++++++++++++++++++++++++++++++++++++++++++ src/pvxs/util.h | 50 ++++++++++++++++++++ src/pvxs/version.h | 11 ++++- src/udp_collector.cpp | 6 ++- src/udp_collector.h | 2 +- src/unittest.cpp | 49 ++++++++++++++++++++ src/util.cpp | 64 +++++++++++++++++++++++++ test/Makefile | 4 ++ test/testev.cpp | 73 +++++++++++++++++++++++++++++ test/testsock.cpp | 18 ++++---- tools/pvxvct.cpp | 2 +- 19 files changed, 430 insertions(+), 29 deletions(-) create mode 100644 COPYRIGHT create mode 100644 LICENSE create mode 100644 src/pvxs/unittest.h create mode 100644 src/pvxs/util.h create mode 100644 src/unittest.cpp create mode 100644 src/util.cpp create mode 100644 test/testev.cpp diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..48457e6 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1 @@ +Copyright 2019 Osprey DCS LLC diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c7a0aa4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/src/Makefile b/src/Makefile index 388b2a0..76c86a8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -11,15 +11,24 @@ USR_CPPFLAGS += -DPVXS_API_BUILDING USR_LDFLAGS_Linux += -Wl,--no-undefined -Wl,--no-allow-shlib-undefined USR_LDFLAGS_DEFAULT += +EXPANDVARS += PVXS_MAJOR_VERSION +EXPANDVARS += PVXS_MINOR_VERSION +EXPANDVARS += PVXS_MAINTENANCE_VERSION + +EXPANDFLAGS += $(foreach var,$(EXPANDVARS),-D$(var)="$(strip $($(var)))") + SHRLIB_VERSION = $(PVXS_MAJOR_VERSION).$(PVXS_MINOR_VERSION) INC += pvxs/version.h INC += pvxs/versionNum.h INC += pvxs/log.h +INC += pvxs/unittest.h LIBRARY = pvxs LIB_SRCS += log.cpp +LIB_SRCS += unittest.cpp +LIB_SRCS += util.cpp LIB_SRCS += evhelper.cpp LIB_SRCS += udp_collector.cpp diff --git a/src/evhelper.cpp b/src/evhelper.cpp index 879d488..9a42bd2 100644 --- a/src/evhelper.cpp +++ b/src/evhelper.cpp @@ -1,6 +1,6 @@ /** * Copyright - See the COPYRIGHT that is included with this distribution. - * pvAccessCPP is distributed subject to a Software License Agreement found + * pvxs is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ @@ -31,11 +31,11 @@ struct evbase::Pvt : public epicsThreadRunable event_base* base; epicsThread worker; - Pvt() + Pvt(const std::string& name, unsigned prio) :base(nullptr) - ,worker(*this, "UDP", + ,worker(*this, name.c_str(), epicsThreadGetStackSize(epicsThreadStackBig), - epicsThreadPriorityCAServerLow-4) + prio) { #if defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED) evthread_use_windows_threads(); @@ -66,8 +66,8 @@ struct evbase::Pvt : public epicsThreadRunable } }; -evbase::evbase() - :pvt(new Pvt) +evbase::evbase(const std::string &name, unsigned prio) + :pvt(new Pvt(name, prio)) ,base(event_base_new()) { if(!base) { @@ -176,6 +176,12 @@ void evbase::assertInLoop() assert(pvt->worker.isCurrentThread()); } +bool evbase::inLoop() +{ + return pvt->worker.isCurrentThread(); +} + + evevent::evevent(struct event_base *base, evutil_socket_t sock, short mask, event_callback_fn fn, void *arg) :ev(event_new(base, sock, mask, fn, arg)) { diff --git a/src/evhelper.h b/src/evhelper.h index abe24e7..d8de95b 100644 --- a/src/evhelper.h +++ b/src/evhelper.h @@ -1,6 +1,6 @@ /** * Copyright - See the COPYRIGHT that is included with this distribution. - * pvAccessCPP is distributed subject to a Software License Agreement found + * pvxs is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ @@ -34,7 +34,7 @@ struct SB { }; struct PVXS_API evbase { - evbase(); + explicit evbase(const std::string& name, unsigned prio=0); ~evbase(); void start(); @@ -46,6 +46,7 @@ struct PVXS_API evbase { void call(std::function&& fn); void assertInLoop(); + bool inLoop(); private: struct Pvt; diff --git a/src/log.cpp b/src/log.cpp index b81052d..4387260 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -1,6 +1,6 @@ /** * Copyright - See the COPYRIGHT that is included with this distribution. - * pvAccessCPP is distributed subject to a Software License Agreement found + * pvxs is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ diff --git a/src/pvaproto.h b/src/pvaproto.h index 8729cac..ac7e009 100644 --- a/src/pvaproto.h +++ b/src/pvaproto.h @@ -1,6 +1,6 @@ /** * Copyright - See the COPYRIGHT that is included with this distribution. - * pvAccessCPP is distributed subject to a Software License Agreement found + * pvxs is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ @@ -18,7 +18,9 @@ namespace pvxsimpl { -//! hold a bounded slice of some other array +//! Hold a bounded slice of some other array. +//! like std::span (added in c++20) +//! blending in error state tracking like std::iostream template struct sbuf { typedef T value_type; diff --git a/src/pvxs/log.h b/src/pvxs/log.h index 4f77f44..3a0ef9e 100644 --- a/src/pvxs/log.h +++ b/src/pvxs/log.h @@ -1,10 +1,10 @@ /** * Copyright - See the COPYRIGHT that is included with this distribution. - * pvAccessCPP is distributed subject to a Software License Agreement found + * pvxs is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ -#ifndef LOG_H -#define LOG_H +#ifndef PVXS_LOG_H +#define PVXS_LOG_H #include @@ -65,4 +65,4 @@ PVXS_API void logger_config_env(); } // namespace pvxs -#endif // LOG_H +#endif // PVXS_LOG_H diff --git a/src/pvxs/unittest.h b/src/pvxs/unittest.h new file mode 100644 index 0000000..288f31d --- /dev/null +++ b/src/pvxs/unittest.h @@ -0,0 +1,105 @@ +/** + * 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 PVXS_UNITTEST_H +#define PVXS_UNITTEST_H + +/** @file pvxs/unittest.h + * + * C++ helpers for use with epicsUnitTest.h + */ + +#include + +#include +#include + +namespace pvxs { + +class PVXS_API testCase +{ + enum { + Nothing, // after move()'d + Diag, // no test, just print + Pass, + Fail, + } result; + std::ostringstream msg; +public: + //! new diag message + testCase(); + //! new test case + explicit testCase(bool result); + testCase(const testCase&) = delete; + testCase& operator=(const testCase&) = delete; + testCase(testCase&&) noexcept; + testCase& operator=(testCase&&) noexcept; + ~testCase(); + + template + inline testCase& operator<<(const T& v) { + msg< +struct test_print { + template + static inline void op(C& strm, const T& v) { + strm< +struct test_print { + template + static inline void op(C& strm, const std::string& v) { + strm<<'"'< +struct test_print { + template + static inline void op(C& strm, const char* v) { + strm<<'"'< +testCase testEq(const char *sLHS, const LHS& lhs, const char *sRHS, const RHS& rhs) +{ + testCase ret(lhs==rhs); + ret<::op(ret, lhs); + ret<<") == "<::op(ret, rhs); + ret<<") "; + return std::move(ret); +} + +template +testCase testNotEq(const char *sLHS, const LHS& lhs, const char *sRHS, const RHS& rhs) +{ + testCase ret(lhs!=rhs); + ret<::op(ret, lhs); + ret<<") != "<::op(ret, rhs); + ret<<") "; + return std::move(ret); +} + +} // namespace detail + +} // namespace pvxs + +#define testEq(LHS, RHS) ::pvxs::detail::testEq(#LHS, LHS, #RHS, RHS) +#define testNotEq(LHS, RHS) ::pvxs::detail::testNotEq(#LHS, LHS, #RHS, RHS) + +#endif // PVXS_UNITTEST_H diff --git a/src/pvxs/util.h b/src/pvxs/util.h new file mode 100644 index 0000000..e702e85 --- /dev/null +++ b/src/pvxs/util.h @@ -0,0 +1,50 @@ +/** + * 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 PVXS_UTIL_H +#define PVXS_UTIL_H + +#include +#include + +#include + +namespace pvxs { + +namespace detail { +// ref. wrapper to mark string for escaping +class Escaper +{ + const char* val; + friend std::ostream& operator<<(std::ostream& strm, const Escaper& esc); +public: + constexpr explicit Escaper(const char* v) :val(v) {} +}; + +PVXS_API +std::ostream& operator<<(std::ostream& strm, const Escaper& esc); + +} // namespace detail + +//! Print string to output string with non-printable charactors escaped. +//! @code +//! std::string blah("this \"is a test\""); +//! std::cout< { // only manipulate from loop worker thread std::map collectors; - Pvt() {} + Pvt() + :loop("PVXUDP", epicsThreadPriorityCAServerLow-4) + {} ~Pvt() { // we should only be destroyed after that last collector has removed itself diff --git a/src/udp_collector.h b/src/udp_collector.h index 86e5ada..5fd803e 100644 --- a/src/udp_collector.h +++ b/src/udp_collector.h @@ -1,6 +1,6 @@ /** * Copyright - See the COPYRIGHT that is included with this distribution. - * pvAccessCPP is distributed subject to a Software License Agreement found + * pvxs is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ diff --git a/src/unittest.cpp b/src/unittest.cpp new file mode 100644 index 0000000..bdfb997 --- /dev/null +++ b/src/unittest.cpp @@ -0,0 +1,49 @@ +/** + * 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 "pvxs/unittest.h" + +namespace pvxs { + +testCase::testCase() + :result(Diag) +{} + +testCase::testCase(bool result) + :result(result ? Pass : Fail) +{} + +testCase::testCase(testCase&& o) noexcept + :result(o.result) + ,msg(std::move(o.msg)) +{ + o.result = Nothing; +} + +testCase& testCase::operator=(testCase&& o) noexcept +{ + if(this!=&o) { + result = o.result; + o.result = Nothing; + msg = std::move(o.msg); + } + return *this; +} + +testCase::~testCase() +{ + if(result==Nothing) { + // do nothing! + } else if(result==Diag) { + testDiag("%s", msg.str().c_str()); + } else { + testOk(result==Pass, "%s", msg.str().c_str()); + } +} + +} // namespace pvxs diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000..cc25dc6 --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,64 @@ +/** + * 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 + +namespace pvxs { + +#define stringify(X) #X + +const char *version_str() +{ + return "PVXS " stringify(PVXS_MAJOR_VERSION); +} + +unsigned long version_int() +{ + return PVXS_VERSION; +} + +namespace detail { + +std::ostream& operator<<(std::ostream& strm, const Escaper& esc) +{ + const char *s = esc.val; + if(!s) { + strm<<""; + } else { + for(; *s; s++) { + char c = *s, next; + switch(c) { + case '\a': next = 'a'; break; + case '\b': next = 'b'; break; + case '\f': next = 'f'; break; + case '\n': next = 'n'; break; + case '\r': next = 'r'; break; + case '\t': next = 't'; break; + case '\v': next = 'v'; break; + case '\\': next = '\\'; break; + case '\'': next = '\''; break; + default: + if(isprint(c)) { + strm.put(c); + } else { + strm<<"\\x"< + +#include + +#include +#include + +namespace { +using namespace pvxsimpl; + +struct my_special_error : public std::runtime_error +{ + my_special_error() : std::runtime_error("Special") {} +}; + +void test_call() +{ + testDiag("%s", __func__); + + evbase base("TEST"); + + testOk1(!base.inLoop()); + + { + bool called = false; + base.call([&called, &base]() { + testDiag("in loop 1"); + called = true; + testOk1(!!base.inLoop()); + base.assertInLoop(); + }); + testOk1(called==true); + } + + { + bool called = false; + base.dispatch([&called]() { + testDiag("in loop 2"); + called = true; + }); + + base.sync(); + testOk1(called==true); + } + + try { + base.call([](){ + testDiag("in loop 3"); + throw my_special_error(); + }); + testFail("Unexpected success"); + }catch(my_special_error&) { + testPass("Caught expected exception"); + }catch(std::exception& e) { + testFail("Caught wrong exception : %s \"%s\"", typeid(e).name(), e.what()); + } + +} + +} // namespace + +MAIN(testev) +{ + testPlan(5); + test_call(); + return testDone(); +} diff --git a/test/testsock.cpp b/test/testsock.cpp index 9afae19..dfdfeca 100644 --- a/test/testsock.cpp +++ b/test/testsock.cpp @@ -1,6 +1,6 @@ /** * Copyright - See the COPYRIGHT that is included with this distribution. - * pvAccessCPP is distributed subject to a Software License Agreement found + * pvxs is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ @@ -12,6 +12,7 @@ #include #include +#include #include namespace { @@ -27,13 +28,13 @@ void test_udp() evsockaddr bind_addr(evsockaddr::loopback(AF_INET)); A.bind(bind_addr); - testOk(bind_addr.port()!=0, "bound port %u", bind_addr.port()); + testNotEq(bind_addr.port(), 0)<<"bound port"; evsockaddr send_addr(bind_addr); send_addr.setPort(0); B.bind(send_addr); - testOk(send_addr.port()!=0 && send_addr.port()!=bind_addr.port(), - "sending from port port %u", send_addr.port()); + testNotEq(send_addr.port(), 0); + testNotEq(send_addr.port(), bind_addr.port()); uint8_t msg[] = {0x12, 0x34, 0x56, 0x78}; int ret = sendto(B.sock, (char*)msg, sizeof(msg), 0, &bind_addr->sa, bind_addr.size()); @@ -48,7 +49,7 @@ void test_udp() testOk(ret==4 && rxbuf[0]==0x12 && rxbuf[1]==0x34 && rxbuf[2]==0x56 && rxbuf[3]==0x78, "Recv'd %d [%u, %u, %u, %u]", ret, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]); - testOk(src==send_addr, "Src %s==%s", src.tostring().c_str(), send_addr.tostring().c_str()); + testEq(src, send_addr); } void test_local_mcast() @@ -83,7 +84,7 @@ void test_local_mcast() uint8_t msg[] = {0x12, 0x34, 0x56, 0x78}; int ret = sendto(B.sock, (char*)msg, sizeof(msg), 0, &mcast_addr->sa, mcast_addr.size()); - testOk(ret==(int)sizeof(msg), "Send test ret==%d", ret); + testEq(ret, (int)sizeof(msg))<<"Send test"; uint8_t rxbuf[8] = {}; evsockaddr src; @@ -94,8 +95,7 @@ void test_local_mcast() testOk(ret==4 && rxbuf[0]==0x12 && rxbuf[1]==0x34 && rxbuf[2]==0x56 && rxbuf[3]==0x78, "Recv'd %d [%u, %u, %u, %u]", ret, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]); - testOk(src==sender_addr, "Src %s==%s", src.tostring().c_str(), sender_addr.tostring().c_str()); - + testEq(src, sender_addr); } void test_from_wire() @@ -204,7 +204,7 @@ void test_to_wire() MAIN(testsock) { - testPlan(32); + testPlan(33); test_udp(); test_local_mcast(); test_from_wire(); diff --git a/tools/pvxvct.cpp b/tools/pvxvct.cpp index e29e6a5..6cb366e 100644 --- a/tools/pvxvct.cpp +++ b/tools/pvxvct.cpp @@ -1,6 +1,6 @@ /** * Copyright - See the COPYRIGHT that is included with this distribution. - * pvAccessCPP is distributed subject to a Software License Agreement found + * pvxs is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */