/* * Copyright 2018-2019, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * 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. * * * Neither the name of the copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT * OWNER 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. */ #include "unittest.hpp" #include #include #include #include #include namespace pmemobj_exp = pmem::obj::experimental; static bool Is_pmemcheck_enabled = false; struct TestSuccess { void run() { auto slice = c.range(2, 2); UT_ASSERT(slice.size() == 2); UT_ASSERT(slice[0] == 3); UT_ASSERT(slice[1] == 4); UT_ASSERT(slice[0] == slice.at(0)); UT_ASSERT(slice[1] == slice.at(1)); UT_ASSERT(slice.begin() == c.begin() + 2); UT_ASSERT(slice.end() == c.begin() + 4); for (auto &it : slice) { it = 0; } UT_ASSERT(c[2] == 0); UT_ASSERT(c[3] == 0); auto zero_slice = c.range(0, 0); UT_ASSERT(zero_slice.begin() == zero_slice.end()); try { /* Out of range */ slice.at(2) = 2; UT_ASSERT(0); } catch (...) { } try { /* Out of range */ c.range(100, 2); UT_ASSERT(0); } catch (...) { } try { /* Out of range */ c.range(5, 2); UT_ASSERT(0); } catch (...) { } try { /* Out of range */ c.crange(5, 2); UT_ASSERT(0); } catch (...) { } try { /* Out of range */ c.range(5, 2, 1); UT_ASSERT(0); } catch (...) { } try { /* Out of range */ c.range(5, 2, 999); UT_ASSERT(0); } catch (...) { } try { /* Out of range */ c.range(5, 2, std::numeric_limits::max()); UT_ASSERT(0); } catch (...) { } try { /* Out of range */ static_cast(c).range(5, 2); UT_ASSERT(0); } catch (...) { } try { c.range(4, 2); } catch (...) { UT_ASSERT(0); } try { c.crange(4, 2); } catch (...) { UT_ASSERT(0); } try { c.range(4, 2, 1); } catch (...) { UT_ASSERT(0); } try { c.range(4, 2, 999); } catch (...) { UT_ASSERT(0); } try { c.range(4, 2, std::numeric_limits::max()); } catch (...) { UT_ASSERT(0); } try { static_cast(c).range(4, 2); } catch (...) { UT_ASSERT(0); } char data[10]; try { pmemobj_exp::slice good_slice(data, data); } catch (...) { UT_ASSERT(0); } try { pmemobj_exp::slice bad_slice(data + 1, data); UT_ASSERT(0); } catch (...) { } { auto ptr_s = c.range(0, 0); auto it_s = c.range(0, 0, 0); UT_ASSERT(ptr_s.size() == 0); UT_ASSERT(ptr_s.size() == it_s.size()); UT_ASSERT(ptr_s.begin() == it_s.begin()); UT_ASSERT(ptr_s.end() == it_s.end()); } { auto ptr_s = c.range(0, 5); auto it_s = c.range(0, 5, 1); UT_ASSERT(ptr_s.size() == 5); UT_ASSERT(ptr_s.size() == it_s.size()); UT_ASSERT(ptr_s.begin() == it_s.begin()); UT_ASSERT(ptr_s.end() == it_s.end()); } { auto ptr_s = c.range(1, 3); auto it_s = c.range(1, 3, 3); UT_ASSERT(ptr_s.size() == 3); UT_ASSERT(ptr_s.size() == it_s.size()); UT_ASSERT(ptr_s.begin() == it_s.begin()); UT_ASSERT(ptr_s.end() == it_s.end()); } } void run_reverse() { auto slice = c.range(1, 5, 2); UT_ASSERT(slice.size() == 5); int i = 0; for (auto it = slice.rbegin(); it != slice.rend(); it++, i++) *it = i; UT_ASSERT(c[5] == 0); UT_ASSERT(c[4] == 1); UT_ASSERT(c[3] == 2); UT_ASSERT(c[2] == 3); UT_ASSERT(c[1] == 4); } using C = pmemobj_exp::array; C c = {{1, 2, 3, 4, 5, 6}}; }; struct TestAbort { void run() { /* slice from 2 to 12 with snapshot_size = 3 * snapshotting ranges are: <2,4>, <5,7>, <8,10>, <11> */ auto slice = c.range(2, 10, 3); UT_ASSERT(slice.size() == 10); auto it = slice.begin(); /* it points to c[2], * <2,4> should be added to a transaction */ *it = 99; it += 9; /* it points to c[11] * <11> should be snapshotted */ *it = 102; it--; it--; /* it points to c[9], * <8,10> should be added to a transaction */ *it = 100; C expected = { {1, 2, 99, 4, 5, 6, 7, 8, 9, 100, 11, 102, 13, 14, 15}}; UT_ASSERT(c == expected); if (!Is_pmemcheck_enabled) { it = slice.begin() + 10; /* it points to c[12] (outside of range) * no snapshotting */ *it = 101; /* zero <5,7> range not adding it to a transaction */ c._data[5] = 0; c._data[6] = 0; c._data[7] = 0; C expected = {{1, 2, 99, 4, 5, 0, 0, 0, 9, 100, 11, 102, 101, 14, 15}}; UT_ASSERT(c == expected); auto ptr_slice = c2.range(1, 4); for (auto &e : ptr_slice) e = 1; c2._data[0] = 0; c2._data[5] = 0; C expected2 = {{0, 1, 1, 1, 1, 0, 7, 8, 9, 10, 11, 12, 13, 14, 15}}; UT_ASSERT(c2 == expected2); } } void run_zero() { auto slice = c.range(0, c.size(), 0); for (auto &e : slice) e = 0; } using C = pmemobj_exp::array; C c = {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}; C c2 = {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}; }; struct TestRanges { template void run() { int ex1[] = {1, 1, 1, 1, 1}; int ex2[] = {2, 2, 2, 2, 2}; auto slice = c.range(0, 7, snapshot_size); auto cslice = static_cast(c).range(0, 7); UT_ASSERT(slice.begin() == cslice.begin()); UT_ASSERT(slice.end() == cslice.end()); for (auto &e : slice) { std::fill(e.data, e.data + 5, 1); } for (auto &e : c.range(7, c.size() - 7)) { std::fill(e.data, e.data + 5, 2); } for (auto it = c.cbegin(); it < c.cbegin() + 7; it++) { UT_ASSERT(std::equal(it->data, it->data + 5, ex1)); } for (auto it = c.cbegin() + 7; it < c.cend(); it++) { UT_ASSERT(std::equal(it->data, it->data + 5, ex2)); } auto ptr_slice = c2.range(0, 5); for (auto &e : ptr_slice) std::fill(e.data, e.data + 5, 1); for (auto it = c2.cbegin(); it < c2.cbegin() + 5; it++) UT_ASSERT(std::equal(it->data, it->data + 5, ex1)); } struct DataStruct { int data[5] = {1, 2, 3, 4, 5}; }; using C = pmemobj_exp::array; C c; C c2; }; struct TestAt { void run() { auto slice = c.range(0, c.size(), 1); slice[2] = 1; slice.begin()[3] = 2; auto rit = slice.rbegin(); *rit = 2.5; rit++; *rit = 3; C excpected = {{0, 0, 1, 2, 3, 2.5}}; UT_ASSERT(c == excpected); } using C = pmemobj_exp::array; C c = {{0, 0, 0, 0, 0, 0}}; }; struct root { pmem::obj::persistent_ptr ptr_s; pmem::obj::persistent_ptr ptr_a; pmem::obj::persistent_ptr ptr_r; pmem::obj::persistent_ptr ptr_at; }; void run_test_success(pmem::obj::pool &pop) { auto r = pop.root(); try { pmem::obj::transaction::run(pop, [&] { r->ptr_s = pmem::obj::make_persistent(); }); } catch (...) { UT_ASSERT(0); } try { pmem::obj::transaction::run(pop, [&] { r->ptr_s->run(); r->ptr_s->run_reverse(); pmem::obj::delete_persistent(r->ptr_s); }); } catch (...) { UT_ASSERT(0); } } void run_test_abort(pmem::obj::pool &pop) { auto r = pop.root(); try { pmem::obj::transaction::run(pop, [&] { r->ptr_a = pmem::obj::make_persistent(); }); } catch (...) { UT_ASSERT(0); } /* Run TestAbort expecting success */ try { pmem::obj::transaction::run(pop, [&] { r->ptr_a->run(); pmem::obj::delete_persistent(r->ptr_a); }); } catch (...) { UT_ASSERT(0); } } void run_test_abort_with_revert(pmem::obj::pool &pop) { auto r = pop.root(); try { pmem::obj::transaction::run(pop, [&] { r->ptr_a = pmem::obj::make_persistent(); }); } catch (...) { UT_ASSERT(0); } /* Run TestAbort expecting transaction abort */ try { pmem::obj::transaction::run(pop, [&] { r->ptr_a->run(); pmem::obj::transaction::abort(0); UT_ASSERT(0); }); } catch (pmem::manual_tx_abort &) { if (Is_pmemcheck_enabled) { TestAbort::C expected = {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}; UT_ASSERT(r->ptr_a->c == expected); } else { /* Ensure that changes not added to the transaction were * not reverted */ TestAbort::C expected = {{1, 2, 3, 4, 5, 0, 0, 0, 9, 10, 11, 12, 101, 14, 15}}; UT_ASSERT(r->ptr_a->c == expected); /* Ensure that changes not added to the transaction were * not reverted */ TestAbort::C expected2 = {{0, 2, 3, 4, 5, 0, 7, 8, 9, 10, 11, 12, 13, 14, 15}}; UT_ASSERT(r->ptr_a->c2 == expected2); } } catch (...) { UT_ASSERT(0); } if (!Is_pmemcheck_enabled) { /* Run TestAbort expecting transaction abort */ try { pmem::obj::transaction::run(pop, [&] { r->ptr_a->run_zero(); pmem::obj::transaction::abort(0); UT_ASSERT(0); }); } catch (pmem::manual_tx_abort &) { /* Ensure that changes not added to the transaction were * not reverted */ TestAbort::C expected = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; UT_ASSERT(r->ptr_a->c == expected); } catch (...) { UT_ASSERT(0); } } try { pmem::obj::transaction::run(pop, [&] { pmem::obj::delete_persistent(r->ptr_a); }); } catch (...) { UT_ASSERT(0); } } void run_test_ranges(pmem::obj::pool &pop) { auto r = pop.root(); try { pmem::obj::transaction::run(pop, [&] { r->ptr_r = pmem::obj::make_persistent(); }); } catch (...) { UT_ASSERT(0); } try { pmem::obj::transaction::run(pop, [&] { r->ptr_r->run<1>(); pmem::obj::delete_persistent(r->ptr_r); }); } catch (...) { UT_ASSERT(0); } try { pmem::obj::transaction::run(pop, [&] { r->ptr_r = pmem::obj::make_persistent(); }); } catch (...) { UT_ASSERT(0); } try { pmem::obj::transaction::run(pop, [&] { r->ptr_r->run< std::numeric_limits::max()>(); pmem::obj::delete_persistent(r->ptr_r); }); } catch (...) { UT_ASSERT(0); } try { pmem::obj::transaction::run(pop, [&] { r->ptr_r = pmem::obj::make_persistent(); }); } catch (...) { UT_ASSERT(0); } try { pmem::obj::transaction::run(pop, [&] { r->ptr_r->run<999>(); pmem::obj::delete_persistent(r->ptr_r); }); } catch (...) { UT_ASSERT(0); } } void run_test_at(pmem::obj::pool &pop) { auto r = pop.root(); try { pmem::obj::transaction::run(pop, [&] { r->ptr_at = pmem::obj::make_persistent(); }); } catch (...) { UT_ASSERT(0); } try { pmem::obj::transaction::run(pop, [&] { r->ptr_at->run(); pmem::obj::delete_persistent(r->ptr_at); }); } catch (...) { UT_ASSERT(0); } } int main(int argc, char *argv[]) { START(); if (argc < 3) { std::cerr << "usage: " << argv[0] << " file-name " << "is-pmemcheck-enabled " << std::endl; return 1; } Is_pmemcheck_enabled = std::stoi(argv[2]); auto path = argv[1]; auto pop = pmem::obj::pool::create( path, "ArrayTest", PMEMOBJ_MIN_POOL, S_IWUSR | S_IRUSR); run_test_success(pop); run_test_abort(pop); run_test_abort_with_revert(pop); run_test_ranges(pop); run_test_at(pop); pop.close(); return 0; }