Files
pvData/testApp/misc/testSharedVector.cpp
Michael Davidsaver dc94b26e50 fix static_shared_vector_cast<>() no-op casting
Turns out that Enablers as typically used for
member functions don't work to select
constructors.

Move this selection logic to
struct detail::static_shared_vector_caster<>
to correctly allow no-op casts (eg. void to void).
Previously this would not compile.

Allows PVScalarArray getAs() and putFrom() using
shared_vector<const void>.
2016-02-26 12:13:23 -05:00

582 lines
14 KiB
C++

/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/* Author: Michael Davidsaver */
#include <stddef.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <vector>
#include <epicsUnitTest.h>
#include <testMain.h>
#include "pv/sharedVector.h"
using std::string;
using namespace epics::pvData;
static void testEmpty()
{
testDiag("Test empty vector");
epics::pvData::shared_vector<int32> empty, empty2;
testOk1(empty.size()==0);
testOk1(empty.empty());
testOk1(empty.begin()==empty.end());
testOk1(empty.unique());
testOk1(empty.data()==NULL);
testOk1(empty==empty);
testOk1(!(empty!=empty));
testOk1(empty==empty2);
testOk1(!(empty!=empty2));
}
static void testInternalAlloc()
{
testDiag("Test vector alloc w/ new[]");
epics::pvData::shared_vector<int32> internal(5);
testOk1(internal.size()==5);
testOk1(!internal.empty());
testOk1(internal.unique());
testOk1(internal.data()!=NULL);
testOk1(internal.begin()+5==internal.end());
testOk1(internal.begin()==internal.end()-5);
testOk1(internal.begin()+2==internal.end()-3);
testOk1(internal.end()-internal.begin()==5);
internal[2] = 42;
testOk1(internal[2]==42);
epics::pvData::shared_vector<int32> internal2(15, 500);
testOk1(internal2.size()==15);
testOk1(internal2[1]==500);
internal.swap(internal2);
testOk1(internal2.size()==5);
testOk1(internal.size()==15);
internal.clear();
testOk1(internal.size()==0);
testOk1(internal.empty());
testOk1(internal.unique());
testOk1(internal.data()==NULL);
}
namespace {
//Note: STL shared_ptr requires that deletors be copy constructable
template<typename E>
struct callCounter {
std::tr1::shared_ptr<int32> count;
callCounter():count(new int32){*count=0;}
callCounter(const callCounter& o):count(o.count) {};
callCounter& operator=(const callCounter& o){count=o.count;}
void operator()(E){*count=1;}
};
}
static void testExternalAlloc()
{
testDiag("Test vector external alloc");
// Simulate a failed malloc() or similar
int32 *oops=0;
epics::pvData::shared_vector<int32> nullPtr(oops, 42, 100);
testOk1(nullPtr.size()==0);
testOk1(nullPtr.empty());
testOk1(nullPtr.begin()==nullPtr.end());
testOk1(nullPtr.unique());
testOk1(nullPtr.data()==NULL);
int32 *raw=new int32[5];
epics::pvData::shared_vector<int32> newData(raw, 1, 4);
testOk1(newData.size()==4);
testOk1(!newData.empty());
// check that offset is used
raw[1]=14;
testOk1(newData[0]==14);
// Check use of custom deleter
int32 localVar[4] = {1,2,3,4};
callCounter<int32*> tracker;
testOk1(*tracker.count==0);
epics::pvData::shared_vector<int32> locvar(localVar,
tracker,
0, 4);
testOk1(locvar[1]==2);
testOk1(*tracker.count==0);
newData.swap(locvar);
testOk1(*tracker.count==0);
newData.clear();
testOk1(*tracker.count==1);
}
static void testShare()
{
testDiag("Test vector Sharing");
epics::pvData::shared_vector<int32> one, two(15);
epics::pvData::shared_vector<int32> three(two);
testOk1(one.unique());
testOk1(!two.unique());
testOk1(!three.unique());
testOk1(two.data() == three.data());
one = two;
testOk1(!one.unique());
testOk1(two.data() == one.data());
one = one; // no-op
testOk1(two.data() == one.data());
one[1] = 43;
testOk1(two[1]==43);
testOk1(three[1]==43);
one.make_unique();
testOk1(one[1]==43);
one[1] = 143;
testOk1(two[1]==43);
testOk1(three[1]==43);
two.resize(two.size());
testOk1(two[1]==43);
two[1] = 243;
testOk1(one[1]==143);
testOk1(three[1]==43);
testOk1(one.unique());
testOk1(two.unique());
testOk1(three.unique());
one.resize(2);
testOk1(one.size()==2);
testOk1(one[1]==143);
testOk1(two.size()==15);
testOk1(three.size()==15);
two.resize(20, 5000);
testOk1(two[1]==243);
testOk1(one.size()==2);
testOk1(two.size()==20);
testOk1(three.size()==15);
testOk1(two[19]==5000);
}
static void testConst()
{
testDiag("Test constant vector");
epics::pvData::shared_vector<int32> writable(15, 100);
epics::pvData::shared_vector<int32>::reference wr = writable[0];
epics::pvData::shared_vector<int32>::const_reference ror = writable[0];
testOk1(wr==ror);
int32 *compare = writable.data();
testOk1(writable.unique());
// can re-target container, but data is R/O
epics::pvData::shared_vector<const int32> rodata(freeze(writable));
epics::pvData::shared_vector<const int32>::reference wcr = rodata[0];
epics::pvData::shared_vector<const int32>::const_reference rocr = rodata[0];
testOk1(wcr==rocr);
testOk1(rodata.data()==compare);
writable = thaw(rodata);
testOk1(writable.data()==compare);
rodata = freeze(writable);
testOk1(rodata.data()==compare);
epics::pvData::shared_vector<const int32> rodata2(rodata);
testOk1(rodata.data()==rodata2.data());
rodata2.make_unique();
testOk1(rodata.data()!=rodata2.data());
}
static void testSlice()
{
testDiag("Test vector slicing");
epics::pvData::shared_vector<int32> original(10, 100);
epics::pvData::shared_vector<int32> half1(original), half2(original), half2a(original);
half1.slice(0, 5);
half2.slice(5, 5);
half2a.slice(5);
testOk1(half1.dataOffset()==0);
testOk1(half2.dataOffset()==5);
testOk1(half2a.dataOffset()==5);
testOk1(half1.size()==5);
testOk1(half2.size()==5);
testOk1(half2a.size()==5);
testOk1(half1.dataTotal()==10);
testOk1(half2.dataTotal()==5);
testOk1(half2a.dataTotal()==5);
testOk1(original.data() == half1.data());
testOk1(half2.data() == half2a.data());
half1.slice(100000);
half2.slice(1);
half2a.slice(1,1);
testOk1(half1.dataOffset()==5);
testOk1(half2.dataOffset()==6);
testOk1(half2a.dataOffset()==6);
testOk1(half1.size()==0);
testOk1(half2.size()==4);
testOk1(half2a.size()==1);
testOk1(half1.dataTotal()==5);
testOk1(half2.dataTotal()==4);
testOk1(half2a.dataTotal()==4);
half2.clear();
testOk1(half2.dataOffset()==0);
testOk1(half2.dataCount()==0);
testOk1(half2.dataTotal()==0);
testOk1(half2.data()==NULL);
}
static void testCapacity()
{
testDiag("Test vector capacity");
epics::pvData::shared_vector<int32> vect(10, 100);
int32 *peek = vect.dataPtr().get();
vect.slice(0, 5);
testOk1(vect.size()==5);
testOk1(vect.dataTotal()==10);
testOk1(vect.dataPtr().get() == peek);
vect.resize(6);
testOk1(vect.dataPtr().get() == peek);
testOk1(vect.size()==6);
testOk1(vect.dataTotal()==10);
vect.resize(10);
testOk1(vect.dataPtr().get() == peek);
testOk1(vect.size()==10);
testOk1(vect.dataTotal()==10);
vect.resize(11);
testOk1(vect.dataPtr().get() != peek);
testOk1(vect.size()==11);
testOk1(vect.dataTotal()>=11);
vect[1] = 124;
vect.reserve(15);
testOk1(vect.size()==11);
testOk1(vect.dataTotal()>=15);
testOk1(vect[1]==124);
}
static void testPush()
{
epics::pvData::shared_vector<int32> vect;
testDiag("Test push_back optimizations");
size_t nallocs = 0;
size_t cap = vect.capacity();
for(size_t s=0; s<16*1024; s++) {
vect.push_back((int)s);
if(cap!=vect.capacity()) {
nallocs++;
cap = vect.capacity();
}
}
testDiag("push_back %ld times caused %ld re-allocations",
(unsigned long)vect.size(),
(unsigned long)nallocs);
testOk1(nallocs==26);
}
static void testVoid()
{
testDiag("Test vector cast to/from void");
epics::pvData::shared_vector<int32> typed(4);
epics::pvData::shared_vector<void> untyped(epics::pvData::static_shared_vector_cast<void>(typed));
testOk1(typed.dataPtr().get()==untyped.dataPtr().get());
testOk1(typed.size()*sizeof(int)==untyped.size());
untyped.slice(sizeof(int), 2*sizeof(int));
typed = epics::pvData::static_shared_vector_cast<int32>(untyped);
testOk1(typed.dataOffset()==1);
testOk1(typed.size()==2);
untyped.clear();
testDiag("Test vector cast to/from const void");
epics::pvData::shared_vector<const int32> ctyped(4);
epics::pvData::shared_vector<const void> cuntyped(epics::pvData::static_shared_vector_cast<const void>(ctyped));
// case const void to const void
epics::pvData::shared_vector<const void> cuntyped2(epics::pvData::static_shared_vector_cast<const void>(cuntyped));
testOk1(ctyped.dataPtr().get()==cuntyped2.dataPtr().get());
testOk1(ctyped.size()*sizeof(int)==cuntyped2.size());
cuntyped2.slice(sizeof(int), 2*sizeof(int));
ctyped = epics::pvData::static_shared_vector_cast<const int32>(cuntyped2);
testOk1(ctyped.dataOffset()==1);
testOk1(ctyped.size()==2);
}
struct dummyStruct {};
static void testNonPOD()
{
testDiag("Test vector of non-POD types");
epics::pvData::shared_vector<string> strings(6);
epics::pvData::shared_vector<std::tr1::shared_ptr<dummyStruct> > structs(5);
testOk1(strings[0].empty());
testOk1(structs[0].get()==NULL);
structs[1].reset(new dummyStruct);
dummyStruct *temp = structs[1].get();
epics::pvData::shared_vector<std::tr1::shared_ptr<dummyStruct> > structs2(structs);
testOk1(!structs.unique());
testOk1(structs[1].unique());
testOk1(structs2[1].get()==temp);
structs2.make_unique();
testOk1(structs.unique());
testOk1(!structs[1].unique());
testOk1(structs2[1].get()==temp);
}
static void testVectorConvert()
{
testDiag("Test shared_vector_convert");
epics::pvData::shared_vector<int32> ints(6, 42), moreints;
epics::pvData::shared_vector<float> floats;
epics::pvData::shared_vector<string> strings;
epics::pvData::shared_vector<void> voids;
testOk1(ints.unique());
// no-op convert. Just returns another reference
moreints = epics::pvData::shared_vector_convert<int32>(ints);
testOk1(!ints.unique());
moreints.clear();
// conversion when both types are known.
// returns a new vector
floats = epics::pvData::shared_vector_convert<float>(ints);
testOk1(ints.unique());
testOk1(floats.size()==ints.size());
testOk1(floats.at(0)==42.0);
// convert to void is static_shared_vector_cast<void>()
// returns a reference
voids = epics::pvData::shared_vector_convert<void>(ints);
testOk1(!ints.unique());
testOk1(voids.size()==ints.size()*sizeof(int32));
// convert from void uses shared_vector<void>::original_type()
// to find that the actual type is 'int32'.
// returns a new vector
testOk1(voids.original_type()==epics::pvData::pvInt);
strings = epics::pvData::shared_vector_convert<string>(voids);
voids.clear();
testOk1(ints.unique());
testOk1(strings.size()==ints.size());
testOk1(strings.at(0)=="42");
}
static void testWeak()
{
testDiag("Test weak_ptr counting");
epics::pvData::shared_vector<int32> data(6);
testOk1(data.unique());
std::tr1::shared_ptr<int32> pdata(data.dataPtr());
testOk1(!data.unique());
pdata.reset();
testOk1(data.unique());
std::tr1::weak_ptr<int32> wdata(data.dataPtr());
testOk1(data.unique()); // True, but I wish it wasn't!!!
pdata = wdata.lock();
testOk1(!data.unique());
}
static void testICE()
{
testDiag("Test freeze and thaw");
epics::pvData::shared_vector<int32> A(6, 42), C;
epics::pvData::shared_vector<const int32> B, D;
int32 *check = A.data();
// check freeze w/ unique reference
// clears A and moves reference to B
// no copy
B = epics::pvData::freeze(A);
testOk1(A.unique());
testOk1(B.unique());
testOk1(A.size()==0);
testOk1(B.size()==6);
testOk1(A.data()!=check);
testOk1(B.data()==check);
D = B; // create second const reference
// clears D, but reference to B
// remains, so a copy is made
C = epics::pvData::thaw(D);
testOk1(B.unique());
testOk1(C.unique());
testOk1(B.size()==6);
testOk1(C.size()==6);
testOk1(B.data()==check);
testOk1(C.data()!=NULL);
testOk1(C.at(0)==42);
C.clear();
// clears B and moves reference to A
// no copy
A = epics::pvData::thaw(B);
testOk1(A.unique());
testOk1(B.unique());
testOk1(A.size()==6);
testOk1(B.size()==0);
testOk1(A.data()==check);
testOk1(B.data()!=check);
C = A; // create second non-const reference
testOk1(!A.unique());
try {
// would clear A, but remaining reference C
// fails operation. A not cleared
// and exception thrown
B = epics::pvData::freeze(A);
testFail("Froze non-unique vector!");
} catch(std::runtime_error& e) {
testPass("freeze of non-unique throws runtime_error as expected");
}
}
MAIN(testSharedVector)
{
testPlan(167);
testDiag("Tests for shared_vector");
testDiag("sizeof(shared_vector<int32>)=%lu",
(unsigned long)sizeof(epics::pvData::shared_vector<int32>));
testEmpty();
testInternalAlloc();
testExternalAlloc();
testCapacity();
testShare();
testConst();
testSlice();
testPush();
testVoid();
testNonPOD();
testVectorConvert();
testWeak();
testICE();
return testDone();
}