/** * 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 #include #include #include #include #include #include #include #include "pv/sharedVector.h" using std::string; using namespace epics::pvData; static void testEmpty() { testDiag("Test empty vector"); epics::pvData::shared_vector 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 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 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 struct callCounter { std::tr1::shared_ptr 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 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 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 tracker; testOk1(*tracker.count==0); epics::pvData::shared_vector 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 one, two(15); epics::pvData::shared_vector 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 writable(15, 100); epics::pvData::shared_vector::reference wr = writable[0]; epics::pvData::shared_vector::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 rodata(freeze(writable)); epics::pvData::shared_vector::reference wcr = rodata[0]; epics::pvData::shared_vector::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 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 original(10, 100); epics::pvData::shared_vector 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 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 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 typed(4); epics::pvData::shared_vector untyped(epics::pvData::static_shared_vector_cast(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(untyped); testOk1(typed.dataOffset()==1); testOk1(typed.size()==2); untyped.clear(); } static void testConstVoid() { testDiag("Test vector cast to/from const void"); epics::pvData::shared_vector ctyped(4); epics::pvData::shared_vector cuntyped(epics::pvData::static_shared_vector_cast(ctyped)); // case const void to const void epics::pvData::shared_vector cuntyped2(epics::pvData::static_shared_vector_cast(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(cuntyped2); testOk1(ctyped.dataOffset()==1); testOk1(ctyped.size()==2); epics::pvData::shared_vector untyped; // not possible to thaw() void as shared_vector has no make_unique() //untyped = thaw(cuntyped); cuntyped = freeze(untyped); } struct dummyStruct {}; static void testNonPOD() { testDiag("Test vector of non-POD types"); epics::pvData::shared_vector strings(6); epics::pvData::shared_vector > 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 > 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 ints(6, 42), moreints; epics::pvData::shared_vector floats; epics::pvData::shared_vector strings; epics::pvData::shared_vector voids; testOk1(ints.unique()); // no-op convert. Just returns another reference moreints = epics::pvData::shared_vector_convert(ints); testOk1(!ints.unique()); moreints.clear(); // conversion when both types are known. // returns a new vector floats = epics::pvData::shared_vector_convert(ints); testOk1(ints.unique()); testOk1(floats.size()==ints.size()); testOk1(floats.at(0)==42.0); // convert to void is static_shared_vector_cast() // returns a reference voids = epics::pvData::shared_vector_convert(ints); testOk1(!ints.unique()); testOk1(voids.size()==ints.size()*sizeof(int32)); // convert from void uses shared_vector::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(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 data(6); testOk1(data.unique()); std::tr1::shared_ptr pdata(data.dataPtr()); testOk1(!data.unique()); pdata.reset(); testOk1(data.unique()); std::tr1::weak_ptr 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 A(6, 42), C; epics::pvData::shared_vector 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"); } } static void testBad() { epics::pvData::shared_vector I; epics::pvData::shared_vector CI; epics::pvData::shared_vector F; epics::pvData::shared_vector CF; epics::pvData::shared_vector V; epics::pvData::shared_vector CV; (void)I; (void)CI; (void)F; (void)CF; (void)V; (void)CV; // Tests which should result in compile failure. // as there is no established way to test this automatically, // uncomment one at a time // No copy from const to non-const //CI = I; //I = CI; //epics::pvData::shared_vector CI2(I); //epics::pvData::shared_vector I2(CI); // shared_vector_convert can't thaw() //I = epics::pvData::shared_vector_convert(CI); //V = epics::pvData::shared_vector_convert(CV); // shared_vector_convert can't freeze() //CI = epics::pvData::shared_vector_convert(I); //CV = epics::pvData::shared_vector_convert(V); // static_shared_vector_cast can't thaw() //I = epics::pvData::static_shared_vector_cast(CI); //V = epics::pvData::static_shared_vector_cast(CV); // static_shared_vector_cast can't freeze() //CI = epics::pvData::static_shared_vector_cast(I); //CV = epics::pvData::static_shared_vector_cast(V); // freeze() can't change type. // the error here will be with the assignment //I = epics::pvData::freeze(CV); //I = epics::pvData::freeze(CF); //CI = epics::pvData::freeze(V); //CI = epics::pvData::freeze(F); // that() can't change type. // the error here will be with the assignment //CI = epics::pvData::thaw(V); //CI = epics::pvData::thaw(F); //I = epics::pvData::thaw(CV); //I = epics::pvData::that(CF); } MAIN(testSharedVector) { testPlan(167); testDiag("Tests for shared_vector"); testDiag("sizeof(shared_vector)=%lu", (unsigned long)sizeof(epics::pvData::shared_vector)); testEmpty(); testInternalAlloc(); testExternalAlloc(); testCapacity(); testShare(); testConst(); testSlice(); testPush(); testVoid(); testConstVoid(); testNonPOD(); testVectorConvert(); testWeak(); testICE(); testBad(); return testDone(); }