diff options
author | Robert Gemmell <robbie@apache.org> | 2015-06-25 10:22:51 +0000 |
---|---|---|
committer | Robert Gemmell <robbie@apache.org> | 2015-06-25 10:22:51 +0000 |
commit | 32ae758bc2e8fd962b66a4ab6341b14009f1907e (patch) | |
tree | 2f4d8174813284a6ea58bb6b7f6520aa92287476 /qpid/cpp/src/tests/legacystore/jrnl | |
parent | 116d91ad7825a98af36a869fc751206fbce0c59f (diff) | |
parent | f7e896076143de4572b4f1f67ef0765125f2498d (diff) | |
download | qpid-python-32ae758bc2e8fd962b66a4ab6341b14009f1907e.tar.gz |
NO-JIRA: create branch for qpid-cpp 0.34 RC process
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/qpid-cpp-0.34-rc@1687469 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/cpp/src/tests/legacystore/jrnl')
53 files changed, 11172 insertions, 0 deletions
diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_auto_expand.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_st_auto_expand.cpp new file mode 100644 index 0000000000..fb5c1f1742 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_auto_expand.cpp @@ -0,0 +1,140 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" +#include <cmath> +#include <iostream> +#include "qpid/legacystore/jrnl/jcntl.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(journal_auto_expand) + +const string test_filename("_st_auto_expand"); + +#include "_st_helper_fns.h" + +// === Test suite === + +QPID_AUTO_TEST_CASE(no_ae_threshold) +{ + string test_name = get_test_name(test_filename, "no_ae_threshold"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + unsigned m; + + // Fill journal to just below threshold + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, + DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, LARGE_MSG_REC_SIZE_DBLKS); + for (m=0; m<t; m++) + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + // This enqueue should exceed the threshold + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t); + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false, RHM_IORES_ENQCAPTHRESH); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(no_ae_threshold_dequeue_some) +{ + string test_name = get_test_name(test_filename, "no_ae_threshold_dequeue_some"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + unsigned m; + + // Fill journal to just below threshold + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, + DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, LARGE_MSG_REC_SIZE_DBLKS); + for (m=0; m<t; m++) + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + // This enqueue should exceed the threshold + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t); + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false, RHM_IORES_ENQCAPTHRESH); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t); + + // Dequeue 25 msgs + #define NUM_MSGS_DEQ 25 + for (m=0; m<NUM_MSGS_DEQ; m++) + deq_msg(jc, m, m+t); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(t-NUM_MSGS_DEQ)); + + // Check we can still enqueue and dequeue + for (m=t+NUM_MSGS_DEQ; m<t+NUM_MSGS_DEQ+NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + for (m=t+NUM_MSGS_DEQ; m<t+NUM_MSGS_DEQ+NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS_DEQ+NUM_MSGS); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(no_ae_threshold_dequeue_all) +{ + string test_name = get_test_name(test_filename, "no_ae_threshold_dequeue_all"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + unsigned m; + + // Fill journal to just below threshold + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, + DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, LARGE_MSG_REC_SIZE_DBLKS); + for (m=0; m<t; m++) + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + // This enqueue should exceed the threshold + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t); + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false, RHM_IORES_ENQCAPTHRESH); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), t); + + // Dequeue all msgs + for (m=0; m<t; m++) + deq_msg(jc, m, m+t); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + + // Check we can still enqueue and dequeue + for (m=2*t; m<2*t + NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + for (m=2*t; m<2*t + NUM_MSGS; m++) + deq_msg(jc, m, m+2*t+NUM_MSGS); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_basic.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_st_basic.cpp new file mode 100644 index 0000000000..4aa6d2e29f --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_basic.cpp @@ -0,0 +1,558 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" +#include <cmath> +#include <iostream> +#include "qpid/legacystore/jrnl/jcntl.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(journal_basic) + +const string test_filename("_st_basic"); + +#include "_st_helper_fns.h" + +// === Test suite === + +#ifndef LONG_TEST +/* + * ============================================== + * NORMAL TESTS + * This section contains normal "make check" tests + * for building/packaging. These are built when + * LONG_TEST is _not_ defined. + * ============================================== + */ + +QPID_AUTO_TEST_CASE(instantiation) +{ + string test_name = get_test_name(test_filename, "instantiation"); + try + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + BOOST_CHECK_EQUAL(jc.is_ready(), false); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(initialization) +{ + string test_name = get_test_name(test_filename, "initialization"); + try + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + BOOST_CHECK_EQUAL(jc.is_ready(), false); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + BOOST_CHECK_EQUAL(jc.is_ready(), true); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_dequeue_block) +{ + string test_name = get_test_name(test_filename, "enqueue_dequeue_block"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m)); + for (int m=0; m<NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS); + + // Again... + for (int m=2*NUM_MSGS; m<3*NUM_MSGS; m++) + BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m)); + for (int m=2*NUM_MSGS; m<3*NUM_MSGS; m++) + deq_msg(jc, m, m+3*NUM_MSGS); + + // Disjoint rids + for (int m=10*NUM_MSGS; m<11*NUM_MSGS; m++) + BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m)); + for (int m=10*NUM_MSGS; m<11*NUM_MSGS; m++) + deq_msg(jc, m, m+11*NUM_MSGS); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_dequeue_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_dequeue_interleaved"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<2*NUM_MSGS; m+=2) + { + BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m)); + deq_msg(jc, m, m+1); + } + + // Again... + for (int m=2*NUM_MSGS; m<4*NUM_MSGS; m+=2) + { + BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m)); + deq_msg(jc, m, m+1); + } + + // Disjoint rids + for (int m=10*NUM_MSGS; m<12*NUM_MSGS; m+=2) + { + BOOST_CHECK_EQUAL(enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false), u_int64_t(m)); + deq_msg(jc, m, m+1); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_dequeue_interleaved_file_rollover) +{ + string test_name = get_test_name(test_filename, "enqueue_dequeue_interleaved_file_rollover"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + unsigned n = num_msgs_to_full(NUM_TEST_JFILES, TEST_JFSIZE_SBLKS * JRNL_SBLK_SIZE, + 16*MSG_REC_SIZE_DBLKS, true); + for (unsigned m=0; m<3*2*n; m+=2) // overwrite files 3 times + { + enq_msg(jc, m, create_msg(msg, m, 16*MSG_SIZE), false); + deq_msg(jc, m, m+1); + } + jc.stop(true); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(empty_recover) +{ + string test_name = get_test_name(test_filename, "empty_recover"); + try + { + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + BOOST_CHECK_EQUAL(jc.is_ready(), false); + BOOST_CHECK_EQUAL(jc.is_read_only(), false); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + BOOST_CHECK_EQUAL(jc.is_ready(), true); + BOOST_CHECK_EQUAL(jc.is_read_only(), false); + } + { + u_int64_t hrid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + BOOST_CHECK_EQUAL(jc.is_ready(), false); + BOOST_CHECK_EQUAL(jc.is_read_only(), false); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(jc.is_ready(), true); + BOOST_CHECK_EQUAL(jc.is_read_only(), true); + BOOST_CHECK_EQUAL(hrid, u_int64_t(0)); + } + { + u_int64_t hrid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + BOOST_CHECK_EQUAL(jc.is_ready(), false); + BOOST_CHECK_EQUAL(jc.is_read_only(), false); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(jc.is_ready(), true); + BOOST_CHECK_EQUAL(jc.is_read_only(), true); + BOOST_CHECK_EQUAL(hrid, u_int64_t(0)); + jc.recover_complete(); + BOOST_CHECK_EQUAL(jc.is_ready(), true); + BOOST_CHECK_EQUAL(jc.is_read_only(), false); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_recover_dequeue_block) +{ + string test_name = get_test_name(test_filename, "enqueue_recover_dequeue_block"); + try + { + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + } + { + u_int64_t hrid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(NUM_MSGS - 1)); + jc.recover_complete(); + for (int m=0; m<NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_recover_dequeue_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_recover_dequeue_interleaved"); + try + { + string msg; + u_int64_t hrid; + + for (int m=0; m<2*NUM_MSGS; m+=2) + { + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + if (m == 0) + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); // First time only + else + { + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(m - 1)); + jc.recover_complete(); + } + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + } + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(m)); + jc.recover_complete(); + deq_msg(jc, m, m+1); + } + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(header_flags) +{ + string test_name = get_test_name(test_filename, "header_flags"); + try + { + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + // Transient msgs - should not recover + for (int m=0; m<NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), true); + // Persistent msgs + for (int m=NUM_MSGS; m<NUM_MSGS*2; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + // Transient extern msgs - should not recover + for (int m=NUM_MSGS*2; m<NUM_MSGS*3; m++) + enq_extern_msg(jc, m, MSG_SIZE, true); + // Persistnet extern msgs + for (int m=NUM_MSGS*3; m<NUM_MSGS*4; m++) + enq_extern_msg(jc, m, MSG_SIZE, false); + } + { + string msg; + u_int64_t hrid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + // Recover non-transient msgs + for (int m=NUM_MSGS; m<NUM_MSGS*2; m++) + { + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_MESSAGE(transientFlag == false, "Transient message recovered."); + BOOST_CHECK_MESSAGE(externalFlag == false, "External flag incorrect."); + BOOST_CHECK_MESSAGE(create_msg(msg, m, MSG_SIZE).compare(rmsg) == 0, + "Non-transient message corrupt during recover."); + } + // Recover non-transient extern msgs + for (int m=NUM_MSGS*3; m<NUM_MSGS*4; m++) + { + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_MESSAGE(transientFlag == false, "Transient message recovered."); + BOOST_CHECK_MESSAGE(externalFlag == true, "External flag incorrect."); + BOOST_CHECK_MESSAGE(rmsg.size() == 0, "External message returned non-zero size."); + } + jc.recover_complete(); + // Read recovered non-transient msgs + for (int m=NUM_MSGS; m<NUM_MSGS*2; m++) + { + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_MESSAGE(transientFlag == false, "Transient message recovered."); + BOOST_CHECK_MESSAGE(externalFlag == false, "External flag incorrect."); + BOOST_CHECK_MESSAGE(create_msg(msg, m, MSG_SIZE).compare(rmsg) == 0, + "Non-transient message corrupt during recover."); + } + // Read recovered non-transient extern msgs + for (int m=NUM_MSGS*3; m<NUM_MSGS*4; m++) + { + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_MESSAGE(transientFlag == false, "Transient message recovered."); + BOOST_CHECK_MESSAGE(externalFlag == true, "External flag incorrect."); + BOOST_CHECK_MESSAGE(rmsg.size() == 0, "External message returned non-zero size."); + } + // Dequeue recovered messages + for (int m=NUM_MSGS; m<NUM_MSGS*2; m++) + deq_msg(jc, m, m+3*NUM_MSGS); + for (int m=NUM_MSGS*3; m<NUM_MSGS*4; m++) + deq_msg(jc, m, m+2*NUM_MSGS); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(double_dequeue) +{ + string test_name = get_test_name(test_filename, "double_dequeue"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + enq_msg(jc, 0, create_msg(msg, 0, MSG_SIZE), false); + deq_msg(jc, 0, 1); + try{ deq_msg(jc, 0, 2); BOOST_ERROR("Did not throw exception on second dequeue."); } + catch (const jexception& e){ BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_WMGR_DEQRIDNOTENQ); } + enq_msg(jc, 2, create_msg(msg, 1, MSG_SIZE), false); + deq_msg(jc, 2, 3); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +#else +/* + * ============================================== + * LONG TESTS + * This section contains long tests and soak tests, + * and are run using target check-long (ie "make + * check-long"). These are built when LONG_TEST is + * defined. + * ============================================== + */ + +QPID_AUTO_TEST_CASE(journal_overflow) +{ + string test_name = get_test_name(test_filename, "journal_overflow"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + unsigned m; + + // Fill journal to just below threshold + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, + MSG_REC_SIZE_DBLKS); + u_int32_t d = num_dequeues_rem(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE); + for (m=0; m<t; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + // This enqueue should exceed the threshold + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false, RHM_IORES_ENQCAPTHRESH); + + // Dequeue as many msgs as possible except first + for (m=1; m<=d; m++) + deq_msg(jc, m, m+t); + deq_msg(jc, d+1, d+2, RHM_IORES_FULL); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(file_cycle_block) +{ + string test_name = get_test_name(test_filename, "file_cycle_block"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + + // 5 cycles of enqueue/dequeue blocks of half threshold exception size + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, + LARGE_MSG_REC_SIZE_DBLKS)/2; + for (unsigned i=0; i<5; i++) + { + for (unsigned m=2*i*t; m<(2*i+1)*t; m++) + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + for (unsigned m=2*i*t; m<(2*i+1)*t; m++) + deq_msg(jc, m, m+t); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(file_cycle_interleaved) +{ + string test_name = get_test_name(test_filename, "file_cycle_interleaved"); + try + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + + // 5 cycles of enqueue/dequeue blocks of half threshold exception size + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, + LARGE_MSG_REC_SIZE_DBLKS)/2; + for (unsigned m=0; m<5*2*t; m+=2) + { + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + deq_msg(jc, m, m+1); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(recover_file_cycle_block) +{ + string test_name = get_test_name(test_filename, "recover_file_cycle_block"); + try + { + string msg; + u_int64_t hrid; + + // 5 cycles of enqueue/dequeue blocks of half threshold exception size + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, + LARGE_MSG_REC_SIZE_DBLKS)/2; + for (unsigned i=0; i<5; i++) + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + if (i) + { + jc.recover(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(2*i*t - 1)); + jc.recover_complete(); + } + else + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + + for (unsigned m=2*i*t; m<(2*i+1)*t; m++) + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + for (unsigned m=2*i*t; m<(2*i+1)*t; m++) + deq_msg(jc, m, m+t); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(recover_file_cycle_interleaved) +{ + string test_name = get_test_name(test_filename, "recover_file_cycle_interleaved"); + try + { + string msg; + u_int64_t hrid; + + // 5 cycles of enqueue/dequeue blocks of half threshold exception size + u_int32_t t = num_msgs_to_threshold(NUM_DEFAULT_JFILES, DEFAULT_JFSIZE_SBLKS * JRNL_SBLK_SIZE, + LARGE_MSG_REC_SIZE_DBLKS)/2; + for (unsigned i=0; i<5; i++) + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + if (i) + { + jc.recover(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(2*i*t - 1)); + jc.recover_complete(); + } + else + jc.initialize(NUM_DEFAULT_JFILES, false, 0, DEFAULT_JFSIZE_SBLKS); + + for (unsigned m=2*i*t; m<2*(i+1)*t; m+=2) + { + enq_msg(jc, m, create_msg(msg, m, LARGE_MSG_SIZE), false); + deq_msg(jc, m, m+1); + } + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +#endif + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_basic_txn.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_st_basic_txn.cpp new file mode 100644 index 0000000000..aa2d31c2ae --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_basic_txn.cpp @@ -0,0 +1,239 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" +#include <cmath> +#include <iostream> +#include "qpid/legacystore/jrnl/jcntl.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(journal_basic_txn) + +const string test_filename("_st_basic_txn"); + +#include "_st_helper_fns.h" + +// === Test suite === + +QPID_AUTO_TEST_CASE(enqueue_commit_dequeue_block) +{ + string test_name = get_test_name(test_filename, "enqueue_commit_dequeue_block"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + create_xid(xid, 0, XID_SIZE); + for (int m=0; m<NUM_MSGS; m++) + BOOST_CHECK_EQUAL(enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(m)); + txn_commit(jc, NUM_MSGS, xid); + for (int m=0; m<NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS+1); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_abort_dequeue_block) +{ + string test_name = get_test_name(test_filename, "enqueue_abort_dequeue_block"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + create_xid(xid, 0, XID_SIZE); + for (int m=0; m<NUM_MSGS; m++) + BOOST_CHECK_EQUAL(enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(m)); + txn_abort(jc, NUM_MSGS, xid); + for (int m=0; m<NUM_MSGS; m++) + { + try + { + deq_msg(jc, m, m+NUM_MSGS+1); + BOOST_ERROR("Expected dequeue to fail with exception JERR_WMGR_DEQRIDNOTENQ."); + } + catch (const jexception& e) { if (e.err_code() != jerrno::JERR_WMGR_DEQRIDNOTENQ) throw; } + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_commit_dequeue_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_commit_dequeue_interleaved"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + create_xid(xid, m, XID_SIZE); + BOOST_CHECK_EQUAL(enq_txn_msg(jc, 3*m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(3*m)); + txn_commit(jc, 3*m+1, xid); + deq_msg(jc, 3*m, 3*m+2); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_abort_dequeue_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_abort_dequeue_interleaved"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + create_xid(xid, m, XID_SIZE); + BOOST_CHECK_EQUAL(enq_txn_msg(jc, 3*m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(3*m)); + txn_abort(jc, 3*m+1, xid); + try + { + deq_msg(jc, 2*m, 2*m+2); + BOOST_ERROR("Expected dequeue to fail with exception JERR_WMGR_DEQRIDNOTENQ."); + } + catch (const jexception& e) { if (e.err_code() != jerrno::JERR_WMGR_DEQRIDNOTENQ) throw; } + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_dequeue_commit_block) +{ + string test_name = get_test_name(test_filename, "enqueue_dequeue_commit_block"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + create_xid(xid, 0, XID_SIZE); + for (int m=0; m<NUM_MSGS; m++) + BOOST_CHECK_EQUAL(enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(m)); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + for (int m=0; m<NUM_MSGS; m++) + deq_txn_msg(jc, m, m+NUM_MSGS, xid); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + txn_commit(jc, 2*NUM_MSGS, xid); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_dequeue_abort_block) +{ + string test_name = get_test_name(test_filename, "enqueue_dequeue_abort_block"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + create_xid(xid, 0, XID_SIZE); + for (int m=0; m<NUM_MSGS; m++) + BOOST_CHECK_EQUAL(enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(m)); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + for (int m=0; m<NUM_MSGS; m++) + deq_txn_msg(jc, m, m+NUM_MSGS, xid); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + txn_abort(jc, 2*NUM_MSGS, xid); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_dequeue_commit_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_dequeue_commit_interleaved"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + create_xid(xid, m, XID_SIZE); + BOOST_CHECK_EQUAL(enq_txn_msg(jc, 3*m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(3*m)); + deq_txn_msg(jc, 3*m, 3*m+1, xid); + txn_commit(jc, 3*m+2, xid); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_dequeue_abort_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_dequeue_abort_interleaved"); + try + { + string msg; + string xid; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + create_xid(xid, m, XID_SIZE); + BOOST_CHECK_EQUAL(enq_txn_msg(jc, 3*m, create_msg(msg, m, MSG_SIZE), xid, false), u_int64_t(3*m)); + deq_txn_msg(jc, 3*m, 3*m+1, xid); + txn_abort(jc, 3*m+2, xid); + BOOST_CHECK_EQUAL(jc.get_enq_cnt(), u_int32_t(0)); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_helper_fns.h b/qpid/cpp/src/tests/legacystore/jrnl/_st_helper_fns.h new file mode 100644 index 0000000000..923065dd11 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_helper_fns.h @@ -0,0 +1,882 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +// NOTE: This file is included in _st_*.cpp files inside the QPID_AUTO_TEST_SUITE() +// definition. + +#define MAX_AIO_SLEEPS 500 +#define AIO_SLEEP_TIME 1000 +#define NUM_TEST_JFILES 4 +#define NUM_DEFAULT_JFILES 8 +#define JRNL_DEFAULT_FSIZE 24 // Multiples of JRNL_RMGR_PAGE_SIZE +#define TEST_JFSIZE_SBLKS 128 +#define DEFAULT_JFSIZE_SBLKS (JRNL_DEFAULT_FSIZE * JRNL_RMGR_PAGE_SIZE) +#define NUM_MSGS 5 +#define MSG_REC_SIZE_DBLKS 2 +#define MSG_SIZE (MSG_REC_SIZE_DBLKS * JRNL_DBLK_SIZE) - sizeof(enq_hdr) - sizeof(rec_tail) +#define LARGE_MSG_REC_SIZE_DBLKS (JRNL_SBLK_SIZE * JRNL_RMGR_PAGE_SIZE) +#define LARGE_MSG_SIZE (LARGE_MSG_REC_SIZE_DBLKS * JRNL_DBLK_SIZE) - sizeof(enq_hdr) - sizeof(rec_tail) +#define XID_SIZE 64 + +#define XLARGE_MSG_RATIO (1.0 * LARGE_MSG_REC_SIZE / JRNL_DBLK_SIZE / JRNL_SBLK_SIZE / JRNL_RMGR_PAGE_SIZE) +#define XLARGE_MSG_THRESHOLD (int)(JRNL_DEFAULT_FSIZE * NUM_DEFAULT_JFILES * JRNL_ENQ_THRESHOLD / 100 / LARGE_MSG_RATIO) + +#define NUM_JFILES 4 +#define JFSIZE_SBLKS 128 + +const char* tdp = getenv("TMP_DATA_DIR"); +const string test_dir(tdp && strlen(tdp) > 0 ? string(tdp) + "/" + test_filename : "/var/tmp/jrnl_test"); + +class test_dtok : public data_tok +{ +private: + bool flag; +public: + test_dtok() : data_tok(), flag(false) {} + virtual ~test_dtok() {} + bool done() { if (flag || _wstate == NONE) return true; else { flag = true; return false; } } +}; + +class test_jrnl_cb : public aio_callback { + virtual void wr_aio_cb(std::vector<data_tok*>& dtokl) + { + for (std::vector<data_tok*>::const_iterator i=dtokl.begin(); i!=dtokl.end(); i++) + { + test_dtok* dtp = static_cast<test_dtok*>(*i); + if (dtp->done()) + delete dtp; + } + } + virtual void rd_aio_cb(std::vector<u_int16_t>& /*pil*/) {} +}; + +class test_jrnl : public jcntl +{ +test_jrnl_cb* cb; + +public: + test_jrnl(const std::string& jid, const std::string& jdir, const std::string& base_filename, test_jrnl_cb& cb0) : + jcntl(jid, jdir, base_filename), + cb(&cb0) {} + virtual ~test_jrnl() {} + void initialize(const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, + const u_int32_t jfsize_sblks) + { + jcntl::initialize(num_jfiles, ae, ae_max_jfiles, jfsize_sblks, JRNL_WMGR_DEF_PAGES, JRNL_WMGR_DEF_PAGE_SIZE, + cb); + _jdir.create_dir(); + } + void recover(const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, + vector<string>* txn_list, u_int64_t& highest_rid) + { jcntl::recover(num_jfiles, ae, ae_max_jfiles, jfsize_sblks, JRNL_WMGR_DEF_PAGES, JRNL_WMGR_DEF_PAGE_SIZE, cb, + txn_list, highest_rid); } +}; + +/* +* This class is for testing recover functionality by maintaining an internal lfid-pfid map, then creating physical +* journal file stubs (just the fhdr section of the journal) and jinf file. This allows the recover functionality (which +* analyzes these components to determine recover order). +* +* First set up a map or "blueprint" of what the journal should look like for recovery, then have the class create the +* physical files. The jinf object under test then reads and analyzes the created journal, and it's analysis is checked +* against what is expected. +* +* General usage pattern: +* 1. Create instance of lfid_pfid_map. +* 2. Call lfid_pfid_map::journal_create() to simulate initial journal creation. +* 3. (optional) Call lfid_pfid_map::journal_insert() one or more times to simulate the addition of journal files. +* 4. Call lfid_pfid_map::write_journal() to create dummy journal files (files containing only file headers) +* 5. Create and initialize the jinf object under test +* 6. Call jinf::analyze() to determine the pfid order - and thus also first and last lids +* 7. Call lfid_pfid_map::check_analysis() to check the conclusions of the analysis +* 8. Call lfid_pfid_map::destroy_journal() to delete the journal files and reset the lfid_pfid_map object. +* 9. (optional) Back to step 2 for more tests +* +* See the individual methods below for more details. +*/ +class lfid_pfid_map +{ + public: + typedef pair<u_int16_t, file_hdr> lppair; // Used for loading the map + typedef multimap<u_int16_t, file_hdr> lpmap; // Stores the journal "plan" before it is created on-disk + typedef lpmap::const_iterator lpmap_citr; // General purpose iterator + typedef pair<lpmap_citr, lpmap_citr> lpmap_range; // Range of values returned by multimap's equal_range() fn + + private: + string _jid; // Journal id + string _base_filename; // Base filename + lpmap _map; // Stores the journal "blueprint" before it is created on-disk + u_int16_t _num_used_files; // number of files which contain jorunals + u_int16_t _oldest_lfid; // lfid where owi flips; always 0 if !_full + u_int16_t _last_pfid; // last pfid (ie last file added) + + public: + lfid_pfid_map(const string& jid, const string& base_filename) : + _jid(jid), _base_filename(base_filename), _num_used_files(0), _oldest_lfid(0), _last_pfid(0) + {} + virtual ~lfid_pfid_map() {} + + // Mainly used for debugging + void print() + { + int cnt = 0; + for (lpmap_citr i=_map.begin(); i!=_map.end(); i++, cnt++) + { + const file_hdr fh = i->second; + cout << " " << cnt << ": owi=" << (fh.get_owi()?"t":"f") << hex << " frid=0x" << fh._rid; + cout << " pfid=0x" << fh._pfid << " lfid=0x" << fh._lfid << " fro=0x" << fh._fro << dec << endl; + } + } + + std::size_t size() + { + return _map.size(); + } + + /* + * Method journal_create(): Used to simulate the initial creation of a journal before file insertions + * take place. + * + * num_jfiles: The initial journal file count. + * num_used_jfiles: If this number is less than num_jfiles, it indicates a clean journal that has not yet + * completed its first rotation, and some files are empty (ie all null). The first + * num_used_jfiles will contain file headers, the remainder will be blank. + * oldest_lfid: The lfid (==pfid, see note 1 below) at which the owi flag flips. During normal operation, + * each time the journal rotates back to file 0, a flag (called the overwrite indicator or owi) + * is flipped. This flag is saved in the file header. During recovery, if scanning from logical + * file 0 upwards, the file at which this flag reverses from its value in file 0 is the file + * that was to have been overwritten next, and is thus the "oldest" file. Recovery analysis must + * start with this file. oldest_lfid sets the file at which this flag will flip value for the + * simulated recovery analysis. Note that this will be ignored if num_used_jfiles < num_jfiles, + * as it is not possible for an overwrite to have occurred if not all the files have been used. + * first_owi: Sets the value of the owi flag in file 0. If set to false, then the flip will be found with + * a true flag (and visa versa). + * + * NOTES: + * 1. By definition, the lfids and pfids coincide for a journal containing no inserted files. Thus pfid == lfid + * for all journals created after using initial_journal_create() alone. + * 2. By definition, if a journal is not full (num_used_jfiles < num_jfiles), then all owi flags for those files + * that are used must be the same. It is not possible for an overwrite situation to arise if a journal is not + * full. + * 3. This function acts on map _map only, and does not create any test files. Call write_journal() to do that. + * 4. This function must be called on a clean test object or on one where the previous test data has been + * cleared by calling journal_destroy(). Running this function more than once on existing data will + * result in invalid journals which cannot be recovered. + */ + void journal_create(const u_int16_t num_jfiles, // Total number of files + const u_int16_t num_used_jfiles, // Number of used files, rest empty at end + const u_int16_t oldest_lfid = 0, // Fid where owi reverses + const u_int16_t bad_lfid = 0, // Fid where owi reverses again (must be > oldest_lifd), + // used for testing bad owi detection + const bool first_owi = false) // Value of first owi flag (ie pfid=0) + { + const bool full = num_used_jfiles == num_jfiles; + bool owi = first_owi; + _oldest_lfid = full ? oldest_lfid : 0; + for (u_int16_t lfid = 0; lfid < num_jfiles; lfid++) + { + const u_int16_t pfid = lfid; + file_hdr fh; + if (pfid < num_used_jfiles) + { + _num_used_files = num_used_jfiles; + /* + * Invert the owi flag from its current value (initially given by first_owi param) only if: + * 1. The journal is full (ie all files are used) + * AND + * 2. oldest_lfid param is non-zero (this is default, but lfid 0 being inverted is logically + * inconsistent with first_owi parameter being present) + * AND + * 3. Either: + * * current lfid == oldest_lfid (ie we are preparing the oldest lfid) + * OR + * * current lfid == bad_lfid AND bad_lfid > oldest (ie we are past the oldest and preparing the + * bad lfid) + */ + if (full && oldest_lfid > 0 && + (lfid == oldest_lfid || (bad_lfid > oldest_lfid && lfid == bad_lfid))) + owi = !owi; + const u_int64_t frid = u_int64_t(random()); + init_fhdr(fh, frid, pfid, lfid, owi); + } + _map.insert(lppair(lfid, fh)); + } + } + + /* + * Method journal_insert(): Used to simulate the insertion of journal files into an existing journal. + * + * after_lfid: The logical file id (lfid) after which the new file is to be inserted. + * num_files: The number of files to be inserted. + * adjust_lids: Flag indicating that the lids of files _following_ the inserted files are to be adjusted upwards + * by the number of inserted files. Not doing so simulates a recovery immediately after insertion + * but before the following files are overwritten with their new lids. If this is set false, then: + * a) after_lfid MUST be the most recent file (_oldest_lfid-1 ie last lfid before owi changes). + * b) This call must be the last insert call. + * + * NOTES: + * 1. It is not possible to insert before lfid/pfid 0; thus these are always coincidental. This operation is + * logically equivalent to inserting after the last lfid, which is possible. + * 2. It is not possible to insert into a journal that is not full. Doing so will result in an unrecoverable + * journal (one that is logically inconsistent that can never occur in reality). + * 3. If a journal is stopped/interrupted immediately after a file insertion, there could be duplicate lids in + * play at recovery, as the following file lids in their headers are only overwritten when the file is + * eventually written to during normal operation. The owi flags, however, are used to determine which of the + * ambiguous lids are the inserted files. + * 4. This function acts on map _map only, and does not create any test files. Call write_journal() to do that. + */ + void journal_insert(const u_int16_t after_lfid, // Insert files after this lfid + const u_int16_t num_files = 1, // Number of files to insert + const bool adjust_lids = true) // Adjust lids following inserted files + { + if (num_files == 0) return; + _num_used_files += num_files; + const u_int16_t num_jfiles_before_append = _map.size(); + lpmap_citr i = _map.find(after_lfid); + if (i == _map.end()) BOOST_FAIL("Unable to find lfid=" << after_lfid << " in map."); + const file_hdr fh_before = (*i).second; + + // Move overlapping lids (if req'd) + if (adjust_lids && after_lfid < num_jfiles_before_append - 1) + { + for (u_int16_t lfid = num_jfiles_before_append - 1; lfid > after_lfid; lfid--) + { + lpmap_citr itr = _map.find(lfid); + if (itr == _map.end()) BOOST_FAIL("Unable to find lfid=" << after_lfid << " in map."); + file_hdr fh = itr->second; + _map.erase(lfid); + fh._lfid += num_files; + if (lfid == _oldest_lfid) + _oldest_lfid += num_files; + _map.insert(lppair(fh._lfid, fh)); + } + } + + // Add new file headers + u_int16_t pfid = num_jfiles_before_append; + u_int16_t lfid = after_lfid + 1; + while (pfid < num_jfiles_before_append + num_files) + { + const u_int64_t frid = u_int64_t(random()); + const size_t fro = 0x200; + const file_hdr fh(RHM_JDAT_FILE_MAGIC, RHM_JDAT_VERSION, frid, pfid, lfid, fro, fh_before.get_owi(), + true); + _map.insert(lppair(lfid, fh)); + _last_pfid = pfid; + pfid++; + lfid++; + } + } + + /* + * Get the list of pfids in the map in order of lfid. The pfids are appended to the supplied vector. Only + * as many headers as are in the map are appended. + * NOTE: will clear any contents from supplied vector before appending pfid list. + */ + void get_pfid_list(vector<u_int16_t>& pfid_list) + { + pfid_list.clear(); + for (lpmap_citr i = _map.begin(); i != _map.end(); i++) + pfid_list.push_back(i->second._pfid); + } + + /* + * Get the list of lfids in the map. The lfids are appended to the supplied vector in the order they appear + * in the map (which is not necessarily the natural or sorted order). + * NOTE: will clear any contents from supplied vector before appending lfid list. + */ + void get_lfid_list(vector<u_int16_t>& lfid_list) + { + lfid_list.clear(); + lfid_list.assign(_map.size(), 0); + for (lpmap_citr i = _map.begin(); i != _map.end(); i++) + lfid_list[i->second._pfid] = i->first; + } + + /* + * Method check_analysis(): Used to check the result of the test jinf object analysis by comparing the pfid order + * array it produces against the internal map. + * + * ji: A ref to the jinf object under test. + */ + void check_analysis(jinf& ji) // jinf object under test after analyze() has been called + { + BOOST_CHECK_EQUAL(ji.get_first_pfid(), get_first_pfid()); + BOOST_CHECK_EQUAL(ji.get_last_pfid(), get_last_pfid()); + + jinf::pfid_list& pfidl = ji.get_pfid_list(); + const u_int16_t num_jfiles = _map.size(); + const bool all_used = _num_used_files == num_jfiles; + BOOST_CHECK_EQUAL(pfidl.size(), _num_used_files); + + const u_int16_t lfid_start = all_used ? _oldest_lfid : 0; + // Because a simulated failure would leave lfid dups in map and last_fid would not exist in map in this + // case, we must find lfid_stop via pfid instead. Search for pfid == num_files. + lpmap_citr itr = _map.begin(); + while (itr != _map.end() && itr->second._pfid != _num_used_files - 1) itr++; + if (itr == _map.end()) + BOOST_FAIL("check(): Unable to find pfid=" << (_num_used_files - 1) << " in map."); + const u_int16_t lfid_stop = itr->second._lfid; + + std::size_t fidl_index = 0; + for (u_int16_t lfid_cnt = lfid_start; lfid_cnt < lfid_stop; lfid_cnt++, fidl_index++) + { + const u_int16_t lfid = lfid_cnt % num_jfiles; + lpmap_citr itr = _map.find(lfid); + if (itr == _map.end()) + BOOST_FAIL("check(): Unable to find lfid=" << lfid << " in map."); + BOOST_CHECK_EQUAL(itr->second._pfid, pfidl[fidl_index]); + } + } + + /* + * Method get_pfid(): Look up a pfid from a known lfid. + */ + u_int16_t get_pfid(const u_int16_t lfid, const bool initial_owi = false) + { + switch (_map.count(lfid)) + { + case 1: + return _map.find(lfid)->second._pfid; + case 2: + for (lpmap_citr itr = _map.lower_bound(lfid); itr != _map.upper_bound(lfid); itr++) + { + if (itr->second.get_owi() != initial_owi) + return itr->second._pfid; + } + default:; + } + BOOST_FAIL("get_pfid(): lfid=" << lfid << " not found in map."); + return 0xffff; + } + + /* + * Method get_first_pfid(): Look up the first (oldest, or next-to-be-overwritten) pfid in the analysis sequence. + */ + u_int16_t get_first_pfid() + { + return get_pfid(_oldest_lfid); + } + + /* + * Method get_last_pfid(): Look up the last (newest, or most recently written) pfid in the analysis sequence. + */ + u_int16_t get_last_pfid() + { + u_int16_t flfid = 0; + if (_num_used_files == _map.size()) // journal full? + { + if (_oldest_lfid) + { + // if failed insert, cycle past duplicate lids + while (_map.count(_oldest_lfid) == 2) + _oldest_lfid++; + while (_map.find(_oldest_lfid) != _map.end() && _map.find(_oldest_lfid)->second.get_owi() == false) + _oldest_lfid++; + flfid = _oldest_lfid - 1; + } + else + flfid = _map.size() - 1; + } + else + flfid = _num_used_files - 1; + return get_pfid(flfid, true); + } + + /* + * Method write_journal(): Used to create the dummy journal files from the built-up map created by calling + * initial_journal_create() and optionally journal_append() one or more times. Since the jinf object reads the + * jinf file and the file headers only, the create object creates a dummy journal file containing only a file + * header (512 bytes each) and a single jinf file which contains the journal metadata required for recovery + * analysis. + */ + void write_journal(const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t fsize_sblks = JFSIZE_SBLKS) + { + create_jinf(ae, ae_max_jfiles); + u_int16_t pfid = 0; + for (lpmap_citr itr = _map.begin(); itr != _map.end(); itr++, pfid++) + { + if (itr->second._pfid == 0 && itr->second._magic == 0) // empty header, use pfid counter instead + create_journal_file(pfid, itr->second, _base_filename, fsize_sblks); + else + create_journal_file(itr->second._pfid, itr->second, _base_filename, fsize_sblks); + } + } + + /* + * Method destroy_journal(): Destroy the files created by create_journal() and reset the lfid_pfid_map test + * object. A new test may be started using the same lfid_pfid_map test object once this call has been made. + */ + void destroy_journal() + { + for (u_int16_t pfid = 0; pfid < _map.size(); pfid++) + { + string fn = create_journal_filename(pfid, _base_filename); + BOOST_WARN_MESSAGE(::unlink(fn.c_str()) == 0, "destroy_journal(): Failed to remove file " << fn); + } + clean_journal_info_file(_base_filename); + _map.clear(); + _num_used_files = 0; + _oldest_lfid = 0; + _last_pfid = 0; + } + + /* + * Method create_new_jinf(): This static call creates a default jinf file only. This is used to test the read + * constructor of a jinf test object which reads a jinf file at instantiation. + */ + static void create_new_jinf(const string jid, const string base_filename, const bool ae) + { + if (jdir::exists(test_dir)) + jdir::delete_dir(test_dir); + create_jinf(NUM_JFILES, ae, (ae ? 5 * NUM_JFILES : 0), jid, base_filename); + } + + /* + * Method clean_journal_info_file(): This static method deletes only a jinf file without harming any other + * journal file or its directory. This is used to clear those tests which rely only on the existence of a + * jinf file. + */ + static void clean_journal_info_file(const string base_filename) + { + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + BOOST_WARN_MESSAGE(::unlink(fn.str().c_str()) == 0, "clean_journal_info_file(): Failed to remove file " << + fn.str()); + } + + static string create_journal_filename(const u_int16_t pfid, const string base_filename) + { + stringstream fn; + fn << test_dir << "/" << base_filename << "."; + fn << setfill('0') << hex << setw(4) << pfid << "." << JRNL_DATA_EXTENSION; + return fn.str(); + } + + private: + static void init_fhdr(file_hdr& fh, + const u_int64_t frid, + const u_int16_t pfid, + const u_int16_t lfid, + const bool owi, + const bool no_enq = false) + { + fh._magic = RHM_JDAT_FILE_MAGIC; + fh._version = RHM_JDAT_VERSION; +#if defined(JRNL_BIG_ENDIAN) + fh._eflag = RHM_BENDIAN_FLAG; +#else + fh._eflag = RHM_LENDIAN_FLAG; +#endif + fh._uflag = owi ? rec_hdr::HDR_OVERWRITE_INDICATOR_MASK : 0; + fh._rid = frid; + fh._pfid = pfid; + fh._lfid = lfid; + fh._fro = no_enq ? 0 : 0x200; + timespec ts; + ::clock_gettime(CLOCK_REALTIME, &ts); + fh._ts_sec = ts.tv_sec; + fh._ts_nsec = ts.tv_nsec; + } + + void create_jinf(const bool ae, const u_int16_t ae_max_jfiles) + { + if (jdir::exists(test_dir)) + jdir::delete_dir(test_dir); + create_jinf(_map.size(), ae, ae_max_jfiles, _jid, _base_filename); + } + + static void create_jinf(u_int16_t num_files, const bool ae, const u_int16_t ae_max_jfiles, const string jid, + const string base_filename) + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + timespec ts; + ::clock_gettime(CLOCK_REALTIME, &ts); + jinf ji(jid, test_dir, base_filename, num_files, ae, ae_max_jfiles, JFSIZE_SBLKS, JRNL_WMGR_DEF_PAGE_SIZE, + JRNL_WMGR_DEF_PAGES, ts); + ji.write(); + } + + static void create_journal_file(const u_int16_t pfid, + const file_hdr& fh, + const string base_filename, + const u_int32_t fsize_sblks = JFSIZE_SBLKS, + const char fill_char = 0) + { + const std::string filename = create_journal_filename(pfid, base_filename); + ofstream of(filename.c_str(), ofstream::out | ofstream::trunc); + if (!of.good()) + BOOST_FAIL("Unable to open test journal file \"" << filename << "\" for writing."); + + write_file_header(filename, of, fh, fill_char); + write_file_body(of, fsize_sblks, fill_char); + + of.close(); + if (of.fail() || of.bad()) + BOOST_FAIL("Error closing test journal file \"" << filename << "\"."); + } + + static void write_file_header(const std::string& filename, + ofstream& of, + const file_hdr& fh, + const char fill_char) + { + // write file header + u_int32_t cnt = sizeof(file_hdr); + of.write((const char*)&fh, cnt); + if (of.fail() || of.bad()) + BOOST_FAIL("Error writing file header to test journal file \"" << filename << "\"."); + + // fill remaining sblk with fill char + while (cnt++ < JRNL_DBLK_SIZE * JRNL_SBLK_SIZE) + { + of.put(fill_char); + if (of.fail() || of.bad()) + BOOST_FAIL("Error writing filler to test journal file \"" << filename << "\"."); + } + } + + static void write_file_body(ofstream& of, const u_int32_t fsize_sblks, const char fill_char) + { + if (fsize_sblks > 1) + { + std::vector<char> sblk_buffer(JRNL_DBLK_SIZE * JRNL_SBLK_SIZE, fill_char); + u_int32_t fwritten_sblks = 0; // hdr + while (fwritten_sblks++ < fsize_sblks) + of.write(&sblk_buffer[0], JRNL_DBLK_SIZE * JRNL_SBLK_SIZE); + } + } +}; + +const string +get_test_name(const string& file, const string& test_name) +{ + cout << test_filename << "." << test_name << ": " << flush; + return file + "." + test_name; +} + +bool +check_iores(const string& ctxt, const iores ret, const iores exp_ret, test_dtok* dtp) +{ + if (ret != exp_ret) + { + delete dtp; + BOOST_FAIL(ctxt << ": Expected " << iores_str(exp_ret) << "; got " << iores_str(ret)); + } + return false; +} + +bool +handle_jcntl_response(const iores res, jcntl& jc, unsigned& aio_sleep_cnt, const std::string& ctxt, const iores exp_ret, + test_dtok* dtp) +{ + if (res == RHM_IORES_PAGE_AIOWAIT) + { + if (++aio_sleep_cnt <= MAX_AIO_SLEEPS) + { + jc.get_wr_events(0); // *** GEV2 + usleep(AIO_SLEEP_TIME); + } + else + return check_iores(ctxt, res, exp_ret, dtp); + } + else + return check_iores(ctxt, res, exp_ret, dtp); + return true; +} + +u_int64_t +enq_msg(jcntl& jc, + const u_int64_t rid, + const string& msg, + const bool transient, + const iores exp_ret = RHM_IORES_SUCCESS) +{ + ostringstream ctxt; + ctxt << "enq_msg(" << rid << ")"; + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_external_rid(true); + try + { + iores res = jc.enqueue_data_record(msg.c_str(), msg.size(), msg.size(), dtp, transient); + check_iores(ctxt.str(), res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +u_int64_t +enq_extern_msg(jcntl& jc, const u_int64_t rid, const std::size_t msg_size, const bool transient, + const iores exp_ret = RHM_IORES_SUCCESS) +{ + ostringstream ctxt; + ctxt << "enq_extern_msg(" << rid << ")"; + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_external_rid(true); + try + { + iores res = jc.enqueue_extern_data_record(msg_size, dtp, transient); + check_iores(ctxt.str(), res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +u_int64_t +enq_txn_msg(jcntl& jc, const u_int64_t rid, const string& msg, const string& xid, const bool transient, + const iores exp_ret = RHM_IORES_SUCCESS) +{ + ostringstream ctxt; + ctxt << "enq_txn_msg(" << rid << ")"; + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_external_rid(true); + try + { + iores res = jc.enqueue_txn_data_record(msg.c_str(), msg.size(), msg.size(), dtp, xid, + transient); + check_iores(ctxt.str(), res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +u_int64_t +enq_extern_txn_msg(jcntl& jc, const u_int64_t rid, const std::size_t msg_size, const string& xid, const bool transient, + const iores exp_ret = RHM_IORES_SUCCESS) +{ + ostringstream ctxt; + ctxt << "enq_extern_txn_msg(" << rid << ")"; + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_external_rid(true); + try + { + iores res = jc.enqueue_extern_txn_data_record(msg_size, dtp, xid, transient); + check_iores(ctxt.str(), res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +u_int64_t +deq_msg(jcntl& jc, const u_int64_t drid, const u_int64_t rid, const iores exp_ret = RHM_IORES_SUCCESS) +{ + ostringstream ctxt; + ctxt << "deq_msg(" << drid << ")"; + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_dequeue_rid(drid); + dtp->set_external_rid(true); + dtp->set_wstate(data_tok::ENQ); + try + { + iores res = jc.dequeue_data_record(dtp); + check_iores(ctxt.str(), res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +u_int64_t +deq_txn_msg(jcntl& jc, const u_int64_t drid, const u_int64_t rid, const string& xid, + const iores exp_ret = RHM_IORES_SUCCESS) +{ + ostringstream ctxt; + ctxt << "deq_txn_msg(" << drid << ")"; + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_dequeue_rid(drid); + dtp->set_external_rid(true); + dtp->set_wstate(data_tok::ENQ); + try + { + iores res = jc.dequeue_txn_data_record(dtp, xid); + check_iores(ctxt.str(), res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +u_int64_t +txn_abort(jcntl& jc, const u_int64_t rid, const string& xid, const iores exp_ret = RHM_IORES_SUCCESS) +{ + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_external_rid(true); + try + { + iores res = jc.txn_abort(dtp, xid); + check_iores("txn_abort", res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +u_int64_t +txn_commit(jcntl& jc, const u_int64_t rid, const string& xid, const iores exp_ret = RHM_IORES_SUCCESS) +{ + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_rid(rid); + dtp->set_external_rid(true); + try + { + iores res = jc.txn_commit(dtp, xid); + check_iores("txn_commit", res, exp_ret, dtp); + u_int64_t dtok_rid = dtp->rid(); + if (dtp->done()) delete dtp; + return dtok_rid; + } + catch (exception& e) { delete dtp; throw; } +} + +void +read_msg(jcntl& jc, string& msg, string& xid, bool& transient, bool& external, const iores exp_ret = RHM_IORES_SUCCESS) +{ + void* mp = 0; + std::size_t msize = 0; + void* xp = 0; + std::size_t xsize = 0; + test_dtok* dtp = new test_dtok; + BOOST_CHECK_MESSAGE(dtp != 0, "Data token allocation failed (dtp == 0)."); + dtp->set_wstate(data_tok::ENQ); + + unsigned aio_sleep_cnt = 0; + try + { + iores res = jc.read_data_record(&mp, msize, &xp, xsize, transient, external, dtp); + while (handle_jcntl_response(res, jc, aio_sleep_cnt, "read_msg", exp_ret, dtp)) + res = jc.read_data_record(&mp, msize, &xp, xsize, transient, external, dtp); + } + catch (exception& e) { delete dtp; throw; } + + if (mp) + msg.assign((char*)mp, msize); + if (xp) + { + xid.assign((char*)xp, xsize); + std::free(xp); + xp = 0; + } + else if (mp) + { + std::free(mp); + mp = 0; + } + delete dtp; +} + +/* + * Returns the number of messages of size msg_rec_size_dblks that will fit into an empty journal with or without + * corresponding dequeues (controlled by include_deq) without a threshold - ie until the journal is full. Assumes + * that dequeue records fit into one dblk. + */ +u_int32_t +num_msgs_to_full(const u_int16_t num_files, const u_int32_t file_size_dblks, const u_int32_t msg_rec_size_dblks, + bool include_deq) +{ + u_int32_t rec_size_dblks = msg_rec_size_dblks; + if (include_deq) + rec_size_dblks++; + return u_int32_t(::floor(1.0 * num_files * file_size_dblks / rec_size_dblks)); +} + +/* + * Returns the number of messages of size msg_rec_size_dblks that will fit into an empty journal before the enqueue + * threshold of JRNL_ENQ_THRESHOLD (%). + */ +u_int32_t +num_msgs_to_threshold(const u_int16_t num_files, const u_int32_t file_size_dblks, const u_int32_t msg_rec_size_dblks) +{ + return u_int32_t(::floor(1.0 * num_files * file_size_dblks * JRNL_ENQ_THRESHOLD / msg_rec_size_dblks / 100)); +} + +/* + * Returns the amount of space reserved in dblks (== num dequeues assuming dequeue size of 1 dblk) for the enqueue + * threshold of JRNL_ENQ_THRESHOLD (%). + */ +u_int32_t +num_dequeues_rem(const u_int16_t num_files, const u_int32_t file_size_dblks) +{ + /* + * Fraction of journal remaining after threshold is used --------------+ + * Total no. dblks in journal ------+ | + * | | + * +------------+------------+ +-----------------+---------------------+ + */ + return u_int32_t(::ceil(num_files * file_size_dblks * (1.0 - (1.0 * JRNL_ENQ_THRESHOLD / 100)))); +} + +string& +create_msg(string& s, const int msg_num, const int len) +{ + ostringstream oss; + oss << "MSG_" << setfill('0') << setw(6) << msg_num << "_"; + for (int i=12; i<=len; i++) + oss << (char)('0' + i%10); + s.assign(oss.str()); + return s; +} + +string& +create_xid(string& s, const int msg_num, const int len) +{ + ostringstream oss; + oss << "XID_" << setfill('0') << setw(6) << msg_num << "_"; + for (int i=11; i<len; i++) + oss << (char)('a' + i%26); + s.assign(oss.str()); + return s; +} + +long +get_seed() +{ + timespec ts; + if (::clock_gettime(CLOCK_REALTIME, &ts)) + BOOST_FAIL("Unable to read clock to generate seed."); + long tenths = ts.tv_nsec / 100000000; + return long(10 * ts.tv_sec + tenths); // time in tenths of a second +} diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_read.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_st_read.cpp new file mode 100644 index 0000000000..ff2c39e14c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_read.cpp @@ -0,0 +1,460 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" +#include <cmath> +#include <iostream> +#include "qpid/legacystore/jrnl/jcntl.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(journal_read) + +const string test_filename("_st_read"); + +#include "_st_helper_fns.h" + +// === Test suite === + +#ifndef LONG_TEST +/* + * ============================================== + * NORMAL TESTS + * This section contains normal "make check" tests + * for building/packaging. These are built when + * LONG_TEST is _not_ defined. + * ============================================== + */ + +QPID_AUTO_TEST_CASE(empty_read) +{ + string test_name = get_test_name(test_filename, "empty_read"); + try + { + string msg; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_read_dequeue_block) +{ + string test_name = get_test_name(test_filename, "enqueue_read_dequeue_block"); + try + { + string msg; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + jc.flush(); + for (int m=0; m<NUM_MSGS; m++) + { + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + for (int m=0; m<NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS); + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_read_dequeue_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_read_dequeue_interleaved"); + try + { + string msg; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<500*NUM_MSGS; m+=2) + { + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + jc.flush(); + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + deq_msg(jc, m, m+1); + jc.flush(); + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_recovered_read_dequeue) +{ + string test_name = get_test_name(test_filename, "enqueue_recovered_read_dequeue"); + try + { + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + } + { + string msg; + u_int64_t hrid; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(NUM_MSGS - 1)); + jc.recover_complete(); + for (int m=0; m<NUM_MSGS; m++) + { + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + for (int m=0; m<NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS); + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(multi_page_enqueue_recovered_read_dequeue_block) +{ + string test_name = get_test_name(test_filename, "multi_page_enqueue_recovered_read_dequeue_block"); + try + { + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(2*NUM_TEST_JFILES, false, 0, 10*TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS*125; m++) + enq_msg(jc, m, create_msg(msg, m, 16*MSG_SIZE), false); + } + { + string msg; + u_int64_t hrid; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.recover(2*NUM_TEST_JFILES, false, 0, 10*TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(NUM_MSGS*125 - 1)); + jc.recover_complete(); + for (int m=0; m<NUM_MSGS*125; m++) + { + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, 16*MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + for (int m=0; m<NUM_MSGS*125; m++) + deq_msg(jc, m, m+NUM_MSGS*125); + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_recover_read_recovered_read_dequeue_block) +{ + string test_name = get_test_name(test_filename, "enqueue_recover_read_recovered_read_dequeue_block"); + try + { + { + string msg; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + } + { + string msg; + u_int64_t hrid; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(NUM_MSGS - 1)); + for (int m=0; m<NUM_MSGS; m++) + { + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + { + string msg; + u_int64_t hrid; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.recover(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS, 0, hrid); + BOOST_CHECK_EQUAL(hrid, u_int64_t(NUM_MSGS - 1)); + for (int m=0; m<NUM_MSGS; m++) + { + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + jc.recover_complete(); + for (int m=0; m<NUM_MSGS; m++) + { + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + for (int m=0; m<NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS); + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(delayed_read) +{ + string test_name = get_test_name(test_filename, "delayed_read"); + try + { + string msg; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + unsigned m; + for (m=0; m<2*NUM_MSGS; m+=2) + { + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + deq_msg(jc, m, m+1); + } + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + jc.flush(); + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(msg, rmsg); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(cache_cycled_delayed_read) +{ + string test_name = get_test_name(test_filename, "cache_cycled_delayed_read"); + try + { + string msg; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + unsigned m; + unsigned read_buffer_size_dblks = JRNL_RMGR_PAGES * JRNL_RMGR_PAGE_SIZE * JRNL_SBLK_SIZE; + unsigned n = num_msgs_to_full(1, read_buffer_size_dblks, 16*MSG_REC_SIZE_DBLKS, true); + for (m=0; m<2*2*n + 20; m+=2) // fill read buffer twice + 10 msgs + { + enq_msg(jc, m, create_msg(msg, m, 16*MSG_SIZE), false); + deq_msg(jc, m, m+1); + } + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + jc.flush(); + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(msg, rmsg); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +#else +/* + * ============================================== + * LONG TESTS + * This section contains long tests and soak tests, + * and are run using target check-long (ie "make + * check-long"). These are built when LONG_TEST is + * defined. + * ============================================== + */ + +QPID_AUTO_TEST_CASE(multi_page_enqueue_read_dequeue_block) +{ + string test_name = get_test_name(test_filename, "multi_page_enqueue_read_dequeue_block"); + try + { + string msg; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(2*NUM_TEST_JFILES, false, 0, 10*TEST_JFSIZE_SBLKS); + for (int i=0; i<10; i++) + { + for (int m=0; m<NUM_MSGS*125; m++) + enq_msg(jc, m, create_msg(msg, m, 16*MSG_SIZE), false); + jc.flush(); + for (int m=0; m<NUM_MSGS*125; m++) + { + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, 16*MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(xid.size(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + for (int m=0; m<NUM_MSGS*125; m++) + deq_msg(jc, m, m+NUM_MSGS*125); + read_msg(jc, rmsg, xid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(increasing_interval_delayed_read) +{ + string test_name = get_test_name(test_filename, "increasing_interval_delayed_read"); + try + { + string msg; + string rmsg; + string xid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + unsigned read_buffer_size_dblks = JRNL_RMGR_PAGES * JRNL_RMGR_PAGE_SIZE * JRNL_SBLK_SIZE; + unsigned n = num_msgs_to_full(1, read_buffer_size_dblks, MSG_REC_SIZE_DBLKS, true); + unsigned m = 0; + + // Validate read pipeline + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + jc.flush(); + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + deq_msg(jc, m, m+1); + m += 2; + + // repeat the following multiple times... + for (int i=0; i<10; i++) + { + // Invalidate read pipeline with large write + unsigned t = m + (i*n) + 25; + for (; m<t; m+=2) + { + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + deq_msg(jc, m, m+1); + } + + // Revalidate read pipeline + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + jc.flush(); + read_msg(jc, rmsg, xid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(msg, rmsg); + deq_msg(jc, m, m+1); + m += 2; + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +#endif + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_st_read_txn.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_st_read_txn.cpp new file mode 100644 index 0000000000..621777d8d3 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_st_read_txn.cpp @@ -0,0 +1,353 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" +#include <cmath> +#include <iostream> +#include "qpid/legacystore/jrnl/jcntl.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(journal_read_txn) + +const string test_filename("_st_read_txn"); + +#include "_st_helper_fns.h" + +// === Test suite === + +QPID_AUTO_TEST_CASE(tx_enqueue_commit_block) +{ + string test_name = get_test_name(test_filename, "tx_enqueue_commit_block"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + create_xid(xid, 0, XID_SIZE); + for (int m=0; m<NUM_MSGS; m++) + enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_commit(jc, NUM_MSGS, xid); + jc.flush(); + for (int m=0; m<NUM_MSGS; m++) + { + read_msg(jc, rmsg, rxid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(rxid, xid); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(tx_enqueue_commit_interleaved) +{ + string test_name = get_test_name(test_filename, "tx_enqueue_commit_interleaved"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + create_xid(xid, 2*m, XID_SIZE); + enq_txn_msg(jc, 2*m, create_msg(msg, 2*m, MSG_SIZE), xid, false); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_commit(jc, 2*m+1, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, 2*m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(rxid, xid); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(tx_enqueue_abort_block) +{ + string test_name = get_test_name(test_filename, "tx_enqueue_abort_block"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + create_xid(xid, 1, XID_SIZE); + for (int m=0; m<NUM_MSGS; m++) + enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_abort(jc, NUM_MSGS, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(tx_enqueue_abort_interleaved) +{ + string test_name = get_test_name(test_filename, "tx_enqueue_abort_interleaved"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + create_xid(xid, 2*m, XID_SIZE); + enq_txn_msg(jc, 2*m, create_msg(msg, 2*m, MSG_SIZE), xid, false); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_abort(jc, 2*m+1, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(tx_enqueue_commit_dequeue_block) +{ + string test_name = get_test_name(test_filename, "tx_enqueue_commit_dequeue_block"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + create_xid(xid, 2, XID_SIZE); + for (int m=0; m<NUM_MSGS; m++) + enq_txn_msg(jc, m, create_msg(msg, m, MSG_SIZE), xid, false); + txn_commit(jc, NUM_MSGS, xid); + for (int m=0; m<NUM_MSGS; m++) + deq_msg(jc, m, m+NUM_MSGS+1); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(tx_enqueue_commit_dequeue_interleaved) +{ + string test_name = get_test_name(test_filename, "tx_enqueue_commit_dequeue_interleaved"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + create_xid(xid, 3*m, XID_SIZE); + enq_txn_msg(jc, 3*m, create_msg(msg, m, MSG_SIZE), xid, false); + txn_commit(jc, 3*m+1, xid); + deq_msg(jc, 3*m, 3*m+2); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_tx_dequeue_commit_block) +{ + string test_name = get_test_name(test_filename, "enqueue_tx_dequeue_commit_block"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + create_xid(xid, 3, XID_SIZE); + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + for (int m=0; m<NUM_MSGS; m++) + deq_txn_msg(jc, m, m+NUM_MSGS, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_commit(jc, 2*NUM_MSGS, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_tx_dequeue_commit_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_tx_dequeue_commit_interleaved"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + enq_msg(jc, 3*m, create_msg(msg, 3*m, MSG_SIZE), false); + create_xid(xid, 3*m, XID_SIZE); + deq_txn_msg(jc, 3*m, 3*m+1, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_commit(jc, 3*m+2, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_tx_dequeue_abort_block) +{ + string test_name = get_test_name(test_filename, "enqueue_tx_dequeue_abort_block"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + create_xid(xid, 4, XID_SIZE); + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + enq_msg(jc, m, create_msg(msg, m, MSG_SIZE), false); + for (int m=0; m<NUM_MSGS; m++) + deq_txn_msg(jc, m, m+NUM_MSGS, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_abort(jc, 2*NUM_MSGS, xid); + jc.flush(); + for (int m=0; m<NUM_MSGS; m++) + { + read_msg(jc, rmsg, rxid, transientFlag, externalFlag); + BOOST_CHECK_EQUAL(create_msg(msg, m, MSG_SIZE), rmsg); + BOOST_CHECK_EQUAL(rxid.length(), std::size_t(0)); + BOOST_CHECK_EQUAL(transientFlag, false); + BOOST_CHECK_EQUAL(externalFlag, false); + } + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enqueue_tx_dequeue_abort_interleaved) +{ + string test_name = get_test_name(test_filename, "enqueue_tx_dequeue_abort_interleaved"); + try + { + string msg; + string xid; + string rmsg; + string rxid; + bool transientFlag; + bool externalFlag; + + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + jc.initialize(NUM_TEST_JFILES, false, 0, TEST_JFSIZE_SBLKS); + for (int m=0; m<NUM_MSGS; m++) + { + enq_msg(jc, 3*m, create_msg(msg, 3*m, MSG_SIZE), false); + create_xid(xid, 3*m, XID_SIZE); + deq_txn_msg(jc, 3*m, 3*m+1, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_TXPENDING); + txn_abort(jc, 3*m+2, xid); + jc.flush(); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag); + read_msg(jc, rmsg, rxid, transientFlag, externalFlag, RHM_IORES_EMPTY); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_enq_map.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_enq_map.cpp new file mode 100644 index 0000000000..f05dcb824c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_enq_map.cpp @@ -0,0 +1,320 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <iostream> +#include "qpid/legacystore/jrnl/enq_map.h" +#include "qpid/legacystore/jrnl/jerrno.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(enq_map_suite) + +const string test_filename("_ut_enq_map"); + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + enq_map e1; + BOOST_CHECK(e1.empty()); + BOOST_CHECK_EQUAL(e1.size(), u_int32_t(0)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(insert_get) +{ + cout << test_filename << ".insert_get: " << flush; + u_int16_t pfid; + u_int64_t rid; + u_int16_t pfid_start = 0x2000U; + u_int64_t rid_begin = 0xffffffff00000000ULL; + u_int64_t rid_end = 0xffffffff00000200ULL; + + // insert with no dups + u_int64_t rid_incr_1 = 4ULL; + enq_map e2; + e2.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1); + for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++) + BOOST_CHECK_EQUAL(e2.insert_pfid(rid, pfid), enq_map::EMAP_OK); + BOOST_CHECK(!e2.empty()); + BOOST_CHECK_EQUAL(e2.size(), u_int32_t(128)); + + // get + u_int64_t rid_incr_2 = 6ULL; + for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_2) + { + BOOST_CHECK_EQUAL(e2.is_enqueued(rid), (rid%rid_incr_1 ? false : true)); + u_int16_t exp_pfid = pfid_start + (u_int16_t)((rid - rid_begin)/rid_incr_1); + int16_t ret_fid = e2.get_pfid(rid); + if (ret_fid < enq_map::EMAP_OK) // fail + { + BOOST_CHECK_EQUAL(ret_fid, enq_map::EMAP_RID_NOT_FOUND); + BOOST_CHECK(rid%rid_incr_1); + } + else + { + BOOST_CHECK_EQUAL(ret_fid, exp_pfid); + BOOST_CHECK(rid%rid_incr_1 == 0); + } + if ((rid + rid_incr_2)%(8 * rid_incr_2) == 0) + pfid++; + } + + // insert with dups + for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_2, pfid++) + { + int16_t res = e2.insert_pfid(rid, pfid); + if (res < enq_map::EMAP_OK) // fail + { + BOOST_CHECK_EQUAL(res, enq_map::EMAP_DUP_RID); + BOOST_CHECK(rid%rid_incr_1 == 0); + } + else + BOOST_CHECK(rid%rid_incr_1); + } + BOOST_CHECK_EQUAL(e2.size(), u_int32_t(171)); + e2.clear(); + BOOST_CHECK(e2.empty()); + BOOST_CHECK_EQUAL(e2.size(), u_int32_t(0)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(get_remove) +{ + cout << test_filename << ".get_remove: " << flush; + u_int16_t pfid; + u_int64_t rid; + u_int16_t pfid_start = 0x3000U; + u_int64_t rid_begin = 0xeeeeeeee00000000ULL; + u_int64_t rid_end = 0xeeeeeeee00000200ULL; + + u_int64_t rid_incr_1 = 4ULL; + u_int64_t num_incr_1 = (rid_end - rid_begin)/rid_incr_1; + enq_map e3; + e3.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1); + for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++) + BOOST_CHECK_EQUAL(e3.insert_pfid(rid, pfid), enq_map::EMAP_OK); + BOOST_CHECK_EQUAL(e3.size(), num_incr_1); + + u_int64_t rid_incr_2 = 6ULL; + for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_2, pfid++) + { + u_int16_t exp_pfid = pfid_start + (u_int16_t)((rid - rid_begin)/rid_incr_1); + int16_t ret_fid = e3.get_remove_pfid(rid); + if (ret_fid < enq_map::EMAP_OK) // fail + { + BOOST_CHECK_EQUAL(ret_fid, enq_map::EMAP_RID_NOT_FOUND); + BOOST_CHECK(rid%rid_incr_1); + } + else + { + BOOST_CHECK_EQUAL(ret_fid, exp_pfid); + BOOST_CHECK(rid%rid_incr_1 == 0); + } + } + BOOST_CHECK_EQUAL(e3.size(), u_int32_t(85)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(lock) +{ + cout << test_filename << ".lock: " << flush; + u_int16_t pfid; + u_int64_t rid; + u_int16_t pfid_start = 0x4000U; + u_int64_t rid_begin = 0xdddddddd00000000ULL; + u_int64_t rid_end = 0xdddddddd00000200ULL; + + // insert, every second entry is locked + u_int64_t rid_incr_1 = 4ULL; + u_int64_t num_incr_1 = (rid_end - rid_begin)/rid_incr_1; + bool locked = false; + enq_map e4; + e4.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1); + for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++) + { + BOOST_CHECK_EQUAL(e4.insert_pfid(rid, pfid, locked), enq_map::EMAP_OK); + locked = !locked; + } + BOOST_CHECK_EQUAL(e4.size(), num_incr_1); + + // unlock and lock non-existent rids + int16_t res = e4.lock(1ULL); + if (res < enq_map::EMAP_OK) + BOOST_CHECK_EQUAL(res, enq_map::EMAP_RID_NOT_FOUND); + else + BOOST_ERROR("Failed to detect locking non-existent rid."); + res = e4.unlock(2ULL); + if (res < enq_map::EMAP_OK) + BOOST_CHECK_EQUAL(res, enq_map::EMAP_RID_NOT_FOUND); + else + BOOST_ERROR("Failed to detect unlocking non-existent rid."); + + // get / unlock + for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_1) + { + int16_t fid = e4.get_pfid(rid); + if (fid < enq_map::EMAP_OK) // fail + { + BOOST_CHECK_EQUAL(fid, enq_map::EMAP_LOCKED); + BOOST_CHECK(rid%(2*rid_incr_1)); + // unlock, read, then relock + BOOST_CHECK_EQUAL(e4.unlock(rid), enq_map::EMAP_OK); + BOOST_CHECK(e4.get_pfid(rid) >= enq_map::EMAP_OK); + BOOST_CHECK_EQUAL(e4.lock(rid), enq_map::EMAP_OK); + fid = e4.get_pfid(rid); + if (fid < enq_map::EMAP_OK) // fail + BOOST_CHECK_EQUAL(fid, enq_map::EMAP_LOCKED); + else + BOOST_ERROR("Failed to prevent getting locked record"); + } + } + + // remove all; if locked, use with txn_flag true; should ignore all locked records + for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_1) + BOOST_CHECK(e4.get_remove_pfid(rid, true) >= enq_map::EMAP_OK); + BOOST_CHECK(e4.empty()); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(lists) +{ + cout << test_filename << ".lists: " << flush; + u_int16_t pfid; + u_int64_t rid; + u_int16_t pfid_start = 0x5000UL; + u_int64_t rid_begin = 0xdddddddd00000000ULL; + u_int64_t rid_end = 0xdddddddd00000200ULL; + + // insert, every second entry is locked + u_int64_t rid_incr_1 = 4ULL; + u_int64_t num_incr_1 = (rid_end - rid_begin)/rid_incr_1; + vector<u_int64_t> rid_list; + vector<u_int16_t> pfid_list; + enq_map e5; + e5.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1); + for (rid = rid_begin, pfid = pfid_start; rid < rid_end; rid += rid_incr_1, pfid++) + { + BOOST_CHECK_EQUAL(e5.insert_pfid(rid, pfid), enq_map::EMAP_OK); + rid_list.push_back(rid); + pfid_list.push_back(pfid); + } + BOOST_CHECK_EQUAL(e5.size(), num_incr_1); + BOOST_CHECK_EQUAL(rid_list.size(), num_incr_1); + BOOST_CHECK_EQUAL(pfid_list.size(), num_incr_1); + + vector<u_int64_t> ret_rid_list; + e5.rid_list(ret_rid_list); + BOOST_CHECK_EQUAL(ret_rid_list.size(), num_incr_1); + for (unsigned i=0; i<ret_rid_list.size(); i++) + BOOST_CHECK_EQUAL(rid_list[i], ret_rid_list[i]); + + vector<u_int16_t> ret_pfid_list; + e5.pfid_list(ret_pfid_list); + BOOST_CHECK_EQUAL(ret_pfid_list.size(), num_incr_1); + for (unsigned i=0; i<ret_pfid_list.size(); i++) + BOOST_CHECK_EQUAL(pfid_list[i], ret_pfid_list[i]); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enq_count) +{ + cout << test_filename << ".enq_count: " << flush; + + enq_map e6; + + // Check the allocation and cleanup as the file size is set both up and down + e6.set_num_jfiles(24); + e6.set_num_jfiles(0); + e6.set_num_jfiles(100); + e6.set_num_jfiles(4); + + // Add 100 enqueues to file 1, check that the counts match + for (u_int16_t pfid=0; pfid<4; pfid++) + BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(0)); + for (u_int64_t rid=0; rid<100; rid++) + BOOST_CHECK_EQUAL(e6.insert_pfid(rid, 1), enq_map::EMAP_OK); + for (u_int16_t pfid=0; pfid<4; pfid++) + { + if (pfid == 1) + BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(100)); + else + BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(0)); + } + + // Now remove 10 from file 1, check that the counts match + for (u_int64_t rid=0; rid<100; rid+=10) + //e6.Xget_remove_pfid(rid); + BOOST_CHECK(e6.get_remove_pfid(rid) >= enq_map::EMAP_OK); + for (u_int16_t pfid=0; pfid<4; pfid++) + { + if (pfid == 1) + BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(90)); + else + BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(0)); + } + + // Now resize the file up and make sure the count in file 1 still exists + e6.set_num_jfiles(8); + for (u_int16_t pfid=0; pfid<8; pfid++) + { + if (pfid == 1) + BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(90)); + else + BOOST_CHECK_EQUAL(e6.get_enq_cnt(pfid), u_int32_t(0)); + } + + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(stress) +{ + cout << test_filename << ".stress: " << flush; + u_int64_t rid; + u_int64_t rid_cnt; + u_int64_t rid_begin = 0xffffffff00000000ULL; + u_int64_t num_rid = 10; + + enq_map e7; + e7.set_num_jfiles(rid_begin + num_rid); + + // insert even rids with no dups + for (rid = rid_begin, rid_cnt = u_int64_t(0); rid_cnt < num_rid; rid += 2ULL, rid_cnt++) + BOOST_CHECK_EQUAL(e7.insert_pfid(rid, u_int16_t(0)), enq_map::EMAP_OK); + BOOST_CHECK_EQUAL(e7.size(), num_rid); + + // insert odd rids with no dups + for (rid = rid_begin + 1, rid_cnt = u_int64_t(0); rid_cnt < num_rid; rid += 2ULL, rid_cnt++) + BOOST_CHECK_EQUAL(e7.insert_pfid(rid, u_int16_t(0)), enq_map::EMAP_OK); + BOOST_CHECK_EQUAL(e7.size(), num_rid * 2); + + // remove even rids + for (rid = rid_begin, rid_cnt = u_int64_t(0); rid_cnt < num_rid; rid += 2ULL, rid_cnt++) + BOOST_CHECK(e7.get_remove_pfid(rid) >= enq_map::EMAP_OK); + BOOST_CHECK_EQUAL(e7.size(), num_rid); + + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_jdir.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jdir.cpp new file mode 100644 index 0000000000..b55d5ff8ef --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jdir.cpp @@ -0,0 +1,416 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <cerrno> +#include <cstring> +#include <dirent.h> +#include <fstream> +#include <iomanip> +#include <iostream> +#include "qpid/legacystore/jrnl/file_hdr.h" +#include "qpid/legacystore/jrnl/jcfg.h" +#include "qpid/legacystore/jrnl/jdir.h" +#include "qpid/legacystore/jrnl/jerrno.h" +#include "qpid/legacystore/jrnl/jexception.h" +#include <sys/stat.h> + +#define NUM_JFILES 4 +#define JFSIZE_SBLKS 128 + +#define ERRORSTR(e) std::strerror(e) << " (" << e << ")" +#define NUM_CLEAR_OPS 20 + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(jdir_suite) + +const string test_filename("_ut_jdir"); +const char* tdp = getenv("TMP_DATA_DIR"); +const string test_dir(tdp && strlen(tdp) > 0 ? string(tdp) + "/_ut_jdir" : "/var/tmp/_ut_jdir"); + +// === Helper functions === + +void create_file(const char* filename, mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) +{ + ofstream of(filename, ofstream::out | ofstream::trunc); + if (!of.good()) + BOOST_FAIL("Unable to open file " << filename << " for writing."); + of.write(filename, std::strlen(filename)); + of.close(); + ::chmod(filename, fmode); +} + +void create_file(const string filename, mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) +{ + create_file(filename.c_str(), fmode); +} + +void create_jdat_file(const char* dirname, const char* base_filename, u_int32_t fid, + u_int64_t first_rid) +{ + stringstream fn; + fn << dirname << "/" << base_filename << "."; + fn << setfill('0') << hex << setw(4) << fid << ".jdat"; + file_hdr fh(RHM_JDAT_FILE_MAGIC, RHM_JDAT_VERSION, 0, first_rid, fid, 0x200, true); + ofstream of(fn.str().c_str(), ofstream::out | ofstream::trunc); + if (!of.good()) + BOOST_FAIL("Unable to open journal data file " << fn.str() << " for writing."); + of.write((const char*)&fh, sizeof(file_hdr)); + of.close(); +} + +void create_jinf_file(const char* dirname, const char* base_filename) +{ + timespec ts; + ::clock_gettime(CLOCK_REALTIME, &ts); + jinf ji("test journal id", dirname, base_filename, NUM_JFILES, false, 0, JFSIZE_SBLKS, + JRNL_WMGR_DEF_PAGE_SIZE, JRNL_WMGR_DEF_PAGES, ts); + ji.write(); +} + +void create_jrnl_fileset(const char* dirname, const char* base_filename) +{ + create_jinf_file(dirname, base_filename); + for (u_int32_t fid = 0; fid < NUM_JFILES; fid++) + { + u_int64_t rid = 0x12340000 + (fid * 0x25); + create_jdat_file(dirname, base_filename, fid, rid); + } +} + +unsigned count_dir_contents(const char* dirname, bool incl_files, bool incl_dirs = true) +{ + struct dirent* entry; + struct stat s; + unsigned file_cnt = 0; + unsigned dir_cnt = 0; + unsigned other_cnt = 0; + DIR* dir = ::opendir(dirname); + if (!dir) + BOOST_FAIL("Unable to open directory " << dirname); + while ((entry = ::readdir(dir)) != NULL) + { + // Ignore . and .. + if (std::strcmp(entry->d_name, ".") != 0 && std::strcmp(entry->d_name, "..") != 0) + { + stringstream fn; + fn << dirname << "/" << entry->d_name; + if (::stat(fn.str().c_str(), &s)) + BOOST_FAIL("Unable to stat dir entry " << entry->d_name << "; err=" << + ERRORSTR(errno)); + if (S_ISREG(s.st_mode)) + file_cnt++; + else if (S_ISDIR(s.st_mode)) + dir_cnt++; + else + other_cnt++; + } + } + ::closedir(dir); + if (incl_files) + { + if (incl_dirs) + return file_cnt + dir_cnt; + return file_cnt; + } + else if (incl_dirs) + return dir_cnt; + return other_cnt; +} + +void check_dir_contents(const char* dirname, const char* base_filename, unsigned num_subdirs, + bool jrnl_present) +{ + if (jdir::is_dir(dirname)) + { + // Subdir count + BOOST_CHECK_EQUAL(count_dir_contents(dirname, false, true), num_subdirs); + + // Journal file count + unsigned num_jrnl_files = jrnl_present ? NUM_JFILES + 1 : 0; + BOOST_CHECK_EQUAL(count_dir_contents(dirname, true, false), num_jrnl_files); + + // Check journal files are present + if (jrnl_present) + try { jdir::verify_dir(dirname, base_filename); } + catch(const jexception& e) { BOOST_ERROR(e); } + for (unsigned subdir_num = 1; subdir_num <= num_subdirs; subdir_num++) + { + stringstream subdir_name; + subdir_name << dirname << "/_" << base_filename << ".bak."; + subdir_name << hex << setfill('0') << setw(4) << subdir_num; + try { jdir::verify_dir(subdir_name.str().c_str(), base_filename); } + catch(const jexception& e) { BOOST_ERROR(e); } + } + } + else + BOOST_ERROR(dirname << " is not a directory"); +} + +void check_dir_not_existing(const char* dirname) +{ + if (jdir::exists(dirname) && jdir::is_dir(dirname)) + jdir::delete_dir(dirname); + if (jdir::exists(dirname)) + BOOST_FAIL("Unable to remove directory " << dirname); +} + +void check_dir_not_existing(const string dirname) +{ + check_dir_not_existing(dirname.c_str()); +} + +// === Test suite === + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + string dir(test_dir + "/A/B/C/D/E/F"); + string bfn("test_base"); + jdir dir1(dir, bfn); + BOOST_CHECK(dir1.dirname().compare(dir) == 0); + BOOST_CHECK(dir1.base_filename().compare(bfn) == 0); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(create_delete_dir) +{ + cout << test_filename << ".create_delete_dir: " << flush; + // Use instance + string dir_A(test_dir + "/A"); + string dir_Ats(test_dir + "/A/"); // trailing '/' + check_dir_not_existing(test_dir + "/A"); + jdir dir1(dir_A, "test_base"); + dir1.create_dir(); + // check all combos of jdir::exists and jdir::is_dir() + BOOST_CHECK(jdir::exists(dir_A)); + BOOST_CHECK(jdir::exists(dir_Ats)); + BOOST_CHECK(jdir::exists(dir_A.c_str())); + BOOST_CHECK(jdir::exists(dir_Ats.c_str())); + BOOST_CHECK(jdir::is_dir(dir_A)); + BOOST_CHECK(jdir::is_dir(dir_Ats)); + BOOST_CHECK(jdir::is_dir(dir_Ats.c_str())); + BOOST_CHECK(jdir::is_dir(dir_Ats.c_str())); + // do it a second time when dir exists + dir1.create_dir(); + BOOST_CHECK(jdir::is_dir(dir_A)); + dir1.delete_dir(); + BOOST_CHECK(!jdir::exists(dir_A)); + + // Use static fn + check_dir_not_existing(test_dir + "/B"); + jdir::create_dir(test_dir + "/B"); + BOOST_CHECK(jdir::is_dir(test_dir + "/B")); + jdir::create_dir(test_dir + "/B"); + BOOST_CHECK(jdir::is_dir(test_dir + "/B")); + jdir::delete_dir(test_dir + "/B"); + BOOST_CHECK(!jdir::exists(test_dir + "/B")); + + // Non-empty dirs + check_dir_not_existing(test_dir + "/C"); + jdir::create_dir(test_dir + "/C"); + BOOST_CHECK(jdir::is_dir(test_dir + "/C")); + create_file(test_dir + "/C/test_file_1.txt"); // mode 644 (default) + create_file(test_dir + "/C/test_file_2.txt", S_IRWXU | S_IRWXG | S_IRWXO); // mode 777 + create_file(test_dir + "/C/test_file_3.txt", S_IRUSR | S_IRGRP | S_IROTH); // mode 444 (read-only) + create_file(test_dir + "/C/test_file_4.txt", 0); // mode 000 (no permissions) + BOOST_CHECK(jdir::is_dir(test_dir + "/C")); + jdir::create_dir(test_dir + "/C"); + BOOST_CHECK(jdir::is_dir(test_dir + "/C")); + jdir::delete_dir(test_dir + "/C"); + BOOST_CHECK(!jdir::exists(test_dir + "/C")); + + // Check non-existent dirs fail + check_dir_not_existing(test_dir + "/D"); + try + { + jdir::is_dir(test_dir + "/D"); + BOOST_ERROR("jdir::is_dir() failed to throw jexeption for non-existent directory."); + } + catch(const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_JDIR_STAT); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(create_delete_dir_recursive) +{ + cout << test_filename << ".create_delete_dir_recursive: " << flush; + // Use instances + check_dir_not_existing(test_dir + "/E"); + jdir dir1(test_dir + "/E/F/G/H", "test_base"); + dir1.create_dir(); + BOOST_CHECK(jdir::is_dir(test_dir + "/E/F/G/H")); + dir1.delete_dir(); + BOOST_CHECK(!jdir::exists(test_dir + "/E/F/G/H")); // only H deleted, E/F/G remain + BOOST_CHECK(jdir::exists(test_dir + "/E/F/G")); + jdir::delete_dir(test_dir + "/E"); // delete remaining dirs + BOOST_CHECK(!jdir::exists(test_dir + "/E")); + + check_dir_not_existing(test_dir + "/F"); + jdir dir2(test_dir + "/F/G/H/I/", "test_base"); // trailing '/' + dir2.create_dir(); + BOOST_CHECK(jdir::is_dir(test_dir + "/F/G/H/I/")); + dir2.delete_dir(); + BOOST_CHECK(!jdir::exists(test_dir + "/F/G/H/I/")); + BOOST_CHECK(jdir::exists(test_dir + "/F/G/H/")); + jdir::delete_dir(test_dir + "/F"); + BOOST_CHECK(!jdir::exists(test_dir + "/F")); + + check_dir_not_existing(test_dir + "/G"); + jdir dir3(test_dir + "/G/H//I//J", "test_base"); // extra '/' in path + dir3.create_dir(); + BOOST_CHECK(jdir::is_dir(test_dir + "/G/H//I//J")); + dir3.delete_dir(); + BOOST_CHECK(!jdir::exists(test_dir + "/G/H//I//J")); + BOOST_CHECK(jdir::exists(test_dir + "/G/H//I")); + jdir::delete_dir(test_dir + "/F"); + BOOST_CHECK(!jdir::exists(test_dir + "/F")); + + // Use static fn + check_dir_not_existing(test_dir + "/H"); + jdir::create_dir(test_dir + "/H/I/J/K"); + BOOST_CHECK(jdir::is_dir(test_dir + "/H/I/J/K")); + jdir::delete_dir(test_dir + "/H/I/J/K"); + BOOST_CHECK(!jdir::exists(test_dir + "/H/I/J/K")); // only J deleted, H/I/J remain + BOOST_CHECK(jdir::exists(test_dir + "/H/I/J")); + jdir::delete_dir(test_dir + "/H"); + BOOST_CHECK(!jdir::exists(test_dir + "/H")); + + check_dir_not_existing(test_dir + "/I"); + jdir::create_dir(test_dir + "/I/J/K/L/"); // trailing '/' + BOOST_CHECK(jdir::is_dir(test_dir + "/I/J/K/L/")); + jdir::delete_dir(test_dir + "/I/J/K/L/"); + BOOST_CHECK(!jdir::exists(test_dir + "/I/J/K/L/")); + BOOST_CHECK(jdir::exists(test_dir + "/I/J/K/")); + jdir::delete_dir(test_dir + "/I"); + BOOST_CHECK(!jdir::exists(test_dir + "/I")); + + check_dir_not_existing(test_dir + "//J"); + jdir::create_dir(test_dir + "//J//K//L//M"); // extra '/' in path + BOOST_CHECK(jdir::is_dir(test_dir + "//J//K//L//M")); + jdir::delete_dir(test_dir + "//J//K//L//M"); + BOOST_CHECK(!jdir::exists(test_dir + "//J//K//L//M")); + BOOST_CHECK(jdir::exists(test_dir + "//J//K//L")); + jdir::delete_dir(test_dir + "//J"); + BOOST_CHECK(!jdir::exists(test_dir + "//J")); + + // Non-empty dirs + check_dir_not_existing(test_dir + "/K"); + jdir::create_dir(test_dir + "/K/L/M1/N1"); + jdir::create_dir(test_dir + "/K/L/M1/N2"); + jdir::create_dir(test_dir + "/K/L/M1/N3"); + jdir::create_dir(test_dir + "/K/L/M1/N4"); + create_file(test_dir + "/K/L/M1/N4/test_file_1.txt"); // mode 644 (default) + create_file(test_dir + "/K/L/M1/N4/test_file_2.txt", S_IRWXU | S_IRWXG | S_IRWXO); // mode 777 + create_file(test_dir + "/K/L/M1/N4/test_file_3.txt", S_IRUSR | S_IRGRP | S_IROTH); // mode 444 + create_file(test_dir + "/K/L/M1/N4/test_file_4.txt", 0); // mode 000 (no permissions) + jdir::create_dir(test_dir + "/K/L/M2"); + jdir::create_dir(test_dir + "/K/L/M3/N5"); + jdir::create_dir(test_dir + "/K/L/M3/N6"); + BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N1")); + BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N2")); + BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N3")); + BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M1/N4")); + BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M2")); + BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M3/N5")); + BOOST_CHECK(jdir::is_dir(test_dir + "/K/L/M3/N6")); + jdir::delete_dir(test_dir + "/K"); + BOOST_CHECK(!jdir::exists(test_dir + "/K")); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(clear_verify_dir) +{ + cout << test_filename << ".clear_verify_dir: " << flush; + // Use instances + const char* jrnl_dir = "/var/tmp/test_dir_1"; + const char* bfn = "test_base"; + check_dir_not_existing(jrnl_dir); + jdir test_dir_1(jrnl_dir, bfn); + test_dir_1.create_dir(); + BOOST_CHECK(jdir::is_dir(jrnl_dir)); + // add journal files, check they exist, then clear them + unsigned cnt = 0; + while (cnt < NUM_CLEAR_OPS) + { + create_jrnl_fileset(jrnl_dir, bfn); + check_dir_contents(jrnl_dir, bfn, cnt, true); + test_dir_1.clear_dir(); + check_dir_contents(jrnl_dir, bfn, ++cnt, false); + } + // clean up + test_dir_1.delete_dir(); + BOOST_CHECK(!jdir::exists(jrnl_dir)); + + // Non-existent dir with auto-create true + jrnl_dir = "/var/tmp/test_dir_2"; + check_dir_not_existing(jrnl_dir); + jdir test_dir_2(jrnl_dir, bfn); + // clear dir + test_dir_2.clear_dir(); // create flag is true by default + check_dir_contents(jrnl_dir, bfn, 0, false); + // clear empty dir, should not create subdir + test_dir_2.clear_dir(); // create flag is true by default + check_dir_contents(jrnl_dir, bfn, 0, false); + // clean up + test_dir_2.delete_dir(); + BOOST_CHECK(!jdir::exists(jrnl_dir)); + + // non-existent dir with auto-create false + jrnl_dir = "/var/tmp/test_dir_3"; + check_dir_not_existing(jrnl_dir); + jdir test_dir_3(jrnl_dir, bfn); + try + { + test_dir_3.clear_dir(false); + BOOST_ERROR("jdir::clear_dir(flase) failed to throw jexeption for non-existent directory."); + } + catch(const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_JDIR_OPENDIR); + } + + // Use static fn + jrnl_dir = "/var/tmp/test_dir_4"; + check_dir_not_existing(jrnl_dir); + jdir::clear_dir(jrnl_dir, bfn); // should create dir if it does not exist + // add journal files, check they exist, then clear them + cnt = 0; + while (cnt < NUM_CLEAR_OPS) + { + create_jrnl_fileset(jrnl_dir, bfn); + check_dir_contents(jrnl_dir, bfn, cnt, true); + jdir::clear_dir(jrnl_dir, bfn); + check_dir_contents(jrnl_dir, bfn, ++cnt, false); + } + // clean up + jdir::delete_dir(jrnl_dir); + BOOST_CHECK(!jdir::exists(jrnl_dir)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_jerrno.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jerrno.cpp new file mode 100644 index 0000000000..1ae1138355 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jerrno.cpp @@ -0,0 +1,47 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <cstring> +#include <iostream> +#include "qpid/legacystore/jrnl/jerrno.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(jerrno_suite) +using namespace mrg::journal; + +const string test_filename("_ut_jerrno"); + +QPID_AUTO_TEST_CASE(jerrno_val) +{ + cout << test_filename << ".jerrno_val: " << flush; + const char* m = "JERR__MALLOC"; + string malloc_msg = string(jerrno::err_msg(jerrno::JERR__MALLOC)); + BOOST_CHECK(malloc_msg.substr(0, std::strlen(m)).compare(m) == 0); + BOOST_CHECK(std::strcmp(jerrno::err_msg(0), "<Unknown error code>") == 0); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_jexception.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jexception.cpp new file mode 100644 index 0000000000..8b9e876aa6 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jexception.cpp @@ -0,0 +1,346 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <cstring> +#include <iostream> +#include "qpid/legacystore/jrnl/jerrno.h" +#include "qpid/legacystore/jrnl/jexception.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(jexception_suite) + +const string test_filename("_ut_jexception"); + +// === Helper functions === + +void throw_exception(const jexception& e, std::size_t what_len, std::size_t ai_len, + std::size_t tc_len, std::size_t tf_len) +{ + try { throw e; } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(std::strlen(e.what()), what_len); + BOOST_CHECK_EQUAL(e.additional_info().size(), ai_len); + BOOST_CHECK_EQUAL(e.throwing_class().size(), tc_len); + BOOST_CHECK_EQUAL(e.throwing_fn().size(), tf_len); + } +} + +void throw_exception(const jexception& e, std::size_t what_len, std::size_t ai_len) +{ + throw_exception(e, what_len, ai_len, 0, 0); +} + +void throw_exception(const jexception& e, std::size_t what_len, std::size_t tc_len, + std::size_t tf_len) +{ + throw_exception(e, what_len, 0, tc_len, tf_len); +} + +// === Test suite === + +QPID_AUTO_TEST_CASE(constructor_1) +{ + cout << test_filename << ".constructor_1: " << flush; + try + { + jexception e1; + BOOST_CHECK_EQUAL(e1.err_code(), (u_int32_t)0); + BOOST_CHECK(e1.additional_info().size() == 0); + BOOST_CHECK(e1.throwing_class().size() == 0); + BOOST_CHECK(e1.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e1.what()) > 0); + throw e1; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), (u_int32_t)0); + BOOST_CHECK(e.additional_info().size() == 0); + BOOST_CHECK(e.throwing_class().size() == 0); + BOOST_CHECK(e.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_2) +{ + cout << test_filename << ".constructor_2: " << flush; + const u_int32_t err_code = 2; + try + { + jexception e2(err_code); + BOOST_CHECK_EQUAL(e2.err_code(), err_code); + BOOST_CHECK(e2.additional_info().size() == 0); + BOOST_CHECK(e2.throwing_class().size() == 0); + BOOST_CHECK(e2.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e2.what()) > 0); + throw e2; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), err_code); + BOOST_CHECK(e.additional_info().size() == 0); + BOOST_CHECK(e.throwing_class().size() == 0); + BOOST_CHECK(e.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_3a) +{ + cout << test_filename << ".constructor_3a: " << flush; + const char* err_msg = "exception3"; + try + { + jexception e3(err_msg); + BOOST_CHECK_EQUAL(e3.err_code(), (u_int32_t)0); + BOOST_CHECK(e3.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e3.throwing_class().size() == 0); + BOOST_CHECK(e3.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e3.what()) > 0); + throw e3; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), (u_int32_t)0); + BOOST_CHECK(e.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e.throwing_class().size() == 0); + BOOST_CHECK(e.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_3b) +{ + cout << test_filename << ".constructor_3b: " << flush; + const string err_msg("exception3"); + try + { + jexception e3(err_msg); + BOOST_CHECK_EQUAL(e3.err_code(), (u_int32_t)0); + BOOST_CHECK(e3.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e3.throwing_class().size() == 0); + BOOST_CHECK(e3.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e3.what()) > 0); + throw e3; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), (u_int32_t)0); + BOOST_CHECK(e.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e.throwing_class().size() == 0); + BOOST_CHECK(e.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_4a) +{ + cout << test_filename << ".constructor_4a: " << flush; + const u_int32_t err_code = 4; + const char* err_msg = "exception4"; + try + { + jexception e4(err_code, err_msg); + BOOST_CHECK_EQUAL(e4.err_code(), err_code); + BOOST_CHECK(e4.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e4.throwing_class().size() == 0); + BOOST_CHECK(e4.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e4.what()) > 0); + throw e4; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), err_code); + BOOST_CHECK(e.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e.throwing_class().size() == 0); + BOOST_CHECK(e.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_4b) +{ + cout << test_filename << ".constructor_4b: " << flush; + const u_int32_t err_code = 4; + const string err_msg("exception4"); + try + { + jexception e4(err_code, err_msg); + BOOST_CHECK_EQUAL(e4.err_code(), err_code); + BOOST_CHECK(e4.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e4.throwing_class().size() == 0); + BOOST_CHECK(e4.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e4.what()) > 0); + throw e4; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), err_code); + BOOST_CHECK(e.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e.throwing_class().size() == 0); + BOOST_CHECK(e.throwing_fn().size() == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_5a) +{ + cout << test_filename << ".constructor_5a: " << flush; + const u_int32_t err_code = 5; + const char* err_class = "class5"; + const char* err_fn = "fn5"; + try + { + jexception e5(err_code, err_class, err_fn); + BOOST_CHECK_EQUAL(e5.err_code(), err_code); + BOOST_CHECK(e5.additional_info().size() == 0); + BOOST_CHECK(e5.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e5.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e5.what()) > 0); + throw e5; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), err_code); + BOOST_CHECK(e.additional_info().size() == 0); + BOOST_CHECK(e.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_5b) +{ + cout << test_filename << ".constructor_5b: " << flush; + const u_int32_t err_code = 5; + const string err_class("class5"); + const string err_fn("fn5"); + try + { + jexception e5(err_code, err_class, err_fn); + BOOST_CHECK_EQUAL(e5.err_code(), err_code); + BOOST_CHECK(e5.additional_info().size() == 0); + BOOST_CHECK(e5.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e5.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e5.what()) > 0); + throw e5; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), err_code); + BOOST_CHECK(e.additional_info().size() == 0); + BOOST_CHECK(e.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_6a) +{ + cout << test_filename << ".constructor_6a: " << flush; + const u_int32_t err_code = 6; + const char* err_msg = "exception6"; + const char* err_class = "class6"; + const char* err_fn = "fn6"; + try + { + jexception e6(err_code, err_msg, err_class, err_fn); + BOOST_CHECK_EQUAL(e6.err_code(), err_code); + BOOST_CHECK(e6.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e6.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e6.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e6.what()) > 0); + throw e6; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), err_code); + BOOST_CHECK(e.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_6b) +{ + cout << test_filename << ".constructor_6b: " << flush; + const u_int32_t err_code = 6; + const string err_msg("exception6"); + const string err_class("class6"); + const string err_fn("fn6"); + try + { + jexception e6(err_code, err_msg, err_class, err_fn); + BOOST_CHECK_EQUAL(e6.err_code(), err_code); + BOOST_CHECK(e6.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e6.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e6.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e6.what()) > 0); + throw e6; + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), err_code); + BOOST_CHECK(e.additional_info().compare(err_msg) == 0); + BOOST_CHECK(e.throwing_class().compare(err_class) == 0); + BOOST_CHECK(e.throwing_fn().compare(err_fn) == 0); + BOOST_CHECK(std::strlen(e.what()) > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(msg_scope) +{ + cout << test_filename << ".msg_scope: " << flush; + try + { + // These will go out of scope as soon as jexception is thrown... + const string msg("Error message"); + const string cls("class"); + const string fn("function"); + throw jexception(100, msg, cls, fn); + } + catch (const jexception& e) + { + stringstream ss; + ss << e; + BOOST_CHECK(ss.str().size() > 0); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_jinf.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jinf.cpp new file mode 100644 index 0000000000..f239139306 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_jinf.cpp @@ -0,0 +1,402 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <cmath> +#include <iostream> +#include "qpid/legacystore/jrnl/jcntl.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(jinf_suite) + +const string test_filename("_ut_jinf"); + +#include "_st_helper_fns.h" + +timespec ts; + +QPID_AUTO_TEST_CASE(write_constructor) +{ + string test_name = get_test_name(test_filename, "write_constructor"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + jdir::create_dir(test_dir); // Check test dir exists; create it if not + ::clock_gettime(CLOCK_REALTIME, &ts); + jinf ji(jid, test_dir, base_filename, NUM_JFILES, false, 0, JFSIZE_SBLKS, JRNL_WMGR_DEF_PAGE_SIZE, JRNL_WMGR_DEF_PAGES, ts); + BOOST_CHECK_EQUAL(ji.jver(), RHM_JDAT_VERSION); + BOOST_CHECK(ji.jid().compare(jid) == 0); + BOOST_CHECK(ji.jdir().compare(test_dir) == 0); + BOOST_CHECK(ji.base_filename().compare(base_filename) == 0); + const timespec this_ts = ji.ts(); + BOOST_CHECK_EQUAL(this_ts.tv_sec, ts.tv_sec); + BOOST_CHECK_EQUAL(this_ts.tv_nsec, ts.tv_nsec); + BOOST_CHECK_EQUAL(ji.num_jfiles(), u_int16_t(NUM_JFILES)); + BOOST_CHECK_EQUAL(ji.is_ae(), false); + BOOST_CHECK_EQUAL(ji.ae_max_jfiles(), u_int16_t(0)); + BOOST_CHECK_EQUAL(ji.jfsize_sblks(), u_int32_t(JFSIZE_SBLKS)); + BOOST_CHECK_EQUAL(ji.sblk_size_dblks(), u_int16_t(JRNL_SBLK_SIZE)); + BOOST_CHECK_EQUAL(ji.dblk_size(), u_int32_t(JRNL_DBLK_SIZE)); + BOOST_CHECK_EQUAL(ji.wcache_pgsize_sblks(), u_int32_t(JRNL_WMGR_DEF_PAGE_SIZE)); + BOOST_CHECK_EQUAL(ji.wcache_num_pages(), u_int16_t(JRNL_WMGR_DEF_PAGES)); + BOOST_CHECK_EQUAL(ji.rcache_pgsize_sblks(), u_int32_t(JRNL_RMGR_PAGE_SIZE)); + BOOST_CHECK_EQUAL(ji.rcache_num_pages(), u_int16_t(JRNL_RMGR_PAGES)); + ji.write(); + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(read_constructor) +{ + string test_name = get_test_name(test_filename, "read_constructor"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map::create_new_jinf(jid, base_filename, false); + + stringstream fn; + fn << test_dir << "/" <<base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + BOOST_CHECK_EQUAL(ji.jver(), RHM_JDAT_VERSION); + BOOST_CHECK(ji.jid().compare(jid) == 0); + BOOST_CHECK(ji.jdir().compare(test_dir) == 0); + BOOST_CHECK(ji.base_filename().compare(base_filename) == 0); +// const timespec this_ts = ji.ts(); +// BOOST_CHECK_EQUAL(this_ts.tv_sec, ts.tv_sec); +// BOOST_CHECK_EQUAL(this_ts.tv_nsec, ts.tv_nsec); + BOOST_CHECK_EQUAL(ji.num_jfiles(), u_int16_t(NUM_JFILES)); + BOOST_CHECK_EQUAL(ji.is_ae(), false); + BOOST_CHECK_EQUAL(ji.ae_max_jfiles(), u_int16_t(0)); + BOOST_CHECK_EQUAL(ji.jfsize_sblks(), u_int32_t(JFSIZE_SBLKS)); + BOOST_CHECK_EQUAL(ji.sblk_size_dblks(), u_int16_t(JRNL_SBLK_SIZE)); + BOOST_CHECK_EQUAL(ji.dblk_size(), u_int32_t(JRNL_DBLK_SIZE)); + BOOST_CHECK_EQUAL(ji.wcache_pgsize_sblks(), u_int32_t(JRNL_WMGR_DEF_PAGE_SIZE)); + BOOST_CHECK_EQUAL(ji.wcache_num_pages(), u_int16_t(JRNL_WMGR_DEF_PAGES)); + BOOST_CHECK_EQUAL(ji.rcache_pgsize_sblks(), u_int32_t(JRNL_RMGR_PAGE_SIZE)); + BOOST_CHECK_EQUAL(ji.rcache_num_pages(), u_int16_t(JRNL_RMGR_PAGES)); + + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(set_functions) +{ + string test_name = get_test_name(test_filename, "set_functions"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map::create_new_jinf(jid, base_filename, false); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + + ji.set_jdir("abc123"); + BOOST_CHECK(ji.jdir().compare("abc123") == 0); + ji.set_jdir(test_dir); + BOOST_CHECK(ji.jdir().compare(test_dir) == 0); + ji.incr_num_jfiles(); + BOOST_CHECK_EQUAL(ji.num_jfiles(), u_int16_t(NUM_JFILES+1)); + ji.incr_num_jfiles(); + BOOST_CHECK_EQUAL(ji.num_jfiles(), u_int16_t(NUM_JFILES+2)); + + lfid_pfid_map::clean_journal_info_file(test_dir); + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(validate) +{ + string test_name = get_test_name(test_filename, "validate"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map::create_new_jinf(jid, base_filename, false); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), true); + // TODO: Check validation picks up conflict, but need to be friend to jinf to do it + + lfid_pfid_map::clean_journal_info_file(test_dir); + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_empty_journal) +{ + string test_name = get_test_name(test_filename, "analyze_empty_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + jdir::create_dir(test_dir); // Check test dir exists; create it if not + + lfid_pfid_map m(jid, base_filename); + m.journal_create(NUM_JFILES, 0, 0); + m.write_journal(false, 0); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + try { ji.analyze(); } + catch (const jexception& e) + { + if (e.err_code() != jerrno::JERR_JINF_JDATEMPTY) + BOOST_ERROR("Failed to throw expected exception jerrno::JERR_JINF_JDATEMPTY"); + } + + m.destroy_journal(); + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_part_full_journal) +{ + string test_name = get_test_name(test_filename, "analyze_part_full_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + for (u_int16_t num_files = 1; num_files < NUM_JFILES; num_files++) + { + m.journal_create(NUM_JFILES, num_files, 0); + m.write_journal(false, 0); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + ji.analyze(); + m.check_analysis(ji); + + m.destroy_journal(); + } + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_full_journal) +{ + string test_name = get_test_name(test_filename, "analyze_full_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + for (u_int16_t file_num = 0; file_num < NUM_JFILES; file_num++) + { + m.journal_create(NUM_JFILES, NUM_JFILES, file_num); + m.write_journal(false, 0); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + ji.analyze(); + m.check_analysis(ji); + + m.destroy_journal(); + } + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_single_appended_journal) +{ + string test_name = get_test_name(test_filename, "analyze_single_appended_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + for (u_int16_t oldest_lid = 0; oldest_lid < NUM_JFILES; oldest_lid++) + for (u_int16_t after_lid = 0; after_lid < NUM_JFILES; after_lid++) + for (u_int16_t num_files = 1; num_files <= 5; num_files++) + { + m.journal_create(NUM_JFILES, NUM_JFILES, oldest_lid); + m.journal_insert(after_lid, num_files); + m.write_journal(true, 16); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + ji.analyze(); + m.check_analysis(ji); + + m.destroy_journal(); + } + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_multi_appended_journal) +{ + string test_name = get_test_name(test_filename, "analyze_multi_appended_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + ::srand48(1); + + for (u_int16_t num_appends = 1; num_appends <= 2*NUM_JFILES; num_appends++) + { + const u_int16_t oldest_lid = u_int16_t(NUM_JFILES * ::drand48()); + m.journal_create(NUM_JFILES, NUM_JFILES, oldest_lid); + for (u_int16_t a = 0; a < num_appends; a++) + { + const u_int16_t num_files = u_int16_t(1 + (NUM_JFILES * ::drand48())); + const u_int16_t after_lid = u_int16_t(m.size() * ::drand48()); + m.journal_insert(after_lid, num_files); + } + m.write_journal(true, 24); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + ji.analyze(); + m.check_analysis(ji); + + m.destroy_journal(); + } + + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_multi_appended_then_failed_journal) +{ + string test_name = get_test_name(test_filename, "analyze_multi_appended_then_failed_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + ::srand48(1); + + // As this test relies on repeatable but random sequences, use many iterations for coverage + for (int c = 1; c <= 100; c++) + { + for (u_int16_t num_appends = 1; num_appends <= 2*NUM_JFILES; num_appends++) + { + u_int16_t oldest_lid = u_int16_t(NUM_JFILES * ::drand48()); + m.journal_create(NUM_JFILES, NUM_JFILES, oldest_lid); + for (u_int16_t a = 0; a < num_appends-1; a++) + { + const u_int16_t num_files = u_int16_t(1 + (NUM_JFILES * ::drand48())); + const u_int16_t after_lid = u_int16_t(m.size() * ::drand48()); + m.journal_insert(after_lid, num_files); + if (after_lid < oldest_lid) + oldest_lid += num_files; + } + const u_int16_t num_files = u_int16_t(1 + (NUM_JFILES * ::drand48())); + const u_int16_t after_lid = oldest_lid == 0 ? m.size() - 1 : oldest_lid - 1; + m.journal_insert(after_lid, num_files, false); + m.write_journal(true, 32); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + ji.analyze(); + m.check_analysis(ji); + + m.destroy_journal(); + } + } + + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_inconsistent_jdat_file_size_in_journal) +{ + string test_name = get_test_name(test_filename, "analyze_inconsistent_jdat_file_size_in_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + ::srand48(1); + + for (u_int16_t pfid = 1; pfid < NUM_JFILES; pfid++) + { + m.journal_create(NUM_JFILES, NUM_JFILES, 0); + m.write_journal(false, 0); + + const std::string filename = m.create_journal_filename(pfid, base_filename); + std::ofstream of(filename.c_str(), ofstream::out | ofstream::app); + if (!of.good()) + BOOST_FAIL("Unable to open test journal file \"" << filename << "\" for writing."); + std::size_t expand_size = std::size_t(10 * JRNL_DBLK_SIZE * JRNL_SBLK_SIZE * ::drand48()); + std::vector<char> sblk_buffer(expand_size, 0); + of.write(&sblk_buffer[0], expand_size); + of.close(); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + try + { + ji.analyze(); + BOOST_FAIL("Failed to detect irregular journal file size in file \"" << filename << "\""); + } + catch (const jexception& e) {} // ignore - expected + + m.destroy_journal(); + } + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_owi_in_non_ae_journal) +{ + string test_name = get_test_name(test_filename, "analyze_owi_in_non_ae_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + for (u_int16_t oldest_file = 1; oldest_file < NUM_DEFAULT_JFILES-1; oldest_file++) + { + for (u_int16_t bad_owi_file = oldest_file + 1; bad_owi_file < NUM_DEFAULT_JFILES; bad_owi_file++) + { + m.journal_create(NUM_DEFAULT_JFILES, NUM_DEFAULT_JFILES, oldest_file, bad_owi_file); + m.write_journal(false, 0); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + try + { + ji.analyze(); + BOOST_FAIL("Failed to detect irregular OWI flag in non-ae journal file \"" << fn.str() << "\""); + } + catch (const jexception& e) {} // ignore - expected + + m.destroy_journal(); + } + } + cout << "done" << endl; +} + +QPID_AUTO_TEST_CASE(analyze_owi_in_ae_min_size_journal) +{ + string test_name = get_test_name(test_filename, "analyze_owi_in_ae_min_size_journal"); + const string jid = test_name + "_jid"; + const string base_filename = test_name + "_bfn"; + lfid_pfid_map m(jid, base_filename); + for (u_int16_t oldest_file = 1; oldest_file < NUM_JFILES-1; oldest_file++) + { + for (u_int16_t bad_owi_file = oldest_file + 1; bad_owi_file < NUM_JFILES; bad_owi_file++) + { + m.journal_create(NUM_JFILES, NUM_JFILES, oldest_file, bad_owi_file); + m.write_journal(true, 16); + + stringstream fn; + fn << test_dir << "/" << base_filename << "." << JRNL_INFO_EXTENSION; + jinf ji(fn.str(), false); + try + { + ji.analyze(); + BOOST_FAIL("Failed to detect irregular OWI flag in min-sized ae journal file \"" << fn.str() << "\""); + } + catch (const jexception& e) {} // ignore - expected + + m.destroy_journal(); + } + } + cout << "done" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_lpmgr.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_lpmgr.cpp new file mode 100644 index 0000000000..2dc20ffa7c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_lpmgr.cpp @@ -0,0 +1,886 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" +#include <cmath> +#include <iostream> +#include "qpid/legacystore/jrnl/jcntl.h" +#include "qpid/legacystore/jrnl/lpmgr.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(arr_cnt_suite) + +const string test_filename("_ut_lpmgr"); + +#include "_st_helper_fns.h" + +// === Helper functions and definitions === + +typedef vector<u_int16_t> flist; +typedef flist::const_iterator flist_citr; + +class lpmgr_test_helper +{ + lpmgr_test_helper() {} + virtual ~lpmgr_test_helper() {} + +public: + static void check_pfids_lfids(const lpmgr& lm, const u_int16_t pfids[], const u_int16_t lfids[], + const size_t pfid_lfid_size) + { + vector<u_int16_t> res; + lm.get_pfid_list(res); + vectors_equal(lm, pfids, pfid_lfid_size, res, true); + lm.get_lfid_list(res); + vectors_equal(lm, lfids, pfid_lfid_size, res, false); + } + + static void check_pfids_lfids(const lpmgr& lm, const flist& pfids, const flist lfids) + { + vector<u_int16_t> res; + lm.get_pfid_list(res); + vectors_equal(lm, pfids, res, true); + lm.get_lfid_list(res); + vectors_equal(lm, lfids, res, false); + } + + static void check_linear_pfids_lfids(const lpmgr& lm, const size_t pfid_lfid_size) + { + vector<u_int16_t> res; + lm.get_pfid_list(res); + linear_vectors_equal(lm, pfid_lfid_size, res, true); + lm.get_lfid_list(res); + linear_vectors_equal(lm, pfid_lfid_size, res, false); + } + + static void rcvdat_init(rcvdat& rd, const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, + const u_int16_t pfids[]) + { + rd.reset(num_jfiles, ae, ae_max_jfiles); + load_vector(pfids, num_jfiles, rd._fid_list); + rd._jempty = false; + rd._lfid = pfids[num_jfiles - 1]; + rd._eo = 100 * JRNL_DBLK_SIZE * JRNL_SBLK_SIZE; + } + + static void rcvdat_init(rcvdat& rd, const flist& pfidl, const bool ae, const u_int16_t ae_max_jfiles) + { + const u_int16_t num_jfiles = pfidl.size(); + rd.reset(num_jfiles, ae, ae_max_jfiles); + load_vector(pfidl, rd._fid_list); + rd._jempty = false; + rd._lfid = pfidl[num_jfiles - 1]; + rd._eo = 100 * JRNL_DBLK_SIZE * JRNL_SBLK_SIZE; + } + + static void initialize(lpmgr& lm, test_jrnl& jc, const u_int16_t num_jfiles, const bool ae, + const u_int16_t ae_max_jfiles) + { + lm.initialize(num_jfiles, ae, ae_max_jfiles, &jc, &jc.new_fcntl); + BOOST_CHECK_EQUAL(lm.is_init(), true); + BOOST_CHECK_EQUAL(lm.is_ae(), ae); + BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles); + BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles); + if (num_jfiles) + check_linear_pfids_lfids(lm, num_jfiles); + else + BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0); + } + + // version which sets up the lfid_pfid_map for later manipulation by insert tests + static void initialize(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const u_int16_t num_jfiles, const bool ae, + const u_int16_t ae_max_jfiles) + { + lfm.journal_create(num_jfiles, num_jfiles); + initialize(lm, jc, num_jfiles, ae, ae_max_jfiles); + } + + static void prepare_recover(lfid_pfid_map& lfm, const u_int16_t size) + { + if (size < 4) BOOST_FAIL("prepare_recover(): size parameter (" << size << ") too small."); + lfm.journal_create(4, 4); // initial journal of size 4 + u_int16_t s = 4; // cumulative size + while (s < size) + { + const u_int16_t ins_posn = u_int16_t(s * ::drand48()); // this insert posn + if (3.0 * ::drand48() > 1.0 || size - s < 2) // 2:1 chance of single insert when >= 2 still to insert + { + lfm.journal_insert(ins_posn); // single insert + s++; + } + else + { + // multiple insert, either 2 - 5 + const u_int16_t max_ins_size = size - s >5 ? 5 : size - s; + const u_int16_t ins_size = 2 + u_int16_t((max_ins_size - 2) * ::drand48()); // this insert size + lfm.journal_insert(ins_posn, ins_size); + s += ins_size; + } + } + } + + static void recover(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const bool ae, const u_int16_t ae_max_jfiles) + { + flist pfidl; + flist lfidl; + rcvdat rd; + const u_int16_t num_jfiles = lfm.size(); + + lfm.get_pfid_list(pfidl); + lfm.get_lfid_list(lfidl); + lm.finalize(); // clear all file handles before erasing old journal files + lfm.write_journal(ae, ae_max_jfiles, JFSIZE_SBLKS); + + lpmgr_test_helper::rcvdat_init(rd, pfidl, ae, ae_max_jfiles); + lm.recover(rd, &jc, &jc.new_fcntl); + BOOST_CHECK_EQUAL(lm.is_init(), true); + BOOST_CHECK_EQUAL(lm.is_ae(), ae); + BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles); + BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles); + if (num_jfiles) + check_pfids_lfids(lm, pfidl, lfidl); + else + BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0); + } + + static void finalize(lpmgr& lm) + { + lm.finalize(); + BOOST_CHECK_EQUAL(lm.is_init(), false); + BOOST_CHECK_EQUAL(lm.is_ae(), false); + BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), u_int16_t(0)); + BOOST_CHECK_EQUAL(lm.num_jfiles(), u_int16_t(0)); + BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0); + vector<u_int16_t> res; + lm.get_pfid_list(res); + BOOST_CHECK_EQUAL(res.size(), u_int16_t(0)); + lm.get_lfid_list(res); + BOOST_CHECK_EQUAL(res.size(), u_int16_t(0)); + } + + static void insert(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const u_int16_t after_lfid, const u_int16_t incr = 1) + { + flist pfidl; + flist lfidl; + const u_int16_t num_jfiles = lm.num_jfiles(); + lfm.journal_insert(after_lfid, incr); + lfm.get_pfid_list(pfidl); + lfm.get_lfid_list(lfidl); + lm.insert(after_lfid, &jc, &jc.new_fcntl, incr); + BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles + incr); + lpmgr_test_helper::check_pfids_lfids(lm, pfidl, lfidl); + } + + static void check_ae_max_jfiles(lpmgr& lm, const u_int16_t num_jfiles, const u_int16_t ae_max_jfiles) + { + bool legal = ae_max_jfiles > num_jfiles || ae_max_jfiles == 0; + + lm.set_ae(false); + BOOST_CHECK(!lm.is_ae()); + if (legal) + { + lm.set_ae_max_jfiles(ae_max_jfiles); + BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles); + lm.set_ae(true); + BOOST_CHECK(lm.is_ae()); + BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), ae_max_jfiles + ? ae_max_jfiles - num_jfiles + : JRNL_MAX_NUM_FILES - num_jfiles); + } + else + { + lm.set_ae_max_jfiles(ae_max_jfiles); + BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles); + try + { + lm.set_ae(true); // should raise exception + BOOST_ERROR("Auto-expand enabled with out-of-range ae_max_jfiles"); + } + catch (const jexception& e) { BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_BADAEFNUMLIM); } + BOOST_CHECK(!lm.is_ae()); + BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), 0); + } + BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), ae_max_jfiles); + } + + static void check_multiple_initialization_recover(lfid_pfid_map& lfm, test_jrnl& jc, + const u_int16_t num_jfiles_arr[][2], const bool init_flag_0, const bool finalize_flag, + const bool init_flag_1) + { + unsigned i_njf = 0; + while (num_jfiles_arr[i_njf][0] && num_jfiles_arr[i_njf][1]) // cycle through each entry in num_jfiles_arr + { + for (unsigned i1_njf = 0; i1_njf <= 1; i1_njf++) // cycle through the two numbers in each entry of num_jfiles_arr + { + const u_int16_t num_jfiles_0 = num_jfiles_arr[i_njf][i1_njf == 0]; // first number in pair + const u_int16_t num_jfiles_1 = num_jfiles_arr[i_njf][i1_njf != 0]; // second number in pair + + for (unsigned i_ae = 0; i_ae < 4; i_ae++) // cycle through combinations of enabling AE + { + const bool ae_0 = i_ae & 0x1; // first bit: enable AE on first init + const bool ae_1 = i_ae & 0x2; // second bit: enable AE on second init + for (unsigned i_aemjf = 0; i_aemjf < 4; i_aemjf++) // cycle through combinations of enabling/disabling ae limit + { + const u_int16_t ae_max_jfiles_0 = i_aemjf & 0x1 ? 3 * num_jfiles_0 : 0; // max ae files, 0 = disable max + const u_int16_t ae_max_jfiles_1 = i_aemjf & 0x2 ? 4 * num_jfiles_1 : 0; // max ae files, 0 = disable max + + lpmgr lm; // DUT + + if (init_flag_0) + initialize(lm, jc, num_jfiles_0, ae_0, ae_max_jfiles_0); + else + { + prepare_recover(lfm, num_jfiles_0); + recover(lfm, lm, jc, ae_1, ae_max_jfiles_0); + lfm.destroy_journal(); + } + + if (finalize_flag) finalize(lm); + + if (init_flag_1) + initialize(lm, jc, num_jfiles_1, ae_1, ae_max_jfiles_1); + else + { + prepare_recover(lfm, num_jfiles_1); + recover(lfm, lm, jc, ae_1, ae_max_jfiles_1); + lfm.destroy_journal(); + } + } + } + } + i_njf++; + } + } + + static void check_insert(lfid_pfid_map& lfm, lpmgr& lm, test_jrnl& jc, const u_int16_t after_lfid, + const u_int16_t incr = 1) + { + const u_int16_t num_jfiles = lm.num_jfiles(); + const u_int16_t ae_max_jfiles = lm.ae_max_jfiles(); + const u_int16_t effective_ae_max_jfiles = ae_max_jfiles ? ae_max_jfiles : JRNL_MAX_NUM_FILES; + BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), effective_ae_max_jfiles - num_jfiles); + bool legal = lm.is_ae() && num_jfiles + incr <= effective_ae_max_jfiles; + if (legal) + { + insert(lfm, lm, jc, after_lfid, incr); + BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles + incr); + BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), effective_ae_max_jfiles - num_jfiles - incr); + } + else + { + try + { + insert(lfm, lm, jc, after_lfid, incr); + if (lm.is_ae()) + BOOST_ERROR("lpmgr::insert() succeeded and exceeded limit"); + else + BOOST_ERROR("lpmgr::insert() succeeded with auto-expand disabled"); + } + catch (const jexception& e) + { + if (lm.is_ae()) + BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_AEFNUMLIMIT); + else + BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_AEDISABLED); + } + BOOST_CHECK_EQUAL(lm.num_jfiles(), num_jfiles); + BOOST_CHECK_EQUAL(lm.ae_jfiles_rem(), effective_ae_max_jfiles - num_jfiles); + } + } + + static void check_limit(lfid_pfid_map& lfm, test_jrnl& jc, const bool ae, const u_int16_t num_jfiles, + const u_int16_t ae_max_jfiles) + { + lpmgr lm; + + for (unsigned i = 0; i < 2; i++) + { + if (i) + initialize(lfm, lm, jc, num_jfiles, ae, ae_max_jfiles); + else + { + prepare_recover(lfm, num_jfiles); + recover(lfm, lm, jc, ae, ae_max_jfiles); + } + + // use up all available files + unsigned j = ae_max_jfiles ? ae_max_jfiles : JRNL_MAX_NUM_FILES; + while (ae && j > num_jfiles) + { + const u_int16_t posn = static_cast<u_int16_t>((lm.num_jfiles() - 1) * ::drand48()); + const u_int16_t incr = 1 + static_cast<u_int16_t>((lm.ae_jfiles_rem() > 4 + ? 3 : lm.ae_jfiles_rem() - 1) * ::drand48()); + check_insert(lfm, lm, jc, posn, incr); + j -= incr; + } + // these should be over the limit or illegal + check_insert(lfm, lm, jc, 0); + check_insert(lfm, lm, jc, 2, 2); + lfm.destroy_journal(); + } + } + +private: + static void load_vector(const u_int16_t a[], const size_t n, flist& v) + { + for (size_t i = 0; i < n; i++) + v.push_back(a[i]); + } + + static void load_vector(const flist& a, flist& b) + { + for (flist_citr i = a.begin(); i < a.end(); i++) + b.push_back(*i); + } + + static void vectors_equal(const lpmgr& lm, const u_int16_t a[], const size_t n, const flist& b, + const bool pfid_check) + { + BOOST_CHECK_EQUAL(n, b.size()); + for (size_t i = 0; i < n; i++) + { + BOOST_CHECK_EQUAL(a[i], b[i]); + fcntl* fp = lm.get_fcntlp(i); + BOOST_CHECK_MESSAGE(fp != (void*)0, "Unexpected void pointer returned by lpmgr::get_fcntlp()"); + if (fp) BOOST_CHECK_EQUAL(pfid_check ? fp->pfid() : fp->lfid(), pfid_check ? a[i] : i); + } + } + + static void vectors_equal(const lpmgr& lm, const flist& a, const flist& b, const bool pfid_check) + { + BOOST_CHECK_EQUAL(a.size(), b.size()); + for (size_t i = 0; i < a.size(); i++) + { + BOOST_CHECK_EQUAL(a[i], b[i]); + fcntl* fp = lm.get_fcntlp(i); + BOOST_CHECK_MESSAGE(fp != (void*)0, "Unexpected void pointer returned by lpmgr::get_fcntlp()"); + if (fp) BOOST_CHECK_EQUAL(pfid_check ? fp->pfid() : fp->lfid(), pfid_check ? a[i] : i); + } + } + + static void linear_vectors_equal(const lpmgr& lm, const size_t n, const flist& f, const bool pfid_check) + { + BOOST_CHECK_EQUAL(n, f.size()); + for (size_t i = 0; i < n; i++) + { + BOOST_CHECK_EQUAL(i, f[i]); + fcntl* fp = lm.get_fcntlp(i); + BOOST_CHECK_MESSAGE(fp != (void*)0, "Unexpected void pointer returned by lpmgr::get_fcntlp()"); + if (fp) BOOST_CHECK_EQUAL(pfid_check ? fp->pfid() : fp->lfid(), i); + } + } +}; + +// === Tests === + +#ifndef LONG_TEST +/* + * ============================================== + * NORMAL TESTS + * This section contains normal "make check" tests + * for building/packaging. These are built when + * LONG_TEST is _not_ defined. + * ============================================== + */ + +/* + * Check that after construction, the fcntl array _fcntl_arr is empty and the is_init() function returns false. + */ +QPID_AUTO_TEST_CASE(default_constructor) +{ + string test_name = get_test_name(test_filename, "default_constructor"); + try + { + lpmgr lm; + BOOST_CHECK_EQUAL(lm.is_init(), false); + BOOST_CHECK_EQUAL(lm.is_ae(), false); + BOOST_CHECK_EQUAL(lm.ae_max_jfiles(), u_int16_t(0)); + BOOST_CHECK_EQUAL(lm.num_jfiles(), u_int16_t(0)); + BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that initialize() correctly creates an ordered fcntl array _fcntl_arr. + */ +QPID_AUTO_TEST_CASE(initialize) +{ + string test_name = get_test_name(test_filename, "initialize"); + const u_int16_t num_jfiles = 8; + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + { + lpmgr lm; + lpmgr_test_helper::initialize(lm, jc, num_jfiles, false, 0); + } + { + lpmgr lm; + lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 0); + } + { + lpmgr lm; + lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 5 * num_jfiles); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that recover() correctly sets up the specified pfid list order. + */ +QPID_AUTO_TEST_CASE(recover) +{ + string test_name = get_test_name(test_filename, "recover"); + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + try + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + + { + lpmgr lm; + lpmgr_test_helper::prepare_recover(lfm, 8); + lpmgr_test_helper::recover(lfm, lm, jc, false, 0); + lfm.destroy_journal(); + } + { + lpmgr lm; + lpmgr_test_helper::prepare_recover(lfm, 8); + lpmgr_test_helper::recover(lfm, lm, jc, true, 0); + lfm.destroy_journal(); + } + { + lpmgr lm; + lpmgr_test_helper::prepare_recover(lfm, 8); + lpmgr_test_helper::recover(lfm, lm, jc, true, 5 * lfm.size()); + lfm.destroy_journal(); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that finalize() after an initialize() empties _fcntl_arr and that afterwards is_init() returns false. + */ +QPID_AUTO_TEST_CASE(initialize_finalize) +{ + string test_name = get_test_name(test_filename, "initialize_finalize"); + const u_int16_t num_jfiles = 8; + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + { + lpmgr lm; + lpmgr_test_helper::initialize(lm, jc, num_jfiles, false, 0); + lpmgr_test_helper::finalize(lm); + } + { + lpmgr lm; + lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 0); + lpmgr_test_helper::finalize(lm); + } + { + lpmgr lm; + lpmgr_test_helper::initialize(lm, jc, num_jfiles, true, 5 * num_jfiles); + lpmgr_test_helper::finalize(lm); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that finalize() after a recover() empties _fcntl_arr and that afterwards is_init() returns false. + */ +QPID_AUTO_TEST_CASE(recover_finalize) +{ + string test_name = get_test_name(test_filename, "recover_finalize"); + const u_int16_t num_jfiles = 8; + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + try + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + + { + lpmgr lm; + lpmgr_test_helper::prepare_recover(lfm, num_jfiles); + lpmgr_test_helper::recover(lfm, lm, jc, false, 0); + lpmgr_test_helper::finalize(lm); + lfm.destroy_journal(); + } + { + lpmgr lm; + lpmgr_test_helper::prepare_recover(lfm, num_jfiles); + lpmgr_test_helper::recover(lfm, lm, jc, true, 0); + lpmgr_test_helper::finalize(lm); + lfm.destroy_journal(); + } + { + lpmgr lm; + lpmgr_test_helper::prepare_recover(lfm, num_jfiles); + lpmgr_test_helper::recover(lfm, lm, jc, true, 5 * lfm.size()); + lpmgr_test_helper::finalize(lm); + lfm.destroy_journal(); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that 0 and/or null and other extreme/boundary parameters behave as expected. + */ +QPID_AUTO_TEST_CASE(zero_null_params) +{ + string test_name = get_test_name(test_filename, "zero_null_params"); + const u_int16_t num_jfiles = 8; + try + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + lpmgr lm; + lpmgr_test_helper::initialize(lfm, lm, jc, num_jfiles, true, 0); + + // Check that inserting 0 files works ok + lpmgr_test_helper::insert(lfm, lm, jc, 0, 0); + lpmgr_test_helper::insert(lfm, lm, jc, 2, 0); + lpmgr_test_helper::insert(lfm, lm, jc, num_jfiles - 1, 0); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that initialize()/recover() works correctly after a previous initialize()/recover() with/without an intervening + * finalize(). + */ +QPID_AUTO_TEST_CASE(multiple_initialization_recover) +{ + string test_name = get_test_name(test_filename, "multiple_initialization_recover"); + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + + // Set combinations of value pairs to be used for number of journal files in first and second init + u_int16_t num_jfiles_arr[][2] = {{8, 12}, {4, 7}, {0, 0}}; // end with zeros + try + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + for (unsigned p = 0; p < 8; p++) + { + const bool i_0 = p & 0x01; // first bit + const bool i_1 = p & 0x02; // second bit + const bool f = p & 0x04; // third bit + lpmgr_test_helper::check_multiple_initialization_recover(lfm, jc, num_jfiles_arr, i_0, f, i_1); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that insert() works correctly after initialize() and shifts the pfid sequence beyond the insert point correctly: + * + * The following sequence is tested: + * initialize 4 pfids=[0,1,2,3] lfids=[0,1,2,3] + * insert 1 after lfid 0 pfids=[0,4,1,2,3] lfids=[0,2,3,4,1] + * insert 2 after lfid 2 pfids=[0,4,1,5,6,2,3] lfids=[0,2,5,6,1,3,4] + * insert 1 after lfid 6 pfids=[0,4,1,5,6,2,3,7] lfids=[0,2,5,6,1,3,4,7] + * issert 1 after lfid 3 pfids=[0,4,1,5,8,6,2,3,7] lfids=[0,2,6,7,1,3,5,8,4] + */ +QPID_AUTO_TEST_CASE(initialize_insert) +{ + string test_name = get_test_name(test_filename, "initialize_insert"); + const u_int16_t initial_num_jfiles = 8; + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + lpmgr lm; + lpmgr_test_helper::initialize(lfm, lm, jc, initial_num_jfiles, true, 0); + + lpmgr_test_helper::insert(lfm, lm, jc, 0); + lpmgr_test_helper::insert(lfm, lm, jc, 2, 2); + lpmgr_test_helper::insert(lfm, lm, jc, 6); + lpmgr_test_helper::insert(lfm, lm, jc, 3); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that insert() works correctly after recover() and shifts the pfid sequence beyond the insert point correctly: + * + * The following sequence is tested: + * recover 4 pfids=[0,2,3,1] lfids=[0,3,1,2] + * insert 1 after lfid 0 pfids=[0,4,2,3,1] lfids=[0,4,2,3,1] + * insert 2 after lfid 2 pfids=[0,4,2,5,6,3,1] lfids=[0,6,2,5,1,3,4] + * insert 1 after lfid 6 pfids=[0,4,2,5,6,3,1,7] lfids=[0,6,2,5,1,3,4,7] + * issert 1 after lfid 3 pfids=[0,4,2,5,8,6,3,1,7] lfids=[0,7,2,6,1,3,5,8,4] + */ +QPID_AUTO_TEST_CASE(recover_insert) +{ + string test_name = get_test_name(test_filename, "recover_insert"); + const u_int16_t initial_num_jfiles = 4; + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + lpmgr lm; + lpmgr_test_helper::prepare_recover(lfm, initial_num_jfiles); + lpmgr_test_helper::recover(lfm, lm, jc, true, 0); + + lpmgr_test_helper::insert(lfm, lm, jc, 0); + lpmgr_test_helper::insert(lfm, lm, jc, 2, 2); + lpmgr_test_helper::insert(lfm, lm, jc, 6); + lpmgr_test_helper::insert(lfm, lm, jc, 3); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that illegal ae parameter combinations are caught and result in an exception being thrown. + */ +QPID_AUTO_TEST_CASE(ae_parameters) +{ + string test_name = get_test_name(test_filename, "ae_parameters"); + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + const u_int16_t num_jfiles = 8; + lpmgr lm; + + for (unsigned i = 0; i < 2; i++) + { + if (i) + lpmgr_test_helper::initialize(lfm, lm, jc, num_jfiles, false, 0); + else + { + lpmgr_test_helper::prepare_recover(lfm, num_jfiles); + lpmgr_test_helper::recover(lfm, lm, jc, false, 0); + } + + lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, num_jfiles - 2); + lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, 0); + lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, 2 * num_jfiles); + lpmgr_test_helper::check_ae_max_jfiles(lm, num_jfiles, num_jfiles); + lfm.destroy_journal(); + } + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that initialized or recovered journals with auto-expand disabled will not allow either inserts or appends. + */ +QPID_AUTO_TEST_CASE(ae_disabled) +{ + string test_name = get_test_name(test_filename, "ae_disabled"); + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + lpmgr_test_helper::check_limit(lfm, jc, false, 8, 0); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that initialized or recovered journals with auto-expand enabled and a file limit set will enforce the correct + * limits on inserts and appends. + */ +QPID_AUTO_TEST_CASE(ae_enabled_limit) +{ + string test_name = get_test_name(test_filename, "ae_enabled_limit"); + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + lpmgr_test_helper::check_limit(lfm, jc, true, 8, 32); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +/* + * Check that initialized or recovered journals with auto-expand enabled and no file limit set (0) will allow inserts and + * appends up to the file limit JRNL_MAX_NUM_FILES. + */ +QPID_AUTO_TEST_CASE(ae_enabled_unlimited) +{ + string test_name = get_test_name(test_filename, "ae_enabled_unlimited"); + ::srand48(1); // init random gen for repeatable tests when using lpmgr_test_helper::prepare_recover() + try + { + jdir::create_dir(test_dir); // Check test dir exists; create it if not + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lfid_pfid_map lfm(test_name, test_name); + lpmgr_test_helper::check_limit(lfm, jc, true, 8, 0); + } + catch(const exception& e) { BOOST_FAIL(e.what()); } + cout << "done" << endl; +} + +#else +/* + * ============================================== + * LONG TESTS + * This section contains long tests and soak tests, + * and are run using target check-long (ie "make + * check-long"). These are built when LONG_TEST is + * defined. + * ============================================== + */ + +/* + * Tests randomized combinations of initialization/recovery, initial size, number, size and location of inserts. + * + * To reproduce a specific test, comment out the get_seed() statement and uncomment the literal below, adjusting the seed + * value to that required. + */ +QPID_AUTO_TEST_CASE(randomized_tests) +{ + string test_name = get_test_name(test_filename, "randomized_tests"); + const long seed = get_seed(); + // const long seed = 0x2d9b69d32; + cout << "seed=0x" << hex << seed << dec << " " << flush; + ::srand48(seed); + + lfid_pfid_map lfm(test_name, test_name); + flist pfidl; + flist lfidl; + rcvdat rd; + u_int16_t curr_ae_max_jfiles = 0; + jdir::create_dir(test_dir); // Check test dir exists; create it if not + + for (int test_num = 0; test_num < 250; test_num++) + { + test_jrnl_cb cb; + test_jrnl jc(test_name, test_dir, test_name, cb); + lpmgr lm; + // 50% chance of recovery except first run and if there is still ae space left + const bool recover_flag = test_num > 0 && + curr_ae_max_jfiles > lfm.size() && + 2.0 * ::drand48() < 1.0; + if (recover_flag) + { + // Recover from previous iteration + lfm.get_pfid_list(pfidl); + lfm.get_lfid_list(lfidl); + lfm.write_journal(true, curr_ae_max_jfiles, JFSIZE_SBLKS); + lpmgr_test_helper::rcvdat_init(rd, pfidl, true, curr_ae_max_jfiles); + lm.recover(rd, &jc, &jc.new_fcntl); + lpmgr_test_helper::check_pfids_lfids(lm, pfidl, lfidl); + } + else + { + // Initialize from scratch + const u_int16_t num_jfiles = 4 + u_int16_t(21.0 * ::drand48()); // size: 4 - 25 files + curr_ae_max_jfiles = u_int16_t(4 * num_jfiles * ::drand48()); // size: 0 - 100 files + if (curr_ae_max_jfiles > JRNL_MAX_NUM_FILES) curr_ae_max_jfiles = JRNL_MAX_NUM_FILES; + else if (curr_ae_max_jfiles <= num_jfiles) curr_ae_max_jfiles = 0; + lfm.destroy_journal(); + lfm.journal_create(num_jfiles, num_jfiles); + lfm.get_pfid_list(pfidl); + lfm.get_lfid_list(lfidl); + lm.initialize(num_jfiles, true, curr_ae_max_jfiles, &jc, &jc.new_fcntl); + lpmgr_test_helper::check_linear_pfids_lfids(lm, num_jfiles); + } + + // Loop to insert pfids + const int num_inserts = 1 + int(lfm.size() * ::drand48()); + for (int i = 0; i < num_inserts; i++) + { + const u_int16_t size = lm.num_jfiles(); + const u_int16_t after_lfid = u_int16_t(1.0 * size * ::drand48()); + const u_int16_t num_jfiles = 1 + u_int16_t(4.0 * ::drand48()); + const bool legal = lm.ae_max_jfiles() + ? size + num_jfiles <= lm.ae_max_jfiles() + : size + num_jfiles <= JRNL_MAX_NUM_FILES; + if (legal) + { + lfm.journal_insert(after_lfid, num_jfiles); + lfm.get_pfid_list(pfidl); + lfm.get_lfid_list(lfidl); + + lm.insert(after_lfid, &jc, &jc.new_fcntl, num_jfiles); + lpmgr_test_helper::check_pfids_lfids(lm, pfidl, lfidl); + } + else + { + try + { + lm.insert(after_lfid, &jc, &jc.new_fcntl, num_jfiles); + BOOST_FAIL("lpmgr::insert() succeeded and exceeded limit"); + } + catch (const jexception& e) + { + BOOST_CHECK_EQUAL(e.err_code(), jerrno::JERR_LFMGR_AEFNUMLIMIT); + break; // no more inserts... + } + } + } + lm.finalize(); + BOOST_CHECK_EQUAL(lm.is_init(), false); + BOOST_CHECK_EQUAL(lm.num_jfiles(), u_int16_t(0)); + BOOST_CHECK_EQUAL(lm.get_fcntlp(0), (void*)0); + } + cout << "done" << endl; +} + +#endif + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_rec_hdr.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_rec_hdr.cpp new file mode 100644 index 0000000000..099e576bbd --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_rec_hdr.cpp @@ -0,0 +1,438 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <ctime> +#include <iostream> +#include "qpid/legacystore/jrnl/deq_hdr.h" +#include "qpid/legacystore/jrnl/enq_hdr.h" +#include "qpid/legacystore/jrnl/file_hdr.h" +#include "qpid/legacystore/jrnl/jcfg.h" +#include "qpid/legacystore/jrnl/rec_tail.h" +#include "qpid/legacystore/jrnl/txn_hdr.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(rec_hdr_suite) + +const string test_filename("_ut_rec_hdr"); + +QPID_AUTO_TEST_CASE(hdr_class) +{ + cout << test_filename << ".hdr_class: " << flush; + rec_hdr h1; + BOOST_CHECK_EQUAL(h1._magic, 0UL); + BOOST_CHECK_EQUAL(h1._version, 0); + BOOST_CHECK_EQUAL(h1._eflag, 0); + BOOST_CHECK_EQUAL(h1._uflag, 0); + BOOST_CHECK_EQUAL(h1._rid, 0ULL); + BOOST_CHECK(!h1.get_owi()); + + const u_int32_t magic = 0x89abcdefUL; + const u_int16_t uflag = 0x5537; + const u_int8_t version = 0xef; + const u_int64_t rid = 0x123456789abcdef0ULL; + const bool owi = true; + + rec_hdr h2(magic, version, rid, owi); + BOOST_CHECK_EQUAL(h2._magic, magic); + BOOST_CHECK_EQUAL(h2._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(h2._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(h2._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(h2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); + BOOST_CHECK_EQUAL(h2._rid, rid); + BOOST_CHECK_EQUAL(h2.get_owi(), owi); + h2._uflag = uflag; + BOOST_CHECK(h2.get_owi()); + h2.set_owi(true); + BOOST_CHECK(h2.get_owi()); + BOOST_CHECK_EQUAL(h2._uflag, uflag); + h2.set_owi(false); + BOOST_CHECK(!h2.get_owi()); + BOOST_CHECK_EQUAL(h2._uflag, (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK)); + h2.set_owi(true); + BOOST_CHECK(h2.get_owi()); + BOOST_CHECK_EQUAL(h2._uflag, uflag); + + h1.hdr_copy(h2); + BOOST_CHECK_EQUAL(h1._magic, magic); + BOOST_CHECK_EQUAL(h1._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(h1._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(h1._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(h1._uflag, uflag); + BOOST_CHECK_EQUAL(h1._rid, rid); + BOOST_CHECK(h1.get_owi()); + BOOST_CHECK_EQUAL(h1._uflag, uflag); + + h1.reset(); + BOOST_CHECK_EQUAL(h1._magic, 0UL); + BOOST_CHECK_EQUAL(h1._version, 0); + BOOST_CHECK_EQUAL(h1._eflag, 0); + BOOST_CHECK_EQUAL(h1._uflag, 0); + BOOST_CHECK_EQUAL(h1._rid, 0ULL); + BOOST_CHECK(!h1.get_owi()); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(rec_tail_class) +{ + cout << test_filename << ".rec_tail_class: " << flush; + const u_int32_t magic = 0xfedcba98; + const u_int64_t rid = 0xfedcba9876543210ULL; + const u_int32_t xmagic = ~magic; + + { + rec_tail rt1; + BOOST_CHECK_EQUAL(rt1._xmagic, 0xffffffffUL); + BOOST_CHECK_EQUAL(rt1._rid, 0ULL); + } + + { + rec_tail rt2(magic, rid); + BOOST_CHECK_EQUAL(rt2._xmagic, magic); + BOOST_CHECK_EQUAL(rt2._rid, rid); + } + + { + rec_hdr h(magic, RHM_JDAT_VERSION, rid, true); + rec_tail rt3(h); + BOOST_CHECK_EQUAL(rt3._xmagic, xmagic); + BOOST_CHECK_EQUAL(rt3._rid, rid); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(file_hdr_class) +{ + cout << test_filename << ".file_hdr_class: " << flush; + const u_int32_t magic = 0xfedcba98UL; + const u_int8_t version = 0xa5; + const u_int16_t uflag = 0x5537; + const u_int64_t rid = 0xfedcba9876543210ULL; + const u_int16_t pfid = 0xfedcU; + const u_int16_t lfid = 0xf0e1U; +#ifdef JRNL_32_BIT + const std::size_t fro = 0xfedcba98UL; +#else + const std::size_t fro = 0xfedcba9876543210ULL; +#endif + timespec ts; + const bool owi = true; + + { + file_hdr fh1; + BOOST_CHECK_EQUAL(fh1._magic, 0UL); + BOOST_CHECK_EQUAL(fh1._version, 0); + BOOST_CHECK_EQUAL(fh1._eflag, 0); + BOOST_CHECK_EQUAL(fh1._uflag, 0); + BOOST_CHECK_EQUAL(fh1._rid, 0ULL); + BOOST_CHECK_EQUAL(fh1._pfid, 0UL); + BOOST_CHECK_EQUAL(fh1._lfid, 0U); + BOOST_CHECK_EQUAL(fh1._fro, std::size_t(0)); + BOOST_CHECK_EQUAL(fh1._ts_sec, std::time_t(0)); + BOOST_CHECK_EQUAL(fh1._ts_nsec, u_int32_t(0)); + BOOST_CHECK(!fh1.get_owi()); + } + + { + file_hdr fh2(magic, version, rid, pfid, lfid, fro, owi, false); + BOOST_CHECK_EQUAL(fh2._magic, magic); + BOOST_CHECK_EQUAL(fh2._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(fh2._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(fh2._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(fh2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); + BOOST_CHECK_EQUAL(fh2._rid, rid); + BOOST_CHECK_EQUAL(fh2._pfid, pfid ); + BOOST_CHECK_EQUAL(fh2._lfid, lfid); + BOOST_CHECK_EQUAL(fh2._fro, fro); + BOOST_CHECK_EQUAL(fh2._ts_sec, std::time_t(0)); + BOOST_CHECK_EQUAL(fh2._ts_nsec, u_int32_t(0)); + ::clock_gettime(CLOCK_REALTIME, &ts); + fh2.set_time(ts); + BOOST_CHECK_EQUAL(fh2._ts_sec, ts.tv_sec); + BOOST_CHECK_EQUAL(fh2._ts_nsec, u_int32_t(ts.tv_nsec)); + BOOST_CHECK(fh2.get_owi()); + + fh2._uflag = uflag; + BOOST_CHECK(fh2.get_owi()); + + fh2.set_owi(false); + BOOST_CHECK(!fh2.get_owi()); + BOOST_CHECK_EQUAL(fh2._uflag, + (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK)); + + fh2.set_owi(true); + BOOST_CHECK(fh2.get_owi()); + BOOST_CHECK_EQUAL(fh2._uflag, uflag); + } + + { + file_hdr fh3(magic, version, rid, pfid, lfid, fro, owi, true); + BOOST_CHECK_EQUAL(fh3._magic, magic); + BOOST_CHECK_EQUAL(fh3._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(fh3._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(fh3._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(fh3._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); + BOOST_CHECK_EQUAL(fh3._rid, rid); + BOOST_CHECK_EQUAL(fh3._pfid, pfid); + BOOST_CHECK_EQUAL(fh3._lfid, lfid); + BOOST_CHECK_EQUAL(fh3._fro, fro); + BOOST_CHECK(fh3._ts_sec - ts.tv_sec <= 1); // No more than 1 sec difference + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(enq_hdr_class) +{ + cout << test_filename << ".enq_hdr_class: " << flush; + const u_int32_t magic = 0xfedcba98UL; + const u_int8_t version = 0xa5; + const u_int64_t rid = 0xfedcba9876543210ULL; + const u_int16_t uflag = 0x5537; +#ifdef JRNL_32_BIT + const std::size_t xidsize = 0xfedcba98UL; + const std::size_t dsize = 0x76543210UL; +#else + const std::size_t xidsize = 0xfedcba9876543210ULL; + const std::size_t dsize = 0x76543210fedcba98ULL; +#endif + const bool owi = true; + + { + enq_hdr eh1; + BOOST_CHECK_EQUAL(eh1._magic, 0UL); + BOOST_CHECK_EQUAL(eh1._version, 0); + BOOST_CHECK_EQUAL(eh1._eflag, 0); + BOOST_CHECK_EQUAL(eh1._uflag, 0); + BOOST_CHECK_EQUAL(eh1._rid, 0ULL); + BOOST_CHECK_EQUAL(eh1._xidsize, std::size_t(0)); + BOOST_CHECK_EQUAL(eh1._dsize, std::size_t(0)); + BOOST_CHECK(!eh1.get_owi()); + } + + { + enq_hdr eh2(magic, version, rid, xidsize, dsize, owi, false); + BOOST_CHECK_EQUAL(eh2._magic, magic); + BOOST_CHECK_EQUAL(eh2._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(eh2._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(eh2._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(eh2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); + BOOST_CHECK_EQUAL(eh2._rid, rid); + BOOST_CHECK_EQUAL(eh2._xidsize, xidsize); + BOOST_CHECK_EQUAL(eh2._dsize, dsize); + BOOST_CHECK(eh2.get_owi()); + BOOST_CHECK(!eh2.is_transient()); + BOOST_CHECK(!eh2.is_external()); + + eh2._uflag = uflag; + BOOST_CHECK(eh2.get_owi()); + BOOST_CHECK(eh2.is_transient()); + BOOST_CHECK(eh2.is_external()); + + eh2.set_owi(false); + BOOST_CHECK(!eh2.get_owi()); + BOOST_CHECK(eh2.is_transient()); + BOOST_CHECK(eh2.is_external()); + BOOST_CHECK_EQUAL(eh2._uflag, + (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK)); + + eh2.set_owi(true); + BOOST_CHECK(eh2.get_owi()); + BOOST_CHECK(eh2.is_transient()); + BOOST_CHECK(eh2.is_external()); + BOOST_CHECK_EQUAL(eh2._uflag, uflag); + + eh2.set_transient(false); + BOOST_CHECK(eh2.get_owi()); + BOOST_CHECK(!eh2.is_transient()); + BOOST_CHECK(eh2.is_external()); + BOOST_CHECK_EQUAL(eh2._uflag, uflag & ~(const u_int16_t)enq_hdr::ENQ_HDR_TRANSIENT_MASK); + + eh2.set_transient(true); + BOOST_CHECK(eh2.get_owi()); + BOOST_CHECK(eh2.is_transient()); + BOOST_CHECK(eh2.is_external()); + BOOST_CHECK_EQUAL(eh2._uflag, uflag); + + eh2.set_external(false); + BOOST_CHECK(eh2.get_owi()); + BOOST_CHECK(eh2.is_transient()); + BOOST_CHECK(!eh2.is_external()); + BOOST_CHECK_EQUAL(eh2._uflag, uflag & ~(const u_int16_t)enq_hdr::ENQ_HDR_EXTERNAL_MASK); + + eh2.set_external(true); + BOOST_CHECK(eh2.get_owi()); + BOOST_CHECK(eh2.is_transient()); + BOOST_CHECK(eh2.is_external()); + BOOST_CHECK_EQUAL(eh2._uflag, uflag); + } + + { + enq_hdr eh3(magic, version, rid, xidsize, dsize, owi, true); + BOOST_CHECK_EQUAL(eh3._magic, magic); + BOOST_CHECK_EQUAL(eh3._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(eh3._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(eh3._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(eh3._uflag, (const u_int16_t)enq_hdr::ENQ_HDR_TRANSIENT_MASK | + (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); + BOOST_CHECK_EQUAL(eh3._rid, rid); + BOOST_CHECK_EQUAL(eh3._xidsize, xidsize); + BOOST_CHECK_EQUAL(eh3._dsize, dsize); + BOOST_CHECK(eh3.get_owi()); + BOOST_CHECK(eh3.is_transient()); + BOOST_CHECK(!eh3.is_external()); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(deq_hdr_class) +{ + cout << test_filename << ".deq_hdr_class: " << flush; + const u_int32_t magic = 0xfedcba98UL; + const u_int8_t version = 0xa5; + const u_int16_t uflag = 0x5537; + const u_int64_t rid = 0xfedcba9876543210ULL; + const u_int64_t drid = 0x76543210fedcba98ULL; +#ifdef JRNL_32_BIT + const std::size_t xidsize = 0xfedcba98UL; +#else + const std::size_t xidsize = 0xfedcba9876543210ULL; +#endif + const bool owi = true; + + { + deq_hdr dh1; + BOOST_CHECK_EQUAL(dh1._magic, 0UL); + BOOST_CHECK_EQUAL(dh1._version, 0); + BOOST_CHECK_EQUAL(dh1._eflag, 0); + BOOST_CHECK_EQUAL(dh1._uflag, 0); + BOOST_CHECK_EQUAL(dh1._rid, 0ULL); + BOOST_CHECK_EQUAL(dh1._deq_rid, 0ULL); + BOOST_CHECK_EQUAL(dh1._xidsize, std::size_t(0)); + BOOST_CHECK(!dh1.get_owi()); + } + + { + deq_hdr dh2(magic, version, rid, drid, xidsize, owi); + BOOST_CHECK_EQUAL(dh2._magic, magic); + BOOST_CHECK_EQUAL(dh2._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(dh2._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(dh2._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(dh2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); + BOOST_CHECK_EQUAL(dh2._rid, rid); + BOOST_CHECK_EQUAL(dh2._deq_rid, drid); + BOOST_CHECK_EQUAL(dh2._xidsize, xidsize); + BOOST_CHECK(dh2.get_owi()); + + dh2._uflag = uflag; + BOOST_CHECK(dh2.get_owi()); + + dh2.set_owi(false); + BOOST_CHECK(!dh2.get_owi()); + BOOST_CHECK_EQUAL(dh2._uflag, + (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK)); + + dh2.set_owi(true); + BOOST_CHECK(dh2.get_owi()); + BOOST_CHECK_EQUAL(dh2._uflag, uflag); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(txn_hdr_class) +{ + cout << test_filename << ".txn_hdr_class: " << flush; + const u_int32_t magic = 0xfedcba98UL; + const u_int8_t version = 0xa5; + const u_int16_t uflag = 0x5537; + const u_int64_t rid = 0xfedcba9876543210ULL; +#ifdef JRNL_32_BIT + const std::size_t xidsize = 0xfedcba98UL; +#else + const std::size_t xidsize = 0xfedcba9876543210ULL; +#endif + const bool owi = true; + + { + txn_hdr th1; + BOOST_CHECK_EQUAL(th1._magic, 0UL); + BOOST_CHECK_EQUAL(th1._version, 0); + BOOST_CHECK_EQUAL(th1._eflag, 0); + BOOST_CHECK_EQUAL(th1._uflag, 0); + BOOST_CHECK_EQUAL(th1._rid, 0ULL); + BOOST_CHECK_EQUAL(th1._xidsize, std::size_t(0)); + BOOST_CHECK(!th1.get_owi()); + } + + { + txn_hdr th2(magic, version, rid, xidsize, owi); + BOOST_CHECK_EQUAL(th2._magic, magic); + BOOST_CHECK_EQUAL(th2._version, version); +#ifdef JRNL_LITTLE_ENDIAN + BOOST_CHECK_EQUAL(th2._eflag, RHM_LENDIAN_FLAG); +#else + BOOST_CHECK_EQUAL(th2._eflag, RHM_BENDIAN_FLAG); +#endif + BOOST_CHECK_EQUAL(th2._uflag, (const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK); + BOOST_CHECK_EQUAL(th2._rid, rid); + BOOST_CHECK_EQUAL(th2._xidsize, xidsize); + BOOST_CHECK(th2.get_owi()); + + th2._uflag = uflag; + BOOST_CHECK(th2.get_owi()); + + th2.set_owi(false); + BOOST_CHECK(!th2.get_owi()); + BOOST_CHECK_EQUAL(th2._uflag, + (uflag & ~(const u_int16_t)rec_hdr::HDR_OVERWRITE_INDICATOR_MASK)); + + th2.set_owi(true); + BOOST_CHECK(th2.get_owi()); + BOOST_CHECK_EQUAL(th2._uflag, uflag); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_time_ns.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_time_ns.cpp new file mode 100644 index 0000000000..f1b53bb97b --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_time_ns.cpp @@ -0,0 +1,163 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <ctime> +#include <iostream> +#include "qpid/legacystore/jrnl/time_ns.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(time_ns_suite) + +const string test_filename("_ut_time_ns"); + +QPID_AUTO_TEST_CASE(constructors) +{ + cout << test_filename << ".constructors: " << flush; + const std::time_t sec = 123; + const long nsec = 123456789; + + time_ns t1; + BOOST_CHECK_EQUAL(t1.tv_sec, 0); + BOOST_CHECK_EQUAL(t1.tv_nsec, 0); + BOOST_CHECK_EQUAL(t1.is_zero(), true); + time_ns t2(sec, nsec); + BOOST_CHECK_EQUAL(t2.tv_sec, sec); + BOOST_CHECK_EQUAL(t2.tv_nsec, nsec); + BOOST_CHECK_EQUAL(t2.is_zero(), false); + time_ns t3(t1); + BOOST_CHECK_EQUAL(t3.tv_sec, 0); + BOOST_CHECK_EQUAL(t3.tv_nsec, 0); + BOOST_CHECK_EQUAL(t3.is_zero(), true); + time_ns t4(t2); + BOOST_CHECK_EQUAL(t4.tv_sec, sec); + BOOST_CHECK_EQUAL(t4.tv_nsec, nsec); + BOOST_CHECK_EQUAL(t4.is_zero(), false); + t4.set_zero(); + BOOST_CHECK_EQUAL(t4.tv_sec, 0); + BOOST_CHECK_EQUAL(t4.tv_nsec, 0); + BOOST_CHECK_EQUAL(t4.is_zero(), true); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(operators) +{ + cout << test_filename << ".operators: " << flush; + const std::time_t sec1 = 123; + const long nsec1 = 123456789; + const std::time_t sec2 = 1; + const long nsec2 = 999999999; + const std::time_t sec_sum = sec1 + sec2 + 1; + const long nsec_sum = nsec1 + nsec2 - 1000000000; + const std::time_t sec_1_minus_2 = sec1 - sec2 - 1; + const long nsec_1_minus_2 = nsec1 - nsec2 + 1000000000; + const std::time_t sec_2_minus_1 = sec2 - sec1; + const long nsec_2_minus_1 = nsec2 - nsec1; + time_ns z; + time_ns t1(sec1, nsec1); + time_ns t2(sec2, nsec2); + + time_ns t3 = z; + BOOST_CHECK_EQUAL(t3.tv_sec, 0); + BOOST_CHECK_EQUAL(t3.tv_nsec, 0); + BOOST_CHECK_EQUAL(t3 == z, true); + BOOST_CHECK_EQUAL(t3 != z, false); + BOOST_CHECK_EQUAL(t3 > z, false); + BOOST_CHECK_EQUAL(t3 >= z, true); + BOOST_CHECK_EQUAL(t3 < z, false); + BOOST_CHECK_EQUAL(t3 <= z, true); + + t3 = t1; + BOOST_CHECK_EQUAL(t3.tv_sec, sec1); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec1); + BOOST_CHECK_EQUAL(t3 == t1, true); + BOOST_CHECK_EQUAL(t3 != t1, false); + BOOST_CHECK_EQUAL(t3 > t1, false); + BOOST_CHECK_EQUAL(t3 >= t1, true); + BOOST_CHECK_EQUAL(t3 < t1, false); + BOOST_CHECK_EQUAL(t3 <= t1, true); + + t3 += z; + BOOST_CHECK_EQUAL(t3.tv_sec, sec1); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec1); + + t3 = t2; + BOOST_CHECK_EQUAL(t3.tv_sec, sec2); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec2); + BOOST_CHECK_EQUAL(t3 == t2, true); + BOOST_CHECK_EQUAL(t3 != t2, false); + BOOST_CHECK_EQUAL(t3 > t2, false); + BOOST_CHECK_EQUAL(t3 >= t2, true); + BOOST_CHECK_EQUAL(t3 < t2, false); + BOOST_CHECK_EQUAL(t3 <= t2, true); + + t3 += z; + BOOST_CHECK_EQUAL(t3.tv_sec, sec2); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec2); + + t3 = t1; + t3 += t2; + BOOST_CHECK_EQUAL(t3.tv_sec, sec_sum); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_sum); + + t3 = t1; + t3 -= t2; + BOOST_CHECK_EQUAL(t3.tv_sec, sec_1_minus_2); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_1_minus_2); + + t3 = t2; + t3 -= t1; + BOOST_CHECK_EQUAL(t3.tv_sec, sec_2_minus_1); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_2_minus_1); + + t3 = t1 + t2; + BOOST_CHECK_EQUAL(t3.tv_sec, sec_sum); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_sum); + + t3 = t1 - t2; + BOOST_CHECK_EQUAL(t3.tv_sec, sec_1_minus_2); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_1_minus_2); + + t3 = t2 - t1; + BOOST_CHECK_EQUAL(t3.tv_sec, sec_2_minus_1); + BOOST_CHECK_EQUAL(t3.tv_nsec, nsec_2_minus_1); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(str) +{ + cout << test_filename << ".str: " << flush; + time_ns t1(123, 123456789); + BOOST_CHECK_EQUAL(t1.str(), "123.123457"); + BOOST_CHECK_EQUAL(t1.str(9), "123.123456789"); + BOOST_CHECK_EQUAL(t1.str(0), "123"); + time_ns t2(1, 1); + BOOST_CHECK_EQUAL(t2.str(9), "1.000000001"); + time_ns t3(-12, 345); + BOOST_CHECK_EQUAL(t3.str(9), "-11.999999655"); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/_ut_txn_map.cpp b/qpid/cpp/src/tests/legacystore/jrnl/_ut_txn_map.cpp new file mode 100644 index 0000000000..595ce0f6c6 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/_ut_txn_map.cpp @@ -0,0 +1,106 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../unit_test.h" + +#include <iomanip> +#include <iostream> +#include "qpid/legacystore/jrnl/txn_map.h" +#include <sstream> + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace std; + +QPID_AUTO_TEST_SUITE(txn_map_suite) + +const string test_filename("_ut_txn_map"); + +// === Helper functions === + +const string make_xid(u_int64_t rid) +{ + stringstream ss; + ss << "XID-" << setfill('0') << setw(16) << hex << rid; + ss << "-0123456789abcdef"; + return ss.str(); +} + +void check_td_equal(txn_data& td1, txn_data& td2) +{ + BOOST_CHECK_EQUAL(td1._rid, td2._rid); + BOOST_CHECK_EQUAL(td1._drid, td2._drid); + BOOST_CHECK_EQUAL(td1._pfid, td2._pfid); + BOOST_CHECK_EQUAL(td1._enq_flag, td2._enq_flag); + BOOST_CHECK_EQUAL(td1._aio_compl, td2._aio_compl); +} + +// === Test suite === + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + const u_int64_t rid = 0x123456789abcdef0ULL; + const u_int64_t drid = 0xfedcba9876543210ULL; + const u_int16_t pfid = 0xfedcU; + const bool enq_flag = true; + txn_data td(rid, drid, pfid, enq_flag); + BOOST_CHECK_EQUAL(td._rid, rid); + BOOST_CHECK_EQUAL(td._drid, drid); + BOOST_CHECK_EQUAL(td._pfid, pfid); + BOOST_CHECK_EQUAL(td._enq_flag, enq_flag); + BOOST_CHECK_EQUAL(td._aio_compl, false); + + txn_map t1; + BOOST_CHECK(t1.empty()); + BOOST_CHECK_EQUAL(t1.size(), u_int32_t(0)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(insert_get) +{ + cout << test_filename << ".insert_get: " << flush; + u_int16_t fid; + u_int64_t rid; + u_int16_t pfid_start = 0x2000U; + u_int64_t rid_begin = 0xffffffff00000000ULL; + u_int64_t rid_end = 0xffffffff00000200ULL; + + // insert with no dups + u_int64_t rid_incr_1 = 4ULL; + txn_map t2; + t2.set_num_jfiles(pfid_start + (rid_end - rid_begin)/rid_incr_1); + for (rid = rid_begin, fid = pfid_start; rid < rid_end; rid += rid_incr_1, fid++) + t2.insert_txn_data(make_xid(rid), txn_data(rid, ~rid, fid, false)); + BOOST_CHECK(!t2.empty()); + BOOST_CHECK_EQUAL(t2.size(), u_int32_t(128)); + + // get + u_int64_t rid_incr_2 = 6ULL; + for (u_int64_t rid = rid_begin; rid < rid_end; rid += rid_incr_2) + { + string xid = make_xid(rid); + BOOST_CHECK_EQUAL(t2.in_map(xid), (rid%rid_incr_1 ? false : true)); + } + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/chk_jdata b/qpid/cpp/src/tests/legacystore/jrnl/chk_jdata new file mode 100755 index 0000000000..2ac87d91b9 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/chk_jdata @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +JRNL_BLK_SIZE=512 # Block size in bytes +JRNL_PAGE_SIZE=256 # Journal page size in blocks +JRNL_FILE_SIZE=12 # Journal file size in pages +let END_OFFSET=${JRNL_BLK_SIZE}*${JRNL_PAGE_SIZE}*${JRNL_FILE_SIZE} +for f in jdata/test.*.jdat; do + echo $f + hexdump -C -n 1024 $f + hexdump -C -s ${END_OFFSET} $f + echo "============" +done diff --git a/qpid/cpp/src/tests/legacystore/jrnl/cp_rtest_jrnl b/qpid/cpp/src/tests/legacystore/jrnl/cp_rtest_jrnl new file mode 100755 index 0000000000..e21f991788 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/cp_rtest_jrnl @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +JDATA_DIR=jdata +TAR_DIR=rd_test_jrnls + +function get_filename +{ + local prefix=$1 + local file_num=$2 + local suffix=$3 + + if (( file_num < 10 )); then + local num="000${file_num}" + elif (( file_num < 100 )); then + local num="00${file_num}" + elif (( file_num < 1000 )); then + local num="0${file_num}" + else + local num="${file_num}" + fi + FILENAME=${prefix}${num}${suffix} + return 0 +} + +if (( $# != 1 )); then + echo "Incorrect args, expected 1 arg (usage: \"prep <testnum>\")" + exit +fi + +get_filename "t" $1 ".tar.gz" +if [[ -d ${JDATA_DIR} ]]; then + rm -rf ${JDATA_DIR}/* +else + mkdir -p ${JDATA_DIR} +fi +if [[ -f "${TAR_DIR}/${FILENAME}" ]]; then + tar -C ${JDATA_DIR} -xzf "${TAR_DIR}/${FILENAME}" +else + echo "Error: file \"${TAR_DIR}/${FILENAME}\" not found." +fi diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jhexdump b/qpid/cpp/src/tests/legacystore/jrnl/jhexdump new file mode 100755 index 0000000000..b013914441 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jhexdump @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +if [ -z "$1" ]; then + echo "No directory specified." + exit +fi + +JDIR=$1 +echo "Target directory: ${JDIR}" + +rm -f j*.txt + +if [ -d "${JDIR}" ]; then + n=0 + for f in "${JDIR}"/*.jdat; do + echo "$f -> j$n.txt" + hexdump -C "$f" > j$n.txt + (( n += 1 )) + done +else + echo "This directory does not exist." +fi diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_data_src.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_data_src.cpp new file mode 100644 index 0000000000..e4656ef83f --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_data_src.cpp @@ -0,0 +1,207 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" +#include <cstddef> +#include "data_src.h" +#include <iomanip> +#include <iostream> + +using namespace boost::unit_test; +using namespace mrg::jtt; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_data_src) + +const string test_filename("_ut_data_src"); + +long +get_seed() +{ + timespec ts; + if (::clock_gettime(CLOCK_REALTIME, &ts)) + BOOST_FAIL("Unable to read clock to generate seed."); + long tenths = ts.tv_nsec / 100000000; + return long(10 * ts.tv_sec + tenths); // time in tenths of a second +} + +#ifndef LONG_TEST +/* + * ============================================== + * NORMAL TESTS + * This section contains normal "make check" tests + * for building/packaging. These are built when + * LONG_TEST is _not_ defined. + * ============================================== + */ + +QPID_AUTO_TEST_CASE(data) +{ + cout << test_filename << ".data: " << flush; + BOOST_CHECK(data_src::max_dsize > 0); + for (std::size_t i=0; i<1024; i++) + { + const char* dp = data_src::get_data(i); + BOOST_CHECK_EQUAL(*dp, static_cast<char>('0' + ((i + 1) % 10))); + } + for (std::size_t i=data_src::max_dsize-1024; i<data_src::max_dsize; i++) + { + const char* dp = data_src::get_data(i); + BOOST_CHECK_EQUAL(*dp, static_cast<char>('0' + ((i + 1) % 10))); + } + const char* dp1 = data_src::get_data(data_src::max_dsize); + BOOST_CHECK_EQUAL(dp1,(char*) 0); + const char* dp2 = data_src::get_data(data_src::max_dsize + 0x1000); + BOOST_CHECK_EQUAL(dp2, (char*)0); + cout << "ok" << endl; +} + +// There is a long version of this test in _ut_long_data_src.cpp +QPID_AUTO_TEST_CASE(xid_data_xid) +{ + const std::size_t num = 64; + cout << test_filename << ".xid_data_xid: " << flush; + BOOST_CHECK_EQUAL(data_src::get_xid(1), "0"); + BOOST_CHECK_EQUAL(data_src::get_xid(2), "01"); + BOOST_CHECK_EQUAL(data_src::get_xid(3), "002"); + BOOST_CHECK_EQUAL(data_src::get_xid(4), "0003"); + BOOST_CHECK_EQUAL(data_src::get_xid(5), "00004"); + BOOST_CHECK_EQUAL(data_src::get_xid(6), "000005"); + BOOST_CHECK_EQUAL(data_src::get_xid(7), "0000006"); + BOOST_CHECK_EQUAL(data_src::get_xid(8), "00000007"); + BOOST_CHECK_EQUAL(data_src::get_xid(9), "xid:00008"); + BOOST_CHECK_EQUAL(data_src::get_xid(10), "xid:000009"); + BOOST_CHECK_EQUAL(data_src::get_xid(11), "xid:0000010"); + BOOST_CHECK_EQUAL(data_src::get_xid(12), "xid:00000011"); + BOOST_CHECK_EQUAL(data_src::get_xid(13), "xid:00000012:"); + BOOST_CHECK_EQUAL(data_src::get_xid(14), "xid:00000013:n"); + BOOST_CHECK_EQUAL(data_src::get_xid(15), "xid:00000014:no"); + std::size_t i = 15; + for (; i<num; i++) + { + string xid(data_src::get_xid(i)); + + ostringstream oss; + oss << setfill('0') << "xid:" << setw(8) << i << ":"; + + BOOST_CHECK_EQUAL(xid.size(), i); + BOOST_CHECK_EQUAL(xid.substr(0, 13), oss.str()); + BOOST_CHECK_EQUAL(xid[13], 'n'); + BOOST_CHECK_EQUAL(xid[i-1], (char)('a' + ((i-1)%26))); + } + for (std::size_t j=data_src::max_xsize-num; j<data_src::max_xsize; j++,i++) + { + string xid(data_src::get_xid(j)); + + ostringstream oss; + oss << setfill('0') << "xid:" << setw(8) << i << ":"; + + BOOST_CHECK_EQUAL(xid.size(), j); + BOOST_CHECK_EQUAL(xid.substr(0, 13), oss.str()); + BOOST_CHECK_EQUAL(xid[13], 'n'); + BOOST_CHECK_EQUAL(xid[j-1], (char)('a' + ((j-1)%26))); + } + cout << "ok" << endl; +} + +#else +/* + * ============================================== + * LONG TESTS + * This section contains long tests and soak tests, + * and are run using target check-long (ie "make + * check-long"). These are built when LONG_TEST is + * defined. + * ============================================== + */ + +/* + * To reproduce a specific test, comment out the get_seed() statement and uncomment the literal below, adjusting the seed + * value to that required. + */ +QPID_AUTO_TEST_CASE(xid_data_xid) +{ + const long seed = get_seed(); + // const long seed = 0x2d9b69d32; + ::srand48(seed); + + const std::size_t num = 1024; + cout << test_filename << ".xid_data_xid seed=0x" << std::hex << seed << std::dec << ": " << flush; + BOOST_CHECK_EQUAL(data_src::get_xid(1), "0"); + BOOST_CHECK_EQUAL(data_src::get_xid(2), "01"); + BOOST_CHECK_EQUAL(data_src::get_xid(3), "002"); + BOOST_CHECK_EQUAL(data_src::get_xid(4), "0003"); + BOOST_CHECK_EQUAL(data_src::get_xid(5), "00004"); + BOOST_CHECK_EQUAL(data_src::get_xid(6), "000005"); + BOOST_CHECK_EQUAL(data_src::get_xid(7), "0000006"); + BOOST_CHECK_EQUAL(data_src::get_xid(8), "00000007"); + BOOST_CHECK_EQUAL(data_src::get_xid(9), "xid:00008"); + BOOST_CHECK_EQUAL(data_src::get_xid(10), "xid:000009"); + BOOST_CHECK_EQUAL(data_src::get_xid(11), "xid:0000010"); + BOOST_CHECK_EQUAL(data_src::get_xid(12), "xid:00000011"); + BOOST_CHECK_EQUAL(data_src::get_xid(13), "xid:00000012:"); + BOOST_CHECK_EQUAL(data_src::get_xid(14), "xid:00000013:n"); + BOOST_CHECK_EQUAL(data_src::get_xid(15), "xid:00000014:no"); + std::size_t i = 15; + for (; i<num; i++) + { + string xid(data_src::get_xid(i)); + + ostringstream oss; + oss << setfill('0') << "xid:" << setw(8) << i << ":"; + + BOOST_CHECK_EQUAL(xid.size(), i); + BOOST_CHECK_EQUAL(xid.substr(0, 13), oss.str()); + BOOST_CHECK_EQUAL(xid[13], 'n'); + BOOST_CHECK_EQUAL(xid[i-1], (char)('a' + ((i-1)%26))); + } + for (std::size_t j=data_src::max_xsize-num; j<data_src::max_xsize; j++,i++) + { + string xid(data_src::get_xid(j)); + + ostringstream oss; + oss << setfill('0') << "xid:" << setw(8) << i << ":"; + + BOOST_CHECK_EQUAL(xid.size(), j); + BOOST_CHECK_EQUAL(xid.substr(0, 13), oss.str()); + BOOST_CHECK_EQUAL(xid[13], 'n'); + BOOST_CHECK_EQUAL(xid[j-1], (char)('a' + ((j-1)%26))); + } + std::srand(seed); + for (int cnt=0; cnt<1000; cnt++,i++) + { + std::size_t k = 1 + ::lrand48() % (data_src::max_xsize - 1); + string xid(data_src::get_xid(k)); + + ostringstream oss; + oss << setfill('0') << "xid:" << setw(8) << i << ":"; + + BOOST_CHECK_EQUAL(xid.size(), k); + BOOST_CHECK_EQUAL(xid.substr(0, 13), oss.str()); + BOOST_CHECK_EQUAL(xid[13], 'n'); + BOOST_CHECK_EQUAL(xid[k-1], (char)('a' + ((k-1)%26))); + } + cout << "ok" << endl; +} + +#endif + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_init_params.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_init_params.cpp new file mode 100644 index 0000000000..9fefe25105 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_init_params.cpp @@ -0,0 +1,100 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" +#include "jrnl_init_params.h" +#include <iostream> + +using namespace boost::unit_test; +using namespace mrg::jtt; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_jrnl_init_params) + +const string test_filename("_ut_jrnl_init_params"); + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + const string jid = "jid"; + const string jdir = "jdir"; + const string bfn = "base filename"; + const u_int16_t num_jfiles = 123; + const bool ae = false; + const u_int16_t ae_max_jfiles = 456; + const u_int32_t jfsize_sblks = 789; + jrnl_init_params jip(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks); + BOOST_CHECK_EQUAL(jip.jid(), jid); + BOOST_CHECK_EQUAL(jip.jdir(), jdir); + BOOST_CHECK_EQUAL(jip.base_filename(), bfn); + BOOST_CHECK_EQUAL(jip.num_jfiles(), num_jfiles); + BOOST_CHECK_EQUAL(jip.is_ae(), ae); + BOOST_CHECK_EQUAL(jip.ae_max_jfiles(), ae_max_jfiles); + BOOST_CHECK_EQUAL(jip.jfsize_sblks(), jfsize_sblks); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(copy_constructor_1) +{ + cout << test_filename << ".copy_constructor_1: " << flush; + const string jid = "jid"; + const string jdir = "jdir"; + const string bfn = "base filename"; + const u_int16_t num_jfiles = 123; + const bool ae = false; + const u_int16_t ae_max_jfiles = 456; + const u_int32_t jfsize_sblks = 789; + jrnl_init_params jip1(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks); + jrnl_init_params jip2(jip1); + BOOST_CHECK_EQUAL(jip2.jid(), jid); + BOOST_CHECK_EQUAL(jip2.jdir(), jdir); + BOOST_CHECK_EQUAL(jip2.base_filename(), bfn); + BOOST_CHECK_EQUAL(jip2.num_jfiles(), num_jfiles); + BOOST_CHECK_EQUAL(jip2.is_ae(), ae); + BOOST_CHECK_EQUAL(jip2.ae_max_jfiles(), ae_max_jfiles); + BOOST_CHECK_EQUAL(jip2.jfsize_sblks(), jfsize_sblks); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(copy_constructor_2) +{ + cout << test_filename << ".copy_constructor_2: " << flush; + const string jid = "jid"; + const string jdir = "jdir"; + const string bfn = "base filename"; + const u_int16_t num_jfiles = 123; + const bool ae = false; + const u_int16_t ae_max_jfiles = 456; + const u_int32_t jfsize_sblks = 789; + jrnl_init_params::shared_ptr p(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks)); + jrnl_init_params jip2(p.get()); + BOOST_CHECK_EQUAL(jip2.jid(), jid); + BOOST_CHECK_EQUAL(jip2.jdir(), jdir); + BOOST_CHECK_EQUAL(jip2.base_filename(), bfn); + BOOST_CHECK_EQUAL(jip2.num_jfiles(), num_jfiles); + BOOST_CHECK_EQUAL(jip2.is_ae(), ae); + BOOST_CHECK_EQUAL(jip2.ae_max_jfiles(), ae_max_jfiles); + BOOST_CHECK_EQUAL(jip2.jfsize_sblks(), jfsize_sblks); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() + diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_instance.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_instance.cpp new file mode 100644 index 0000000000..12f1c542d6 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_jrnl_instance.cpp @@ -0,0 +1,178 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" + +#include <iostream> +#include "jrnl_init_params.h" +#include "jrnl_instance.h" +#include "qpid/legacystore/jrnl/jdir.h" +#include "qpid/legacystore/jrnl/jerrno.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace mrg::jtt; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_jrnl_instance) + +const string test_filename("_ut_jrnl_instance"); +const char* tdp = getenv("TMP_DATA_DIR"); +const string test_dir(tdp && strlen(tdp) > 0 ? tdp : "/var/tmp/JttTest"); + +QPID_AUTO_TEST_CASE(constructor_1) +{ + cout << test_filename << ".constructor_1: " << flush; + const string jid = "jid1"; + const string jdir = test_dir + "/test1"; + const string bfn = "test"; + const u_int16_t num_jfiles = 20; + const bool ae = false; + const u_int16_t ae_max_jfiles = 45; + const u_int32_t jfsize_sblks = 128; + + args a("a1"); + using mrg::jtt::test_case; + test_case::shared_ptr p(new test_case(1, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL, + "t1")); + jrnl_instance ji(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks); + ji.init_tc(p, &a); + ji.run_tc(); + ji.tc_wait_compl(); + try { jdir::verify_dir(jdir, bfn); } + catch (const jexception& e) { BOOST_ERROR(e.what()); } + jdir::delete_dir(jdir); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_2) +{ + cout << test_filename << ".constructor_2: " << flush; + const string jid = "jid2"; + const string jdir = test_dir + "/test2"; + const string bfn = "test"; + const u_int16_t num_jfiles = 20; + const bool ae = false; + const u_int16_t ae_max_jfiles = 45; + const u_int32_t jfsize_sblks = 128; + + args a("a2"); + using mrg::jtt::test_case; + test_case::shared_ptr p(new test_case(2, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL, + "t2")); + jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks)); + jrnl_instance ji(jpp); + ji.init_tc(p, &a); + ji.run_tc(); + ji.tc_wait_compl(); + try { jdir::verify_dir(jdir, bfn); } + catch (const jexception& e) { BOOST_ERROR(e.what()); } + jdir::delete_dir(jdir); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_3) +{ + cout << test_filename << ".constructor_3: " << flush; + const string jid = "jid3"; + const string jdir = test_dir + "/test3"; + const string bfn = "test"; + const u_int16_t num_jfiles = 20; + const bool ae = false; + const u_int16_t ae_max_jfiles = 45; + const u_int32_t jfsize_sblks = 128; + + args a("a3"); + using mrg::jtt::test_case; + test_case::shared_ptr p(new test_case(3, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL, + "t3")); + jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks)); + jrnl_instance ji(jpp); + ji.init_tc(p, &a); + ji.run_tc(); + ji.tc_wait_compl(); + try { jdir::verify_dir(jdir, bfn); } + catch (const jexception& e) { BOOST_ERROR(e.what()); } + jdir::delete_dir(jdir); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(recover) +{ + cout << test_filename << ".recover: " << flush; + const string jid = "jid5"; + const string jdir = test_dir + "/test5"; + const string bfn = "test"; + const u_int16_t num_jfiles = 20; + const bool ae = false; + const u_int16_t ae_max_jfiles = 0; + const u_int32_t jfsize_sblks = 128; + + args a("a4"); + using mrg::jtt::test_case; + test_case::shared_ptr p(new test_case(5, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL, + "t5")); + jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks)); + jrnl_instance ji(jpp); + ji.init_tc(p, &a); + ji.run_tc(); + ji.tc_wait_compl(); + try { jdir::verify_dir(jdir, bfn); } + catch (const jexception& e) { BOOST_ERROR(e.what()); } + a.recover_mode = true; + ji.init_tc(p, &a); + ji.run_tc(); + ji.tc_wait_compl(); + try { jdir::verify_dir(jdir, bfn); } + catch (const jexception& e) { BOOST_ERROR(e.what()); } + jdir::delete_dir(jdir); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(recover_no_files) +{ + cout << test_filename << ".recover_no_files: " << flush; + const string jid = "jid6"; + const string jdir = test_dir + "/test6"; + const string bfn = "test"; + const u_int16_t num_jfiles = 20; + const bool ae = false; + const u_int16_t ae_max_jfiles = 0; + const u_int32_t jfsize_sblks = 128; + + args a("a5"); + a.recover_mode = true; + using mrg::jtt::test_case; + test_case::shared_ptr p(new test_case(6, 0, 0, 0, false, 0, 0, test_case::JTT_PERSISTNET, test_case::JDL_INTERNAL, + "t6")); + jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid, jdir, bfn, num_jfiles, ae, ae_max_jfiles, jfsize_sblks)); + jrnl_instance ji(jpp); + ji.init_tc(p, &a); + ji.run_tc(); + ji.tc_wait_compl(); + try { jdir::verify_dir(jdir, bfn); } + catch (const jexception& e) { BOOST_ERROR(e.what()); } + jdir::delete_dir(jdir); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() + diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_read_arg.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_read_arg.cpp new file mode 100644 index 0000000000..0d2025270d --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_read_arg.cpp @@ -0,0 +1,146 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" +#include <boost/test/unit_test_log.hpp> +#include "read_arg.h" +#include <iostream> + +#include <boost/program_options.hpp> +namespace po = boost::program_options; +using namespace mrg::jtt; +using namespace boost::unit_test; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_read_arg) + +const string test_filename("_ut_read_arg"); + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + read_arg ra1; + BOOST_CHECK_EQUAL(ra1.val(), read_arg::NONE); + BOOST_CHECK_EQUAL(ra1.str(), "NONE"); + read_arg ra2(read_arg::NONE); + BOOST_CHECK_EQUAL(ra2.val(), read_arg::NONE); + BOOST_CHECK_EQUAL(ra2.str(), "NONE"); + read_arg ra3(read_arg::ALL); + BOOST_CHECK_EQUAL(ra3.val(), read_arg::ALL); + BOOST_CHECK_EQUAL(ra3.str(), "ALL"); + read_arg ra4(read_arg::RANDOM); + BOOST_CHECK_EQUAL(ra4.val(), read_arg::RANDOM); + BOOST_CHECK_EQUAL(ra4.str(), "RANDOM"); + read_arg ra5(read_arg::LAZYLOAD); + BOOST_CHECK_EQUAL(ra5.val(), read_arg::LAZYLOAD); + BOOST_CHECK_EQUAL(ra5.str(), "LAZYLOAD"); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(set_val) +{ + cout << test_filename << ".set_val: " << flush; + read_arg ra; + BOOST_CHECK_EQUAL(ra.val(), read_arg::NONE); + BOOST_CHECK_EQUAL(ra.str(), "NONE"); + ra.set_val(read_arg::ALL); + BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL); + BOOST_CHECK_EQUAL(ra.str(), "ALL"); + ra.set_val(read_arg::RANDOM); + BOOST_CHECK_EQUAL(ra.val(), read_arg::RANDOM); + BOOST_CHECK_EQUAL(ra.str(), "RANDOM"); + ra.set_val(read_arg::LAZYLOAD); + BOOST_CHECK_EQUAL(ra.val(), read_arg::LAZYLOAD); + BOOST_CHECK_EQUAL(ra.str(), "LAZYLOAD"); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(parse) +{ + cout << test_filename << ".parse: " << flush; + read_arg ra; + ra.parse("LAZYLOAD"); + BOOST_CHECK_EQUAL(ra.val(), read_arg::LAZYLOAD); + BOOST_CHECK_EQUAL(ra.str(), "LAZYLOAD"); + ra.parse("ALL"); + BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL); + BOOST_CHECK_EQUAL(ra.str(), "ALL"); + BOOST_CHECK_THROW(ra.parse(""), po::invalid_option_value) + BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL); + BOOST_CHECK_EQUAL(ra.str(), "ALL"); + BOOST_CHECK_THROW(ra.parse("abc123"), po::invalid_option_value) + BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL); + BOOST_CHECK_EQUAL(ra.str(), "ALL"); + ra.parse("NONE"); + BOOST_CHECK_EQUAL(ra.val(), read_arg::NONE); + BOOST_CHECK_EQUAL(ra.str(), "NONE"); + ra.parse("RANDOM"); + BOOST_CHECK_EQUAL(ra.val(), read_arg::RANDOM); + BOOST_CHECK_EQUAL(ra.str(), "RANDOM"); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(istream_) +{ + cout << test_filename << ".istream_: " << flush; + read_arg ra; + istringstream ss1("LAZYLOAD", ios::in); + ss1 >> ra; + BOOST_CHECK_EQUAL(ra.val(), read_arg::LAZYLOAD); + BOOST_CHECK_EQUAL(ra.str(), "LAZYLOAD"); + istringstream ss2("ALL", ios::in); + ss2 >> ra; + BOOST_CHECK_EQUAL(ra.val(), read_arg::ALL); + BOOST_CHECK_EQUAL(ra.str(), "ALL"); + istringstream ss3("NONE", ios::in); + ss3 >> ra; + BOOST_CHECK_EQUAL(ra.val(), read_arg::NONE); + BOOST_CHECK_EQUAL(ra.str(), "NONE"); + istringstream ss4("RANDOM", ios::in); + ss4 >> ra; + BOOST_CHECK_EQUAL(ra.val(), read_arg::RANDOM); + BOOST_CHECK_EQUAL(ra.str(), "RANDOM"); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(ostream_) +{ + cout << test_filename << ".ostream_: " << flush; + ostringstream s1; + read_arg ra(read_arg::LAZYLOAD); + s1 << ra; + BOOST_CHECK_EQUAL(s1.str(), "LAZYLOAD"); + ra.set_val(read_arg::ALL); + ostringstream s2; + s2 << ra; + BOOST_CHECK_EQUAL(s2.str(), "ALL"); + ra.set_val(read_arg::NONE); + ostringstream s3; + s3 << ra; + BOOST_CHECK_EQUAL(s3.str(), "NONE"); + ra.set_val(read_arg::RANDOM); + ostringstream s4; + s4 << ra; + BOOST_CHECK_EQUAL(s4.str(), "RANDOM"); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case.cpp new file mode 100644 index 0000000000..3a7d0f951c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case.cpp @@ -0,0 +1,113 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" +#include <cstddef> +#include <iomanip> +#include <iostream> +#include "test_case.h" +#include "test_case_result.h" + +using namespace boost::unit_test; +using namespace mrg::jtt; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_test_case) + +const string test_filename("_ut_test_case"); + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + const unsigned test_case_num = 0x12345; + const u_int32_t num_msgs = 0x100; + const std::size_t min_data_size = 0x1000; + const std::size_t max_data_size = 0; + const bool auto_deq = true; + const std::size_t min_xid_size = 0x200; + const std::size_t max_xid_size = 0x200; + using mrg::jtt::test_case; + const test_case::transient_t transient = test_case::JTT_PERSISTNET; + const test_case::external_t external = test_case::JDL_INTERNAL; + const string comment = "This is a test"; + + test_case tc(test_case_num, num_msgs, min_data_size, max_data_size, auto_deq, + min_xid_size, max_xid_size, transient, external, comment); + BOOST_CHECK_EQUAL(tc.test_case_num(), test_case_num); + BOOST_CHECK_EQUAL(tc.num_msgs(), num_msgs); + BOOST_CHECK_EQUAL(tc.min_data_size(), min_data_size); + BOOST_CHECK_EQUAL(tc.max_data_size(), max_data_size); + BOOST_CHECK_EQUAL(tc.auto_deq(), auto_deq); + BOOST_CHECK_EQUAL(tc.min_xid_size(), min_xid_size); + BOOST_CHECK_EQUAL(tc.max_xid_size(), max_xid_size); + BOOST_CHECK_EQUAL(tc.transient(), transient); + BOOST_CHECK_EQUAL(tc.external(), external); + BOOST_CHECK_EQUAL(tc.comment(), comment); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(results) +{ + cout << test_filename << ".results: " << flush; + const unsigned test_case_num = 0x12345; + const u_int32_t num_msgs = 0x100; + const std::size_t min_data_size = 0x1000; + const std::size_t max_data_size = 0; + const bool auto_deq = true; + const std::size_t min_xid_size = 0x200; + const std::size_t max_xid_size = 0x200; + using mrg::jtt::test_case; + const test_case::transient_t transient = test_case::JTT_PERSISTNET; + const test_case::external_t external = test_case::JDL_INTERNAL; + const string comment = "This is a test"; + const unsigned num_results = 20; + + test_case tc(test_case_num, num_msgs, min_data_size, max_data_size, auto_deq, + min_xid_size, max_xid_size, transient, external, comment); + for (unsigned i=0; i<num_results; i++) + { + ostringstream oss; + oss << "JID_" << setfill('0') << setw(2) << i; + test_case_result::shared_ptr p(new test_case_result(oss.str())); + tc.add_result(p); + } + BOOST_CHECK_EQUAL(tc.num_results(), num_results); + test_case_result_agregation ave = tc.average(); + unsigned i=0; + for (test_case_result_agregation::tcrp_list_citr j=ave.rlist_begin(); j!=ave.rlist_end(); + i++,j++) + { + ostringstream oss; + oss << "JID_" << setfill('0') << setw(2) << i; + BOOST_CHECK_EQUAL((*j)->jid(), oss.str()); + } + for (unsigned i=0; i<num_results; i++) + { + ostringstream oss; + oss << "JID_" << setfill('0') << setw(2) << i; + BOOST_CHECK_EQUAL(ave[i]->jid(), oss.str()); + } + tc.clear(); + BOOST_CHECK_EQUAL(tc.num_results(), unsigned(0)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result.cpp new file mode 100644 index 0000000000..dd83dbee69 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result.cpp @@ -0,0 +1,206 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" + +#include <iostream> +#include "qpid/legacystore/jrnl/jexception.h" +#include "test_case_result.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace mrg::jtt; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_test_case_result) + +const string test_filename("_ut_test_case_result"); + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + const string jid("journal id 1"); + test_case_result tcr(jid); + BOOST_CHECK_EQUAL(tcr.jid(), jid); + BOOST_CHECK_EQUAL(tcr.exception(), false); + BOOST_CHECK_EQUAL(tcr.exception_count(), 0U); + const time_ns& ts1 = tcr.start_time(); + BOOST_CHECK(ts1.is_zero()); + const time_ns& ts2 = tcr.stop_time(); + BOOST_CHECK(ts2.is_zero()); + const time_ns& ts3 = tcr.test_time(); + BOOST_CHECK(ts3.is_zero()); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(start_stop) +{ + cout << test_filename << ".start_stop: " << flush; + const string jid("journal id 2"); + test_case_result tcr(jid); + BOOST_CHECK_EQUAL(tcr.exception(), false); + BOOST_CHECK_EQUAL(tcr.exception_count(), 0U); + const time_ns& ts1 = tcr.start_time(); + BOOST_CHECK(ts1.is_zero()); + const time_ns& ts2 = tcr.stop_time(); + BOOST_CHECK(ts2.is_zero()); + const time_ns& ts3 = tcr.test_time(); + BOOST_CHECK(ts3.is_zero()); + + tcr.set_start_time(); + BOOST_CHECK_EQUAL(tcr.exception(), false); + BOOST_CHECK_EQUAL(tcr.exception_count(), 0U); + const time_ns& ts4 = tcr.start_time(); + BOOST_CHECK(!ts4.is_zero()); + const time_ns& ts5 = tcr.stop_time(); + BOOST_CHECK(ts5.is_zero()); + const time_ns& ts6 = tcr.test_time(); + BOOST_CHECK(ts6.is_zero()); + + ::usleep(1100000); // 1.1 sec in microseconds + tcr.set_stop_time(); + BOOST_CHECK_EQUAL(tcr.exception(), false); + BOOST_CHECK_EQUAL(tcr.exception_count(), 0U); + const time_ns& ts7 = tcr.stop_time(); + BOOST_CHECK(!ts7.is_zero()); + const time_ns& ts8 = tcr.test_time(); + BOOST_CHECK(ts8.tv_sec == 1); + BOOST_CHECK(ts8.tv_nsec > 100000000); // 0.1 sec in nanoseconds + BOOST_CHECK(ts8.tv_nsec < 200000000); // 0.2 sec in nanoseconds + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(start_exception_stop_1) +{ + cout << test_filename << ".start_exception_stop_1: " << flush; + const string jid("journal id 3"); + test_case_result tcr(jid); + const u_int32_t err_code = 0x321; + const string err_msg = "exception message"; + const jexception e(err_code, err_msg); + tcr.set_start_time(); + ::usleep(1100000); // 1.1 sec in microseconds + tcr.add_exception(e); + BOOST_CHECK_EQUAL(tcr.exception(), true); + BOOST_CHECK_EQUAL(tcr.exception_count(), 1U); + BOOST_CHECK_EQUAL(tcr[0], e.what()); + const time_ns& ts1 = tcr.stop_time(); + BOOST_CHECK(!ts1.is_zero()); + const time_ns& ts2 = tcr.test_time(); + BOOST_CHECK(ts2.tv_sec == 1); + BOOST_CHECK(ts2.tv_nsec > 100000000); // 0.1 sec in nanoseconds + BOOST_CHECK(ts2.tv_nsec < 200000000); // 0.2 sec in nanoseconds + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(start_exception_stop_2) +{ + cout << test_filename << ".start_exception_stop_2: " << flush; + const string jid("journal id 4"); + test_case_result tcr(jid); + const string err_msg = "exception message"; + tcr.set_start_time(); + ::usleep(1100000); // 1.1 sec in microseconds + tcr.add_exception(err_msg); + BOOST_CHECK_EQUAL(tcr.exception(), true); + BOOST_CHECK_EQUAL(tcr.exception_count(), 1U); + BOOST_CHECK_EQUAL(tcr[0], err_msg); + const time_ns& ts1 = tcr.stop_time(); + BOOST_CHECK(!ts1.is_zero()); + const time_ns& ts2 = tcr.test_time(); + BOOST_CHECK(ts2.tv_sec == 1); + BOOST_CHECK(ts2.tv_nsec > 100000000); // 0.1 sec in nanoseconds + BOOST_CHECK(ts2.tv_nsec < 200000000); // 0.2 sec in nanoseconds + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(start_exception_stop_3) +{ + cout << test_filename << ".start_exception_stop_3: " << flush; + const string jid("journal id 5"); + test_case_result tcr(jid); + const char* err_msg = "exception message"; + tcr.set_start_time(); + ::usleep(1100000); // 1.1 sec in microseconds + tcr.add_exception(err_msg); + BOOST_CHECK_EQUAL(tcr.exception(), true); + BOOST_CHECK_EQUAL(tcr.exception_count(), 1U); + BOOST_CHECK_EQUAL(tcr[0], err_msg); + const time_ns& ts1 = tcr.stop_time(); + BOOST_CHECK(!ts1.is_zero()); + const time_ns& ts2 = tcr.test_time(); + BOOST_CHECK(ts2.tv_sec == 1); + BOOST_CHECK(ts2.tv_nsec > 100000000); // 0.1 sec in nanoseconds + BOOST_CHECK(ts2.tv_nsec < 200000000); // 0.2 sec in nanoseconds + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(start_exception) +{ + cout << test_filename << ".start_exception: " << flush; + const string jid("journal id 6"); + test_case_result tcr(jid); + u_int32_t err_code = 0x654; + const string err_msg = "exception message"; + const jexception e(err_code, err_msg); + tcr.set_start_time(); + ::usleep(1100000); // 1.1 sec in microseconds + tcr.add_exception(e, false); + BOOST_CHECK_EQUAL(tcr.exception(), true); + BOOST_CHECK_EQUAL(tcr.exception_count(), 1U); + BOOST_CHECK_EQUAL(tcr[0], e.what()); + const time_ns& ts1 = tcr.stop_time(); + BOOST_CHECK(ts1.is_zero()); + const time_ns& ts2 = tcr.test_time(); + BOOST_CHECK(ts2.is_zero()); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(counters) +{ + cout << test_filename << ".counters: " << flush; + const u_int32_t num_enq = 125; + const u_int32_t num_deq = 64; + const u_int32_t num_read = 22; + const string jid("journal id 7"); + test_case_result tcr(jid); + BOOST_CHECK_EQUAL(tcr.num_enq(), u_int32_t(0)); + BOOST_CHECK_EQUAL(tcr.num_deq(), u_int32_t(0)); + BOOST_CHECK_EQUAL(tcr.num_read(), u_int32_t(0)); + for (unsigned i=0; i<num_enq; i++) + tcr.incr_num_enq(); + BOOST_CHECK_EQUAL(tcr.num_enq(), num_enq); + BOOST_CHECK_EQUAL(tcr.num_deq(), u_int32_t(0)); + BOOST_CHECK_EQUAL(tcr.num_read(), u_int32_t(0)); + for (unsigned j=0; j<num_deq; j++) + tcr.incr_num_deq(); + BOOST_CHECK_EQUAL(tcr.num_enq(), num_enq); + BOOST_CHECK_EQUAL(tcr.num_deq(), num_deq); + BOOST_CHECK_EQUAL(tcr.num_read(), u_int32_t(0)); + for (unsigned k=0; k<num_read; k++) + tcr.incr_num_read(); + BOOST_CHECK_EQUAL(tcr.num_enq(), num_enq); + BOOST_CHECK_EQUAL(tcr.num_deq(), num_deq); + BOOST_CHECK_EQUAL(tcr.num_read(), num_read); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result_agregation.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result_agregation.cpp new file mode 100644 index 0000000000..aa01bf833d --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_result_agregation.cpp @@ -0,0 +1,178 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" +#include <ctime> +#include <iostream> +#include "test_case_result_agregation.h" + +using namespace boost::unit_test; +using namespace mrg::journal; +using namespace mrg::jtt; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_test_case_result_agregation) + +const string test_filename("_ut_test_case_result_agregation"); + +// === Helper functions === + +void check_agregate(const test_case_result_agregation& tcra, const u_int32_t num_enq, + const u_int32_t num_deq, const u_int32_t num_reads, const u_int32_t num_results, + const u_int32_t num_exceptions, const std::time_t secs, const long nsec) +{ + BOOST_CHECK_EQUAL(tcra.num_enq(), num_enq); + BOOST_CHECK_EQUAL(tcra.num_deq(), num_deq); + BOOST_CHECK_EQUAL(tcra.num_read(), num_reads); + BOOST_CHECK_EQUAL(tcra.num_results(), num_results); + BOOST_CHECK_EQUAL(tcra.exception_count(), num_exceptions); + BOOST_CHECK_EQUAL(tcra.exception(), num_exceptions > 0); + const time_ns& ts1 = tcra.test_time(); + BOOST_CHECK_EQUAL(ts1.tv_sec, secs); + BOOST_CHECK_EQUAL(ts1.tv_nsec, nsec); +} + +test_case_result::shared_ptr make_result(const string& jid, const u_int32_t num_enq, + const u_int32_t num_deq, const u_int32_t num_reads, const std::time_t secs, const long nsec) +{ + test_case_result::shared_ptr tcrp(new test_case_result(jid)); + for (unsigned i=0; i<num_enq; i++) + tcrp->incr_num_enq(); + for (unsigned i=0; i<num_deq; i++) + tcrp->incr_num_deq(); + for (unsigned i=0; i<num_reads; i++) + tcrp->incr_num_read(); + time_ns ts(secs, nsec); + tcrp->set_test_time(ts); + return tcrp; +} + +// === Test suite === + +QPID_AUTO_TEST_CASE(constructor_1) +{ + cout << test_filename << ".constructor_1: " << flush; + test_case_result_agregation tcra; + BOOST_CHECK_EQUAL(tcra.tc_average_mode(), true); + BOOST_CHECK_EQUAL(tcra.jid(), "Average"); + BOOST_CHECK_EQUAL(tcra.exception(), false); + BOOST_CHECK_EQUAL(tcra.exception_count(), 0U); + const time_ns& ts1 = tcra.start_time(); + BOOST_CHECK(ts1.is_zero()); + const time_ns& ts2 = tcra.stop_time(); + BOOST_CHECK(ts2.is_zero()); + const time_ns& ts3 = tcra.test_time(); + BOOST_CHECK(ts3.is_zero()); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(constructor_2) +{ + cout << test_filename << ".constructor_2: " << flush; + string jid("journal id"); + test_case_result_agregation tcra(jid); + BOOST_CHECK_EQUAL(tcra.tc_average_mode(), false); + BOOST_CHECK_EQUAL(tcra.jid(), jid); + BOOST_CHECK_EQUAL(tcra.exception(), false); + BOOST_CHECK_EQUAL(tcra.exception_count(), 0U); + const time_ns& ts1 = tcra.start_time(); + BOOST_CHECK(ts1.is_zero()); + const time_ns& ts2 = tcra.stop_time(); + BOOST_CHECK(ts2.is_zero()); + const time_ns& ts3 = tcra.test_time(); + BOOST_CHECK(ts3.is_zero()); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(add_test_case) +{ + cout << test_filename << ".add_test_case: " << flush; + string jid("jid1"); + test_case_result::shared_ptr tcrp1 = make_result("jid1", 10, 10, 0, 1, 101010101L); + test_case_result::shared_ptr tcrp2 = make_result("jid1", 25, 0, 35, 10, 20202020L); + test_case_result::shared_ptr tcrp3 = make_result("jid1", 0, 15, 5, 2, 555555555L); + test_case_result::shared_ptr tcrp4 = make_result("jid2", 100, 100, 100, 100, 323232324L); + test_case_result::shared_ptr tcrp5 = make_result("jid1", 5, 0, 0, 0, 100L); + tcrp5->add_exception(string("error 1"), false); + test_case_result::shared_ptr tcrp6 = make_result("jid3", 0, 5, 0, 0, 100L); + jexception e(0x123, "exception 2"); + tcrp6->add_exception(e, false); + test_case_result::shared_ptr tcrp7 = make_result("jid1", 0, 0, 0, 0, 0L); + test_case_result::shared_ptr tcrp8 = make_result("jid1", 200, 100, 300, 12, 323232224L); + + test_case_result_agregation tcra(jid); + check_agregate(tcra, 0, 0, 0, 0, 0, 0, 0L); + tcra.add_test_result(tcrp1); + check_agregate(tcra, 10, 10, 0, 1, 0, 1, 101010101L); + tcra.add_test_result(tcrp2); + check_agregate(tcra, 35, 10, 35, 2, 0, 11, 121212121L); + tcra.add_test_result(tcrp3); + check_agregate(tcra, 35, 25, 40, 3, 0, 13, 676767676L); + tcra.add_test_result(tcrp4); + check_agregate(tcra, 35, 25, 40, 3, 0, 13, 676767676L); + tcra.add_test_result(tcrp5); + check_agregate(tcra, 40, 25, 40, 4, 1, 13, 676767776L); + tcra.add_test_result(tcrp6); + check_agregate(tcra, 40, 25, 40, 4, 1, 13, 676767776L); + tcra.add_test_result(tcrp7); + check_agregate(tcra, 40, 25, 40, 5, 1, 13, 676767776L); + tcra.add_test_result(tcrp8); + check_agregate(tcra, 240, 125, 340, 6, 1, 26, 0L); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(add_test_case_average) +{ + cout << test_filename << ".add_test_case_average: " << flush; + test_case_result::shared_ptr tcrp1 = make_result("jid1", 10, 10, 0, 1, 101010101L); + test_case_result::shared_ptr tcrp2 = make_result("jid2", 25, 0, 35, 10, 20202020L); + test_case_result::shared_ptr tcrp3 = make_result("jid3", 0, 15, 5, 2, 555555555L); + test_case_result::shared_ptr tcrp4 = make_result("jid4", 100, 100, 100, 100, 323232324L); + test_case_result::shared_ptr tcrp5 = make_result("jid5", 5, 0, 0, 0, 100L); + tcrp5->add_exception(string("error 1"), false); + test_case_result::shared_ptr tcrp6 = make_result("jid6", 0, 5, 0, 0, 100L); + jexception e(0x123, "exception 2"); + tcrp6->add_exception(e, false); + test_case_result::shared_ptr tcrp7 = make_result("jid7", 0, 0, 0, 0, 0L); + test_case_result::shared_ptr tcrp8 = make_result("jid8", 200, 100, 300, 12, 222222022L); + + test_case_result_agregation tcra; + check_agregate(tcra, 0, 0, 0, 0, 0, 0, 0L); + tcra.add_test_result(tcrp1); + check_agregate(tcra, 10, 10, 0, 1, 0, 1, 101010101L); + tcra.add_test_result(tcrp2); + check_agregate(tcra, 35, 10, 35, 2, 0, 11, 121212121L); + tcra.add_test_result(tcrp3); + check_agregate(tcra, 35, 25, 40, 3, 0, 13, 676767676L); + tcra.add_test_result(tcrp4); + check_agregate(tcra, 135, 125, 140, 4, 0, 114, 0L); + tcra.add_test_result(tcrp5); + check_agregate(tcra, 140, 125, 140, 5, 1, 114, 100L); + tcra.add_test_result(tcrp6); + check_agregate(tcra, 140, 130, 140, 6, 2, 114, 200L); + tcra.add_test_result(tcrp7); + check_agregate(tcra, 140, 130, 140, 7, 2, 114, 200L); + tcra.add_test_result(tcrp8); + check_agregate(tcra, 340, 230, 440, 8, 2, 126, 222222222L); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.cpp new file mode 100644 index 0000000000..adbdf6884b --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.cpp @@ -0,0 +1,147 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "../../unit_test.h" +#include <cstddef> +#include <iostream> +#include <sys/stat.h> +#include "test_case.h" +#include "test_case_set.h" + +using namespace boost::unit_test; +using namespace mrg::jtt; +using namespace std; + +QPID_AUTO_TEST_SUITE(jtt_test_case_set) + +const string csv_file("_ut_test_case_set.csv"); +const string test_filename("_ut_test_case_set"); + +// === Helper functions === + +bool check_csv_file(const char* filename) +{ + struct stat s; + if (::stat(filename, &s)) + return false; + if (S_ISREG(s.st_mode)) + return true; + return false; +} + +// === Test suite === + +QPID_AUTO_TEST_CASE(constructor) +{ + cout << test_filename << ".constructor: " << flush; + test_case_set tcs; + BOOST_CHECK(tcs.empty()); + BOOST_CHECK_EQUAL(tcs.size(), unsigned(0)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(append_1) +{ + cout << test_filename << ".append_1: " << flush; + const unsigned test_case_num = 0x12345; + const u_int32_t num_msgs = 0x100; + const std::size_t min_data_size = 0x1000; + const std::size_t max_data_size = 0; + const bool auto_deq = true; + const std::size_t min_xid_size = 0x200; + const std::size_t max_xid_size = 0x200; + using mrg::jtt::test_case; + const test_case::transient_t transient = test_case::JTT_PERSISTNET; + const test_case::external_t external = test_case::JDL_INTERNAL; + const string comment = "This is a test"; + + test_case_set tcs; + tcs.append(test_case_num, num_msgs, min_data_size, max_data_size, auto_deq, min_xid_size, + max_xid_size, transient, external, comment); + BOOST_CHECK(!tcs.empty()); + BOOST_CHECK_EQUAL(tcs.size(), unsigned(1)); + test_case::shared_ptr tcp = tcs[0]; + BOOST_CHECK_EQUAL(tcp->test_case_num(), test_case_num); + BOOST_CHECK_EQUAL(tcp->num_msgs(), num_msgs); + BOOST_CHECK_EQUAL(tcp->min_data_size(), min_data_size); + BOOST_CHECK_EQUAL(tcp->max_data_size(), max_data_size); + BOOST_CHECK_EQUAL(tcp->min_xid_size(), min_xid_size); + BOOST_CHECK_EQUAL(tcp->max_xid_size(), max_xid_size); + BOOST_CHECK_EQUAL(tcp->transient(), transient); + BOOST_CHECK_EQUAL(tcp->external(), external); + BOOST_CHECK_EQUAL(tcp->comment(), comment); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(append_2) +{ + cout << test_filename << ".append_2: " << flush; + const unsigned test_case_num = 0x12345; + const u_int32_t num_msgs = 0x100; + const std::size_t min_data_size = 0x1000; + const std::size_t max_data_size = 0; + const bool auto_deq = true; + const std::size_t min_xid_size = 0x200; + const std::size_t max_xid_size = 0x200; + using mrg::jtt::test_case; + const test_case::transient_t transient = test_case::JTT_PERSISTNET; + const test_case::external_t external = test_case::JDL_INTERNAL; + const string comment = "This is a test"; + + test_case::shared_ptr tcp(new test_case(test_case_num, num_msgs, min_data_size, max_data_size, + auto_deq, min_xid_size, max_xid_size, transient, external, comment)); + test_case_set tcs; + tcs.append(tcp); + BOOST_CHECK(!tcs.empty()); + BOOST_CHECK_EQUAL(tcs.size(), unsigned(1)); + tcp = tcs[0]; + BOOST_CHECK_EQUAL(tcp->test_case_num(), test_case_num); + BOOST_CHECK_EQUAL(tcp->num_msgs(), num_msgs); + BOOST_CHECK_EQUAL(tcp->min_data_size(), min_data_size); + BOOST_CHECK_EQUAL(tcp->max_data_size(), max_data_size); + BOOST_CHECK_EQUAL(tcp->min_xid_size(), min_xid_size); + BOOST_CHECK_EQUAL(tcp->max_xid_size(), max_xid_size); + BOOST_CHECK_EQUAL(tcp->transient(), transient); + BOOST_CHECK_EQUAL(tcp->external(), external); + BOOST_CHECK_EQUAL(tcp->comment(), comment); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_CASE(append_from_csv) +{ + cout << test_filename << ".append_from_csv: " << flush; + test_case_set tcs; + BOOST_REQUIRE_MESSAGE(check_csv_file(csv_file.c_str()), "Test CSV file \"" << csv_file << + "\" is missing."); + tcs.append_from_csv(csv_file, false); + BOOST_CHECK(!tcs.empty()); + BOOST_CHECK_EQUAL(tcs.size(), unsigned(44)); + BOOST_CHECK_EQUAL(tcs.ignored(), unsigned(0)); + tcs.clear(); + BOOST_CHECK(tcs.empty()); + tcs.append_from_csv(csv_file, true); + BOOST_CHECK(!tcs.empty()); + BOOST_CHECK_EQUAL(tcs.size(), unsigned(18)); + BOOST_CHECK_EQUAL(tcs.ignored(), unsigned(26)); + cout << "ok" << endl; +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.csv b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.csv new file mode 100644 index 0000000000..f886186275 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/_ut_test_case_set.csv @@ -0,0 +1,74 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +,,,,,,,"Msg size",,"Xid size",,,,,"enq-size",,"deq-size",,"txn-size",, +"Col. 0","1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" +"Test #","tf","pf","amn","mn incr","#msgs","ms incr","Min","Max","Min","Max","auto-deq","transient","extern","bytes","dblks","bytes","dblks","bytes","dblks","comment" +,,,,,,,,,,,,,,,,,,,, +"Initialize only",,,,,,,,,,,,,,,,,,,, +0,"L",0,0,0,0,0,0,0,0,0,FALSE,FALSE,FALSE,44,1,0,0,0,0,"No messages - journal creation/initialization only" +,,,,,,,,,,,,,,,,,,,, +"Simple message combinations of persistent/deq transientueued/non-dequeued, transactional/non-transactional",,,,,,,,,,,,,,,,,,,, +1,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"1 * 10-byte message" +2,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"10 * 10-byte message" +3,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"1 * 10-byte message [transient]" +4,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"10 * 10-byte message [transient]" +5,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn]" +6,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn]" +7,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn transient]" +8,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn transient]" +9,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq]" +10,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq]" +11,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq transient]" +12,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq transient]" +13,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [deq txn]" +14,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [deq txn]" +15,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient]" +16,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient]" +17,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [extern]" +18,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [extern]" +19,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [transient extern]" +20,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [transient extern]" +21,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn extern]" +22,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn extern]" +23,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn transient extern]" +24,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn transient extern]" +25,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq extern]" +26,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq extern]" +27,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq transient extern]" +28,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq transient extern]" +29,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [deq txn extern]" +30,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [deq txn extern]" +31,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient extern]" +32,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient extern]" +,,,,,,,,,,,,,,,,,,,, +"High volume tests of random message lengths - RHM_WRONLY req'd for auto-dequeue == FALSE",,,,,,,,,,,,,,,,,,,, +33,"M",1,5000000,0,5000000,0,0,100,1,100,FALSE,RANDOM,RANDOM,244,2,0,0,0,0,"100 bytes xid max + 100 bytes data max [txn]" +34,"M",3,3000000,0,3000000,0,0,300,1,300,FALSE,RANDOM,RANDOM,644,6,0,0,0,0,"300 bytes xid max + 300 bytes data max [txn]" +35,"M",10,1600000,0,1600000,0,0,1000,1,1000,FALSE,RANDOM,RANDOM,2044,16,0,0,0,0,"1000 bytes xid max + 1000 bytes data max [txn]" +36,"M",30,600000,0,600000,0,0,3000,1,3000,FALSE,RANDOM,RANDOM,6044,48,0,0,0,0,"3000 bytes xid max + 3000 bytes data max [txn]" +37,"M",100,200000,0,200000,0,0,10000,1,10000,FALSE,RANDOM,RANDOM,20044,157,0,0,0,0,"10000 bytes xid max + 10000 bytes data max [txn]" +38,"M",300,60000,0,60000,0,0,30000,1,30000,FALSE,RANDOM,RANDOM,60044,470,0,0,0,0,"30000 bytes xid max + 30000 bytes data max [txn]" +39,"M",1000,20000,0,20000,0,0,100000,1,100000,FALSE,RANDOM,RANDOM,200044,1563,0,0,0,0,"100000 bytes xid max + 100000 bytes data max [txn]" +,,,,,,,,,,,,,,,,,,,, +"STANDARD PERFORMANCE BENCHMARK: 10,000,000 writes, data=212b (2 dblks)",,,,,,,,,,,,,,,,,,,, +40,"M",1,10000000,0,10000000,0,212,212,0,0,FALSE,FALSE,FALSE,256,2,0,0,0,0,"212 bytes data (2 dblks enq)" +41,"M",1,10000000,0,10000000,0,148,148,64,64,FALSE,FALSE,FALSE,256,2,0,0,0,0,"148 bytes data + 64 bytes xid (2 dblks enq)" +42,"M",1,10000000,0,10000000,0,212,212,0,0,TRUE,FALSE,FALSE,256,2,32,1,0,0,"212 bytes data (2 dblks enq + 1 dblk deq)" +43,"M",1,10000000,0,10000000,0,148,148,64,64,TRUE,FALSE,FALSE,256,2,108,1,100,1,"148 bytes data + 64 bytes xid (2 dblks enq + 1 dblks deq + 1 dblks txn)" diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.cpp new file mode 100644 index 0000000000..0f041c380e --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.cpp @@ -0,0 +1,226 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "args.h" + +#include <cstddef> +#include <iostream> + +namespace po = boost::program_options; + +namespace mrg +{ +namespace jtt +{ + +args::args(std::string opt_title): + _options_descr(opt_title), + format_chk(false), + keep_jrnls(false), + lld_rd_num(10), + lld_skip_num(100), + num_jrnls(1), + pause_secs(0), + randomize(false), + read_mode(), + read_prob(50), + recover_mode(false), + repeat_flag(false), + reuse_instance(false), + seed(0) +{ + _options_descr.add_options() + ("csv-file,c", + po::value<std::string>(&test_case_csv_file_name)->default_value("jtt.csv"), + "CSV file containing test cases.") + + ("format-chk", + po::value<bool>(&format_chk)->zero_tokens(), + "Check the format of each journal file.") + + ("help,h", "This help message.") + + ("jrnl-dir", + po::value<std::string>(&journal_dir)->default_value("/var/tmp/jtt"), + "Directory in which journal files will be placed.") + + ("keep-jrnls", + po::value<bool>(&keep_jrnls)->zero_tokens(), + "Keep all test journals.") + + ("lld-rd-num", + po::value<unsigned>(&lld_rd_num)->default_value(10), + "Number of consecutive messages to read after only dequeueing lld-skip-num " + "messages during lazy-loading. Ignored if read-mode is not set to LAZYLOAD.") + + ("lld-skip-num", + po::value<unsigned>(&lld_skip_num)->default_value(100), + "Number of consecutive messages to dequeue only (without reading) prior to " + "reading lld-rd-num messages. Ignored if read-mode is not set to LAZYLOAD.") + + ("num-jrnls", + po::value<unsigned>(&num_jrnls)->default_value(1), + "Number of simultaneous journal instances to test.") + + ("pause", + po::value<unsigned>(&pause_secs)->default_value(0), + "Pause in seconds between test cases (allows disk to catch up).") + + ("randomize", + po::value<bool>(&randomize)->zero_tokens(), + "Randomize the order of the tests.") + + ("read-mode", + po::value<read_arg>(&read_mode)->default_value(read_arg::NONE), + read_arg::descr().c_str()) + + ("read-prob", + po::value<unsigned>(&read_prob)->default_value(50), + "Read probability (percent) for each message when read-mode is set to RANDOM.") + + ("recover-mode", + po::value<bool>(&recover_mode)->zero_tokens(), + "Recover journal from the previous test for each test case.") + + ("repeat", + po::value<bool>(&repeat_flag)->zero_tokens(), + "Repeat all test cases indefinitely.") + + ("reuse-instance", + po::value<bool>(&reuse_instance)->zero_tokens(), + "Reuse journal instance for all test cases.") + + ("seed", + po::value<unsigned>(&seed)->default_value(0), + "Seed for use in random number generator.") + + ("analyzer", + po::value<std::string>(&jfile_analyzer)->default_value("./file_chk.py"), + "Journal file analyzer program to use when the --format-chk option is used, ignored otherwise.") + + ; +} + +bool +args::parse(int argc, char** argv) // return true if error, false if ok +{ + try + { + po::store(po::parse_command_line(argc, argv, _options_descr), _vmap); + po::notify(_vmap); + } + catch (const std::exception& e) + { + std::cout << "ERROR: " << e.what() << std::endl; + return usage(); + } + if (_vmap.count("help")) + return usage(); + if (num_jrnls == 0) + { + std::cout << "ERROR: num-jrnls must be 1 or more." << std::endl; + return usage(); + } + if (read_prob > 100) // read_prob is unsigned, so no need to check < 0 + { + std::cout << "ERROR: read-prob must be between 0 and 100 inclusive." << std::endl; + return usage(); + } + if (repeat_flag && keep_jrnls) + { + std::string resp; + std::cout << "WARNING: repeat and keep-jrnls: Monitor disk usage as test journals will" + " accumulate." << std::endl; + std::cout << "Continue? <y/n> "; + std::cin >> resp; + if (resp.size() == 1) + { + if (resp[0] != 'y' && resp[0] != 'Y') + return true; + } + else if (resp.size() == 3) // any combo of lower- and upper-case + { + if (resp[0] != 'y' && resp[0] != 'Y') + return true; + if (resp[1] != 'e' && resp[1] != 'E') + return true; + if (resp[2] != 's' && resp[2] != 'S') + return true; + } + else + return true; + } + return false; +} + +bool +args::usage() const +{ + std::cout << _options_descr << std::endl; + return true; +} + +void +args::print_args() const +{ + std::cout << "Number of journals: " << num_jrnls << std::endl; + std::cout << "Read mode: " << read_mode << std::endl; + if (read_mode.val() == read_arg::RANDOM) + std::cout << "Read probability: " << read_prob << " %" << std::endl; + if (read_mode.val() == read_arg::LAZYLOAD) + { + std::cout << "Lazy-load skips: " << lld_skip_num << std::endl; + std::cout << "Lazy-load reads: " << lld_rd_num << std::endl; + } + if (pause_secs) + std::cout << "Pause between test cases: " << pause_secs << " sec." << std::endl; + if (seed) + std::cout << "Randomize seed: " << seed << std::endl; + print_flags(); +} + +void +args::print_flags() const +{ + if (format_chk || keep_jrnls || randomize || recover_mode || repeat_flag || + reuse_instance) + { + std::cout << "Flag options:"; + // TODO: Get flag args and their strings directly from _options_descr. + if (format_chk) + std::cout << " format-chk"; + if (keep_jrnls) + std::cout << " keep-jrnls"; + if (randomize) + std::cout << " randomize"; + if (recover_mode) + std::cout << " recover-mode"; + if (repeat_flag) + std::cout << " repeat-flag"; + if (reuse_instance) + std::cout << " reuse-instance"; + std::cout << std::endl; + } + std::cout << std::endl; +} + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.h new file mode 100644 index 0000000000..b6f7fb4a79 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/args.h @@ -0,0 +1,66 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_args_hpp +#define mrg_jtt_args_hpp + +#include <boost/program_options.hpp> +#include "read_arg.h" + +namespace mrg +{ +namespace jtt +{ + + struct args + { + boost::program_options::options_description _options_descr; + boost::program_options::variables_map _vmap; + + // Add args here + std::string jfile_analyzer; + std::string test_case_csv_file_name; + std::string journal_dir; + bool format_chk; + bool keep_jrnls; + unsigned lld_rd_num; + unsigned lld_skip_num; + unsigned num_jrnls; + unsigned pause_secs; + bool randomize; + read_arg read_mode; + unsigned read_prob; + bool recover_mode; + bool repeat_flag; + bool reuse_instance; + unsigned seed; + + args(std::string opt_title); + bool parse(int argc, char** argv); // return true if error, false if ok + bool usage() const; // return true + void print_args() const; + void print_flags() const; + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_args_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.cpp new file mode 100644 index 0000000000..3530e0b223 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.cpp @@ -0,0 +1,87 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "data_src.h" + +#include <cstddef> +#include <iomanip> +#include <sstream> + +namespace mrg +{ +namespace jtt +{ + +char data_src::_data_src[data_src::max_dsize]; +char data_src::_xid_src[data_src::max_xsize]; +bool data_src::_initialized = data_src::__init(); +u_int64_t data_src::_xid_cnt = 0ULL; +mrg::journal::smutex data_src::_sm; + +data_src::data_src() +{} + +bool +data_src::__init() +{ + for (unsigned i=0; i<max_dsize; i++) + _data_src[i] = '0' + ((i + 1) % 10); // 123456789012345... + for (unsigned j=0; j<max_xsize; j++) + _xid_src[j] = 'a' + (j % 26); // abc...xyzabc... + return true; +} + +const char* +data_src::get_data(const std::size_t offs) +{ + if (offs >= max_dsize) return 0; + return _data_src + offs; +} + +std::string +data_src::get_xid(const std::size_t xid_size) +{ + if (xid_size == 0) + return ""; + std::ostringstream oss; + oss << std::setfill('0'); + if (xid_size < 9) + oss << std::setw(xid_size) << get_xid_cnt(); + else if (xid_size < 13) + oss << "xid:" << std::setw(xid_size - 4) << get_xid_cnt(); + else + { + oss << "xid:" << std::setw(8) << get_xid_cnt() << ":"; + oss.write(get_xid_content(13), xid_size - 13); + } + return oss.str(); +} + +const char* +data_src::get_xid_content(const std::size_t offs) +{ + if (offs >= max_xsize) return 0; + return _xid_src + offs; +} + +} // namespace jtt +} // namespace mrg + diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.h new file mode 100644 index 0000000000..66dc613787 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/data_src.h @@ -0,0 +1,66 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_data_src_hpp +#define mrg_jtt_data_src_hpp + +#include <cstddef> +#include "qpid/legacystore/jrnl/slock.h" +#include "qpid/legacystore/jrnl/smutex.h" +#include <pthread.h> +#include <string> +#include <sys/types.h> + +#define DATA_SIZE 1024 * 1024 +#define XID_SIZE 1024 * 1024 + +namespace mrg +{ +namespace jtt +{ + class data_src + { + public: + static const std::size_t max_dsize = DATA_SIZE; + static const std::size_t max_xsize = XID_SIZE; + + private: + static char _data_src[]; + static char _xid_src[]; + static u_int64_t _xid_cnt; + static bool _initialized; + static mrg::journal::smutex _sm; + + public: + static const char* get_data(const std::size_t offs); + static std::string get_xid(const std::size_t xid_size); + + private: + data_src(); + static u_int64_t get_xid_cnt() { mrg::journal::slock s(_sm); return _xid_cnt++; } + static const char* get_xid_content(const std::size_t offs); + static bool __init(); + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_data_src_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jfile_chk.py b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jfile_chk.py new file mode 100755 index 0000000000..36ef511f5c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jfile_chk.py @@ -0,0 +1,838 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import sys +import getopt +import string +import xml.parsers.expat +from struct import unpack, calcsize +from time import gmtime, strftime + +dblk_size = 128 +sblk_size = 4 * dblk_size +jfsize = None +hdr_ver = 1 + +TEST_NUM_COL = 0 +NUM_MSGS_COL = 5 +MIN_MSG_SIZE_COL = 7 +MAX_MSG_SIZE_COL = 8 +MIN_XID_SIZE_COL = 9 +MAX_XID_SIZE_COL = 10 +AUTO_DEQ_COL = 11 +TRANSIENT_COL = 12 +EXTERN_COL = 13 +COMMENT_COL = 20 + +owi_mask = 0x01 +transient_mask = 0x10 +extern_mask = 0x20 + +printchars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ' + + + +#== global functions =========================================================== + +def load(f, klass): + args = load_args(f, klass) + subclass = klass.discriminate(args) + result = subclass(*args) + if subclass != klass: + result.init(f, *load_args(f, subclass)) + result.skip(f) + return result; + +def load_args(f, klass): + size = calcsize(klass.format) + foffs = f.tell(), + bin = f.read(size) + if len(bin) != size: + raise Exception("end of file") + return foffs + unpack(klass.format, bin) + +def size_blks(size, blk_size): + return (size + blk_size - 1)/blk_size + +def rem_in_blk(f, blk_size): + foffs = f.tell() + return (size_blks(f.tell(), blk_size) * blk_size) - foffs; + +def file_full(f): + return f.tell() >= jfsize + +def isprintable(s): + return s.strip(printchars) == '' + +def print_xid(xidsize, xid): + if xid == None: + if xidsize > 0: + raise Exception('Inconsistent XID size: xidsize=%d, xid=None' % xidsize) + return '' + if isprintable(xid): + xidstr = split_str(xid) + else: + xidstr = hex_split_str(xid) + if xidsize != len(xid): + raise Exception('Inconsistent XID size: xidsize=%d, xid(%d)=\"%s\"' % (xidsize, len(xid), xidstr)) + return 'xid(%d)=\"%s\" ' % (xidsize, xidstr) + +def print_data(dsize, data): + if data == None: + return '' + if isprintable(data): + datastr = split_str(data) + else: + datastr = hex_split_str(data) + if dsize != len(data): + raise Exception('Inconsistent data size: dsize=%d, data(%d)=\"%s\"' % (dsize, len(data), datastr)) + return 'data(%d)=\"%s\" ' % (dsize, datastr) + +def hex_split_str(s, split_size = 50): + if len(s) <= split_size: + return hex_str(s, 0, len(s)) + if len(s) > split_size + 25: + return hex_str(s, 0, 10) + ' ... ' + hex_str(s, 55, 65) + ' ... ' + hex_str(s, len(s)-10, len(s)) + return hex_str(s, 0, 10) + ' ... ' + hex_str(s, len(s)-10, len(s)) + +def hex_str(s, b, e): + o = '' + for i in range(b, e): + if isprintable(s[i]): + o += s[i] + else: + o += '\\%02x' % ord(s[i]) + return o + +def split_str(s, split_size = 50): + if len(s) < split_size: + return s + return s[:25] + ' ... ' + s[-25:] + +def inv_str(s): + si = '' + for i in range(0,len(s)): + si += chr(~ord(s[i]) & 0xff) + return si + +def load_file_data(f, size, data): + if size == 0: + return (data, True) + if data == None: + loaded = 0 + else: + loaded = len(data) + foverflow = f.tell() + size - loaded > jfsize + if foverflow: + rsize = jfsize - f.tell() + else: + rsize = size - loaded + bin = f.read(rsize) + if data == None: + data = unpack('%ds' % (rsize), bin)[0] + else: + data = data + unpack('%ds' % (rsize), bin)[0] + return (data, not foverflow) + +def exit(code, qflag): + if code != 0 or not qflag: + print out.getvalue() + out.close() + sys.exit(code) + +#== class Sizeable ============================================================= + +class Sizeable: + + def size(self): + classes = [self.__class__] + + size = 0 + while classes: + cls = classes.pop() + if hasattr(cls, "format"): + size += calcsize(cls.format) + classes.extend(cls.__bases__) + + return size + + +#== class Hdr ================================================================== + +class Hdr(Sizeable): + + format = '=4sBBHQ' + + def discriminate(args): + return CLASSES.get(args[1][-1], Hdr) + discriminate = staticmethod(discriminate) + + def __init__(self, foffs, magic, ver, end, flags, rid): + self.foffs = foffs + self.magic = magic + self.ver = ver + self.end = end + self.flags = flags + self.rid = rid + if self.magic[-1] not in ['0x00', 'a', 'c', 'd', 'e', 'f', 'x']: + error = 3 + + def __str__(self): + if self.empty(): + return '0x%08x: <empty>' % (self.foffs) + if self.magic[-1] == 'x': + return '0x%08x: [\"%s\"]' % (self.foffs, self.magic) + if self.magic[-1] in ['a', 'c', 'd', 'e', 'f', 'x']: + return '0x%08x: [\"%s\" v=%d e=%d f=0x%04x rid=0x%x]' % (self.foffs, self.magic, self.ver, self.end, self.flags, self.rid) + return '0x%08x: <error, unknown magic \"%s\" (possible overwrite boundary?)>' % (self.foffs, self.magic) + + def empty(self): + return self.magic == '\x00'*4 + + def owi(self): + return self.flags & owi_mask != 0 + + def skip(self, f): + f.read(rem_in_blk(f, dblk_size)) + + def check(self): + if self.empty() or self.magic[:3] != 'RHM' or self.magic[3] not in ['a', 'c', 'd', 'e', 'f', 'x']: + return True + if self.ver != hdr_ver and self.magic[-1] != 'x': + raise Exception('%s: Invalid header version: found %d, expected %d.' % (self, self.ver, hdr_ver)) + return False + + +#== class FileHdr ============================================================== + +class FileHdr(Hdr): + + format = '=2H4x3Q' + + def init(self, f, foffs, fid, lid, fro, time_sec, time_ns): + self.fid = fid + self.lid = lid + self.fro = fro + self.time_sec = time_sec + self.time_ns = time_ns + + def __str__(self): + return '%s fid=%d lid=%d fro=0x%08x t=%s' % (Hdr.__str__(self), self.fid, self.lid, self.fro, self.timestamp_str()) + + def skip(self, f): + f.read(rem_in_blk(f, sblk_size)) + + def timestamp(self): + return (self.time_sec, self.time_ns) + + def timestamp_str(self): + ts = gmtime(self.time_sec) + fstr = '%%a %%b %%d %%H:%%M:%%S.%09d %%Y' % (self.time_ns) + return strftime(fstr, ts) + + +#== class DeqHdr =============================================================== + +class DeqHdr(Hdr): + + format = '=QQ' + + def init(self, f, foffs, deq_rid, xidsize): + self.deq_rid = deq_rid + self.xidsize = xidsize + self.xid = None + self.deq_tail = None + self.xid_complete = False + self.tail_complete = False + self.tail_bin = None + self.tail_offs = 0 + self.load(f) + + def load(self, f): + if self.xidsize == 0: + self.xid_complete = True + self.tail_complete = True + else: + if not self.xid_complete: + ret = load_file_data(f, self.xidsize, self.xid) + self.xid = ret[0] + self.xid_complete = ret[1] + if self.xid_complete and not self.tail_complete: + ret = load_file_data(f, calcsize(RecTail.format), self.tail_bin) + self.tail_bin = ret[0] + if ret[1]: + self.enq_tail = RecTail(self.tail_offs, *unpack(RecTail.format, self.tail_bin)) + if self.enq_tail.magic_inv != inv_str(self.magic) or self.enq_tail.rid != self.rid: + print " > %s" % self + raise Exception('Invalid dequeue record tail (magic=%s; rid=%d) at 0x%08x' % (self.enq_tail, self.enq_tail.rid, self.enq_tail.foffs)) + self.enq_tail.skip(f) + self.tail_complete = ret[1] + return self.complete() + + def complete(self): + return self.xid_complete and self.tail_complete + + def __str__(self): + return '%s %sdrid=0x%x' % (Hdr.__str__(self), print_xid(self.xidsize, self.xid), self.deq_rid) + + +#== class TxnHdr =============================================================== + +class TxnHdr(Hdr): + + format = '=Q' + + def init(self, f, foffs, xidsize): + self.xidsize = xidsize + self.xid = None + self.tx_tail = None + self.xid_complete = False + self.tail_complete = False + self.tail_bin = None + self.tail_offs = 0 + self.load(f) + + def load(self, f): + if not self.xid_complete: + ret = load_file_data(f, self.xidsize, self.xid) + self.xid = ret[0] + self.xid_complete = ret[1] + if self.xid_complete and not self.tail_complete: + ret = load_file_data(f, calcsize(RecTail.format), self.tail_bin) + self.tail_bin = ret[0] + if ret[1]: + self.enq_tail = RecTail(self.tail_offs, *unpack(RecTail.format, self.tail_bin)) + if self.enq_tail.magic_inv != inv_str(self.magic) or self.enq_tail.rid != self.rid: + print " > %s" % self + raise Exception('Invalid transaction record tail (magic=%s; rid=%d) at 0x%08x' % (self.enq_tail, self.enq_tail.rid, self.enq_tail.foffs)) + self.enq_tail.skip(f) + self.tail_complete = ret[1] + return self.complete() + + def complete(self): + return self.xid_complete and self.tail_complete + + def __str__(self): + return '%s %s' % (Hdr.__str__(self), print_xid(self.xidsize, self.xid)) + + +#== class RecTail ============================================================== + +class RecTail(Sizeable): + + format = '=4sQ' + + def __init__(self, foffs, magic_inv, rid): + self.foffs = foffs + self.magic_inv = magic_inv + self.rid = rid + + def __str__(self): + magic = inv_str(self.magic_inv) + return '[\"%s\" rid=0x%x]' % (magic, self.rid) + + def skip(self, f): + f.read(rem_in_blk(f, dblk_size)) + + +#== class EnqRec =============================================================== + +class EnqRec(Hdr): + + format = '=QQ' + + def init(self, f, foffs, xidsize, dsize): + self.xidsize = xidsize + self.dsize = dsize + self.transient = self.flags & transient_mask > 0 + self.extern = self.flags & extern_mask > 0 + self.xid = None + self.data = None + self.enq_tail = None + self.xid_complete = False + self.data_complete = False + self.tail_complete = False + self.tail_bin = None + self.tail_offs = 0 + self.load(f) + + def load(self, f): + if not self.xid_complete: + ret = load_file_data(f, self.xidsize, self.xid) + self.xid = ret[0] + self.xid_complete = ret[1] + if self.xid_complete and not self.data_complete: + if self.extern: + self.data_complete = True + else: + ret = load_file_data(f, self.dsize, self.data) + self.data = ret[0] + self.data_complete = ret[1] + if self.data_complete and not self.tail_complete: + ret = load_file_data(f, calcsize(RecTail.format), self.tail_bin) + self.tail_bin = ret[0] + if ret[1]: + self.enq_tail = RecTail(self.tail_offs, *unpack(RecTail.format, self.tail_bin)) + if self.enq_tail.magic_inv != inv_str(self.magic) or self.enq_tail.rid != self.rid: + print " > %s" % self + raise Exception('Invalid enqueue record tail (magic=%s; rid=%d) at 0x%08x' % (self.enq_tail, self.enq_tail.rid, self.enq_tail.foffs)) + self.enq_tail.skip(f) + self.tail_complete = ret[1] + return self.complete() + + def complete(self): + return self.xid_complete and self.data_complete and self.tail_complete + + def print_flags(self): + s = '' + if self.transient: + s = '*TRANSIENT' + if self.extern: + if len(s) > 0: + s += ',EXTERNAL' + else: + s = '*EXTERNAL' + if len(s) > 0: + s += '*' + return s + + def __str__(self): + return '%s %s%s %s %s' % (Hdr.__str__(self), print_xid(self.xidsize, self.xid), print_data(self.dsize, self.data), self.enq_tail, self.print_flags()) + + +#== class Main ================================================================= + +class Main: + def __init__(self, argv): + self.bfn = None + self.csvfn = None + self.jdir = None + self.aflag = False + self.hflag = False + self.qflag = False + self.tnum = None + self.num_jfiles = None + self.num_msgs = None + self.msg_len = None + self.auto_deq = None + self.xid_len = None + self.transient = None + self.extern = None + + self.file_start = 0 + self.file_num = 0 + self.fro = 0x200 + self.emap = {} + self.tmap = {} + self.rec_cnt = 0 + self.msg_cnt = 0 + self.txn_msg_cnt = 0 + self.fhdr = None + self.f = None + self.first_rec = False + self.last_file = False + self.last_rid = -1 + self.fhdr_owi_at_msg_start = None + + self.proc_args(argv) + self.proc_csv() + self.read_jinf() + + def run(self): + try: + start_info = self.analyze_files() + stop = self.advance_file(*start_info) + except Exception: + print 'WARNING: All journal files are empty.' + if self.num_msgs > 0: + raise Exception('All journal files are empty, but %d msgs expectd.' % self.num_msgs) + else: + stop = True + while not stop: + warn = '' + if file_full(self.f): + stop = self.advance_file() + if stop: + break + hdr = load(self.f, Hdr) + if hdr.empty(): + stop = True; + break + if hdr.check(): + stop = True; + else: + self.rec_cnt += 1 + self.fhdr_owi_at_msg_start = self.fhdr.owi() + if self.first_rec: + if self.fhdr.fro != hdr.foffs: + raise Exception('File header first record offset mismatch: fro=0x%08x; rec_offs=0x%08x' % (self.fhdr.fro, hdr.foffs)) + else: + if not self.qflag: print ' * fro ok: 0x%08x' % self.fhdr.fro + self.first_rec = False + if isinstance(hdr, EnqRec) and not stop: + while not hdr.complete(): + stop = self.advance_file() + if stop: + break + hdr.load(self.f) + if self.extern != None: + if hdr.extern: + if hdr.data != None: + raise Exception('Message data found on external record') + else: + if self.msg_len > 0 and len(hdr.data) != self.msg_len: + raise Exception('Message length (%d) incorrect; expected %d' % (len(hdr.data), self.msg_len)) + else: + if self.msg_len > 0 and len(hdr.data) != self.msg_len: + raise Exception('Message length (%d) incorrect; expected %d' % (len(hdr.data), self.msg_len)) + if self.xid_len > 0 and len(hdr.xid) != self.xid_len: + print ' ERROR: XID length (%d) incorrect; expected %d' % (len(hdr.xid), self.xid_len) + sys.exit(1) + #raise Exception('XID length (%d) incorrect; expected %d' % (len(hdr.xid), self.xid_len)) + if self.transient != None: + if self.transient: + if not hdr.transient: + raise Exception('Expected transient record, found persistent') + else: + if hdr.transient: + raise Exception('Expected persistent record, found transient') + stop = not self.check_owi(hdr) + if stop: + warn = ' (WARNING: OWI mismatch - could be overwrite boundary.)' + else: + self.msg_cnt += 1 + if self.aflag or self.auto_deq: + if hdr.xid == None: + self.emap[hdr.rid] = (self.fhdr.fid, hdr, False) + else: + self.txn_msg_cnt += 1 + if hdr.xid in self.tmap: + self.tmap[hdr.xid].append((self.fhdr.fid, hdr)) #Append tuple to existing list + else: + self.tmap[hdr.xid] = [(self.fhdr.fid, hdr)] # Create new list + elif isinstance(hdr, DeqHdr) and not stop: + while not hdr.complete(): + stop = self.advance_file() + if stop: + break + hdr.load(self.f) + stop = not self.check_owi(hdr) + if stop: + warn = ' (WARNING: OWI mismatch - could be overwrite boundary.)' + else: + if self.auto_deq != None: + if not self.auto_deq: + warn = ' WARNING: Dequeue record rid=%d found in non-dequeue test - ignoring.' % hdr.rid + if self.aflag or self.auto_deq: + if hdr.xid == None: + if hdr.deq_rid in self.emap: + if self.emap[hdr.deq_rid][2]: + warn = ' (WARNING: dequeue rid 0x%x dequeues locked enqueue record 0x%x)' % (hdr.rid, hdr.deq_rid) + del self.emap[hdr.deq_rid] + else: + warn = ' (WARNING: rid being dequeued 0x%x not found in enqueued records)' % hdr.deq_rid + else: + if hdr.deq_rid in self.emap: + t = self.emap[hdr.deq_rid] + self.emap[hdr.deq_rid] = (t[0], t[1], True) # Lock enq record + if hdr.xid in self.tmap: + self.tmap[hdr.xid].append((self.fhdr.fid, hdr)) #Append to existing list + else: + self.tmap[hdr.xid] = [(self.fhdr.fid, hdr)] # Create new list + elif isinstance(hdr, TxnHdr) and not stop: + while not hdr.complete(): + stop = self.advance_file() + if stop: + break + hdr.load(self.f) + stop = not self.check_owi(hdr) + if stop: + warn = ' (WARNING: OWI mismatch - could be overwrite boundary.)' + else: + if hdr.xid in self.tmap: + mismatched_rids = [] + if hdr.magic[-1] == 'c': # commit + for rec in self.tmap[hdr.xid]: + if isinstance(rec[1], EnqRec): + self.emap[rec[1].rid] = (rec[0], rec[1], False) # Transfer enq to emap + elif isinstance(rec[1], DeqHdr): + if rec[1].deq_rid in self.emap: + del self.emap[rec[1].deq_rid] # Delete from emap + else: + mismatched_rids.append('0x%x' % rec[1].deq_rid) + else: + raise Exception('Unknown header found in txn map: %s' % rec[1]) + elif hdr.magic[-1] == 'a': # abort + for rec in self.tmap[hdr.xid]: + if isinstance(rec[1], DeqHdr): + if rec[1].deq_rid in self.emap: + t = self.emap[rec[1].deq_rid] + self.emap[rec[1].deq_rid] = (t[0], t[1], False) # Unlock enq record + del self.tmap[hdr.xid] + if len(mismatched_rids) > 0: + warn = ' (WARNING: transactional dequeues not found in enqueue map; rids=%s)' % mismatched_rids + else: + warn = ' (WARNING: %s not found in transaction map)' % print_xid(len(hdr.xid), hdr.xid) + if not self.qflag: print ' > %s%s' % (hdr, warn) + if not stop: + stop = (self.last_file and hdr.check()) or hdr.empty() or self.fhdr.empty() + + def analyze_files(self): + fname = '' + fnum = -1 + rid = -1 + fro = -1 + tss = '' + if not self.qflag: print 'Analyzing journal files:' + owi_found = False + for i in range(0, self.num_jfiles): + jfn = self.jdir + '/' + self.bfn + '.%04d.jdat' % i + f = open(jfn) + fhdr = load(f, Hdr) + if fhdr.empty(): + if not self.qflag: + print ' %s: file empty' % jfn + break + if i == 0: + init_owi = fhdr.owi() + fname = jfn + fnum = i + rid = fhdr.rid + fro = fhdr.fro + tss = fhdr.timestamp_str() + elif fhdr.owi() != init_owi and not owi_found: + fname = jfn + fnum = i + rid = fhdr.rid + fro = fhdr.fro + tss = fhdr.timestamp_str() + owi_found = True + if not self.qflag: + print ' %s: owi=%s rid=0x%x, fro=0x%08x ts=%s' % (jfn, fhdr.owi(), fhdr.rid, fhdr.fro, fhdr.timestamp_str()) + if fnum < 0 or rid < 0 or fro < 0: + raise Exception('All journal files empty') + if not self.qflag: print ' Oldest complete file: %s: rid=%d, fro=0x%08x ts=%s' % (fname, rid, fro, tss) + return (fnum, rid, fro) + + def advance_file(self, *start_info): + seek_flag = False + if len(start_info) == 3: + self.file_start = self.file_num = start_info[0] + self.fro = start_info[2] + seek_flag = True + if self.f != None and file_full(self.f): + self.file_num = self.incr_fnum() + if self.file_num == self.file_start: + return True + if self.file_start == 0: + self.last_file = self.file_num == self.num_jfiles - 1 + else: + self.last_file = self.file_num == self.file_start - 1 + if self.file_num < 0 or self.file_num >= self.num_jfiles: + raise Exception('Bad file number %d' % self.file_num) + jfn = self.jdir + '/' + self.bfn + '.%04d.jdat' % self.file_num + self.f = open(jfn) + self.fhdr = load(self.f, Hdr) + if seek_flag and self.f.tell() != self.fro: + self.f.seek(self.fro) + self.first_rec = True + if not self.qflag: print jfn, ": ", self.fhdr + return False + + def incr_fnum(self): + self.file_num += 1 + if self.file_num >= self.num_jfiles: + self.file_num = 0; + return self.file_num + + def check_owi(self, hdr): + return self.fhdr_owi_at_msg_start == hdr.owi() + + def check_rid(self, hdr): + if self.last_rid != -1 and hdr.rid <= self.last_rid: + return False + self.last_rid = hdr.rid + return True + + def read_jinf(self): + filename = self.jdir + '/' + self.bfn + '.jinf' + try: + f = open(filename, 'r') + except IOError: + print 'ERROR: Unable to open jinf file %s' % filename + sys.exit(1) + p = xml.parsers.expat.ParserCreate() + p.StartElementHandler = self.handleStartElement + p.CharacterDataHandler = self.handleCharData + p.EndElementHandler = self.handleEndElement + p.ParseFile(f) + if self.num_jfiles == None: + print 'ERROR: number_jrnl_files not found in jinf file "%s"!' % filename + if jfsize == None: + print 'ERROR: jrnl_file_size_sblks not found in jinf file "%s"!' % filename + if self.num_jfiles == None or jfsize == None: + sys.exit(1) + + def handleStartElement(self, name, attrs): + global jfsize + if name == 'number_jrnl_files': + self.num_jfiles = int(attrs['value']) + if name == 'jrnl_file_size_sblks': + jfsize = (int(attrs['value']) + 1) * sblk_size + + def handleCharData(self, data): pass + + def handleEndElement(self, name): pass + + def proc_csv(self): + if self.csvfn != None and self.tnum != None: + tparams = self.get_test(self.csvfn, self.tnum) + if tparams == None: + print 'ERROR: Test %d not found in CSV file "%s"' % (self.tnum, self.csvfn) + sys.exit(1) + self.num_msgs = tparams['num_msgs'] + if tparams['min_size'] == tparams['max_size']: + self.msg_len = tparams['max_size'] + else: + self.msg_len = 0 + self.auto_deq = tparams['auto_deq'] + if tparams['xid_min_size'] == tparams['xid_max_size']: + self.xid_len = tparams['xid_max_size'] + else: + self.xid_len = 0 + self.transient = tparams['transient'] + self.extern = tparams['extern'] + + def get_test(self, filename, tnum): + try: + f=open(filename, 'r') + except IOError: + print 'ERROR: Unable to open CSV file "%s"' % filename + sys.exit(1) + for l in f: + sl = l.strip().split(',') + if len(sl[0]) > 0 and sl[0][0] != '"': + try: + if (int(sl[TEST_NUM_COL]) == tnum): + return { 'num_msgs':int(sl[NUM_MSGS_COL]), + 'min_size':int(sl[MIN_MSG_SIZE_COL]), + 'max_size':int(sl[MAX_MSG_SIZE_COL]), + 'auto_deq':not (sl[AUTO_DEQ_COL] == 'FALSE' or sl[AUTO_DEQ_COL] == '0'), + 'xid_min_size':int(sl[MIN_XID_SIZE_COL]), + 'xid_max_size':int(sl[MAX_XID_SIZE_COL]), + 'transient':not (sl[TRANSIENT_COL] == 'FALSE' or sl[TRANSIENT_COL] == '0'), + 'extern':not (sl[EXTERN_COL] == 'FALSE' or sl[EXTERN_COL] == '0'), + 'comment':sl[COMMENT_COL] } + except Exception: + pass + return None + + def proc_args(self, argv): + try: + opts, args = getopt.getopt(sys.argv[1:], "ab:c:d:hqt:", ["analyse", "base-filename=", "csv-filename=", "dir=", "help", "quiet", "test-num="]) + except getopt.GetoptError: + self.usage() + sys.exit(2) + for o, a in opts: + if o in ("-h", "--help"): + self.usage() + sys.exit() + if o in ("-a", "--analyze"): + self.aflag = True + if o in ("-b", "--base-filename"): + self.bfn = a + if o in ("-c", "--csv-filename"): + self.csvfn = a + if o in ("-d", "--dir"): + self.jdir = a + if o in ("-q", "--quiet"): + self.qflag = True + if o in ("-t", "--test-num"): + if not a.isdigit(): + print 'ERROR: Illegal test-num argument. Must be a non-negative number' + sys.exit(2) + self.tnum = int(a) + if self.bfn == None or self.jdir == None: + print 'ERROR: Missing requred args.' + self.usage() + sys.exit(2) + if self.tnum != None and self.csvfn == None: + print 'ERROR: Test number specified, but not CSV file' + self.usage() + sys.exit(2) + + def usage(self): + print 'Usage: %s opts' % sys.argv[0] + print ' where opts are in either short or long format (*=req\'d):' + print ' -a --analyze Analyze enqueue/dequeue records' + print ' -b --base-filename [string] * Base filename for journal files' + print ' -c --csv-filename [string] CSV filename containing test parameters' + print ' -d --dir [string] * Journal directory containing journal files' + print ' -h --help Print help' + print ' -q --quiet Quiet (reduced output)' + print ' -t --test-num [int] Test number from CSV file - only valid if CSV file named' + + def report(self): + if not self.qflag: + print + print ' === REPORT ====' + if self.num_msgs > 0 and self.msg_cnt != self.num_msgs: + print 'WARNING: Found %d messages; %d expected.' % (self.msg_cnt, self.num_msgs) + if len(self.emap) > 0: + print + print 'Remaining enqueued records (sorted by rid): ' + keys = sorted(self.emap.keys()) + for k in keys: + if self.emap[k][2] == True: # locked + locked = ' (locked)' + else: + locked = '' + print " fid=%d %s%s" % (self.emap[k][0], self.emap[k][1], locked) + print 'WARNING: Enqueue-Dequeue mismatch, %d enqueued records remain.' % len(self.emap) + if len(self.tmap) > 0: + txn_rec_cnt = 0 + print + print 'Remaining transactions: ' + for t in self.tmap: + print_xid(len(t), t) + for r in self.tmap[t]: + print " fid=%d %s" % (r[0], r[1]) + print " Total: %d records for xid %s" % (len(self.tmap[t]), t) + txn_rec_cnt += len(self.tmap[t]) + print 'WARNING: Incomplete transactions, %d xids remain containing %d records.' % (len(self.tmap), txn_rec_cnt) + print '%d enqueues, %d journal records processed.' % (self.msg_cnt, self.rec_cnt) + + +#=============================================================================== + +CLASSES = { + "a": TxnHdr, + "c": TxnHdr, + "d": DeqHdr, + "e": EnqRec, + "f": FileHdr +} + +m = Main(sys.argv) +m.run() +m.report() + +sys.exit(None) diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.cpp new file mode 100644 index 0000000000..1bc04110af --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.cpp @@ -0,0 +1,77 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "jrnl_init_params.h" + +namespace mrg +{ +namespace jtt +{ + +jrnl_init_params::jrnl_init_params(const std::string& jid, const std::string& jdir, const std::string& base_filename, + const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, + const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks): + _jid(jid), + _jdir(jdir), + _base_filename(base_filename), + _num_jfiles(num_jfiles), + _ae(ae), + _ae_max_jfiles(ae_max_jfiles), + _jfsize_sblks(jfsize_sblks), + _wcache_num_pages(wcache_num_pages), + _wcache_pgsize_sblks(wcache_pgsize_sblks) +{} + +jrnl_init_params::jrnl_init_params(const jrnl_init_params& jp): + _jid(jp._jid), + _jdir(jp._jdir), + _base_filename(jp._base_filename), + _num_jfiles(jp._num_jfiles), + _ae(jp._ae), + _ae_max_jfiles(jp._ae_max_jfiles), + _jfsize_sblks(jp._jfsize_sblks), + _wcache_num_pages(jp._wcache_num_pages), + _wcache_pgsize_sblks(jp._wcache_pgsize_sblks) +{} + +jrnl_init_params::jrnl_init_params(const jrnl_init_params* const jp_ptr): + _jid(jp_ptr->_jid), + _jdir(jp_ptr->_jdir), + _base_filename(jp_ptr->_base_filename), + _num_jfiles(jp_ptr->_num_jfiles), + _ae(jp_ptr->_ae), + _ae_max_jfiles(jp_ptr->_ae_max_jfiles), + _jfsize_sblks(jp_ptr->_jfsize_sblks), + _wcache_num_pages(jp_ptr->_wcache_num_pages), + _wcache_pgsize_sblks(jp_ptr->_wcache_pgsize_sblks) +{} + +// static initializers + +const u_int16_t jrnl_init_params::def_num_jfiles = 8; +const bool jrnl_init_params::def_ae = false; +const u_int16_t jrnl_init_params::def_ae_max_jfiles = 0; +const u_int32_t jrnl_init_params::def_jfsize_sblks = 0xc00; +const u_int16_t jrnl_init_params::def_wcache_num_pages = 32; +const u_int32_t jrnl_init_params::def_wcache_pgsize_sblks = 64; + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.h new file mode 100644 index 0000000000..ece87f8e03 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_init_params.h @@ -0,0 +1,80 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_jrnl_init_params_hpp +#define mrg_jtt_jrnl_init_params_hpp + +#include <boost/shared_ptr.hpp> +#include <string> +#include <sys/types.h> + +namespace mrg +{ +namespace jtt +{ + + class jrnl_init_params + { + public: + static const u_int16_t def_num_jfiles; + static const bool def_ae; + static const u_int16_t def_ae_max_jfiles; + static const u_int32_t def_jfsize_sblks; + static const u_int16_t def_wcache_num_pages; + static const u_int32_t def_wcache_pgsize_sblks; + + typedef boost::shared_ptr<jrnl_init_params> shared_ptr; + + private: + std::string _jid; + std::string _jdir; + std::string _base_filename; + u_int16_t _num_jfiles; + bool _ae; + u_int16_t _ae_max_jfiles; + u_int32_t _jfsize_sblks; + u_int16_t _wcache_num_pages; + u_int32_t _wcache_pgsize_sblks; + + public: + jrnl_init_params(const std::string& jid, const std::string& jdir, const std::string& base_filename, + const u_int16_t num_jfiles = def_num_jfiles, const bool ae = def_ae, + const u_int16_t ae_max_jfiles = def_ae_max_jfiles, const u_int32_t jfsize_sblks = def_jfsize_sblks, + const u_int16_t wcache_num_pages = def_wcache_num_pages, + const u_int32_t wcache_pgsize_sblks = def_wcache_pgsize_sblks); + jrnl_init_params(const jrnl_init_params& jp); + jrnl_init_params(const jrnl_init_params* const jp_ptr); + + inline const std::string& jid() const { return _jid; } + inline const std::string& jdir() const { return _jdir; } + inline const std::string& base_filename() const { return _base_filename; } + inline u_int16_t num_jfiles() const { return _num_jfiles; } + inline bool is_ae() const { return _ae; } + inline u_int16_t ae_max_jfiles() const { return _ae_max_jfiles; } + inline u_int32_t jfsize_sblks() const { return _jfsize_sblks; } + inline u_int16_t wcache_num_pages() const { return _wcache_num_pages; } + inline u_int32_t wcache_pgsize_sblks() const { return _wcache_pgsize_sblks; } + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_jrnl_init_params_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.cpp new file mode 100644 index 0000000000..339dc1b52c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.cpp @@ -0,0 +1,439 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "jrnl_instance.h" + +#include <cstdlib> +#include "data_src.h" +#include "qpid/legacystore/jrnl/data_tok.h" +#include "qpid/legacystore/jrnl/jerrno.h" +#include "test_case_result.h" + +#define MAX_WR_WAIT 10 // in ms +#define MAX_RD_WAIT 100 // in ms +#define MAX_ENQCAPTHRESH_CNT 1000 // 10s if MAX_WR_WAIT is 10 ms + +namespace mrg +{ +namespace jtt +{ + +jrnl_instance::jrnl_instance(const std::string& jid, const std::string& jdir, const std::string& base_filename, + const u_int16_t num_jfiles, const bool ae, const u_int16_t ae_max_jfiles, const u_int32_t jfsize_sblks, + const u_int16_t wcache_num_pages, const u_int32_t wcache_pgsize_sblks): + mrg::journal::jcntl(jid, jdir, base_filename), + _jpp(new jrnl_init_params(jid, jdir, base_filename, num_jfiles, ae, ae_max_jfiles, jfsize_sblks, + wcache_num_pages, wcache_pgsize_sblks)), + _args_ptr(0), + _dtok_master_enq_list(), + _dtok_master_txn_list(), + _dtok_rd_list(), + _dtok_deq_list(), + _rd_aio_cv(_rd_aio_mutex), + _wr_full_cv(_wr_full_mutex), + _rd_list_cv(_rd_list_mutex), + _deq_list_cv(_deq_list_mutex), + _tcp(), + _tcrp() +{} + +jrnl_instance::jrnl_instance(const jrnl_init_params::shared_ptr& p): + mrg::journal::jcntl(p->jid(), p->jdir(), p->base_filename()), + _jpp(p), + _args_ptr(0), + _dtok_master_enq_list(), + _dtok_master_txn_list(), + _dtok_rd_list(), + _dtok_deq_list(), + _rd_aio_cv(_rd_aio_mutex), + _wr_full_cv(_wr_full_mutex), + _rd_list_cv(_rd_list_mutex), + _deq_list_cv(_deq_list_mutex), + _tcp(), + _tcrp() +{} + +jrnl_instance::~jrnl_instance() {} + + +void +jrnl_instance::init_tc(test_case::shared_ptr& tcp, const args* const args_ptr) throw () +{ + test_case_result::shared_ptr p(new test_case_result(_jpp->jid())); + _tcrp = p; + _args_ptr = args_ptr; + try + { + _tcp = tcp; + _dtok_master_enq_list.clear(); + _dtok_master_txn_list.clear(); + _dtok_rd_list.clear(); + _dtok_deq_list.clear(); + + if (_args_ptr->recover_mode) + { + try + { + u_int64_t highest_rid; + recover(_jpp->num_jfiles(), _jpp->is_ae(), _jpp->ae_max_jfiles(), _jpp->jfsize_sblks(), + _jpp->wcache_num_pages(), _jpp->wcache_pgsize_sblks(), this, + 0, highest_rid); + recover_complete(); + } + catch (const mrg::journal::jexception& e) + { + if (e.err_code() == mrg::journal::jerrno::JERR_JDIR_STAT) + initialize(_jpp->num_jfiles(), _jpp->is_ae(), _jpp->ae_max_jfiles(), _jpp->jfsize_sblks(), + _jpp->wcache_num_pages(), _jpp->wcache_pgsize_sblks(), this); + else + throw; + } + } + else + initialize(_jpp->num_jfiles(), _jpp->is_ae(), _jpp->ae_max_jfiles(), _jpp->jfsize_sblks(), + _jpp->wcache_num_pages(), _jpp->wcache_pgsize_sblks(), this); + } + catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); } + catch (const std::exception& e) { _tcrp->add_exception(e.what()); } + catch (...) { _tcrp->add_exception("Unknown exception"); } +} + +void +jrnl_instance::run_tc() throw () +{ + _tcrp->set_start_time(); + ::pthread_create(&_enq_thread, 0, run_enq, this); + ::pthread_create(&_read_thread, 0, run_read, this); + ::pthread_create(&_deq_thread, 0, run_deq, this); +} + +void +jrnl_instance::tc_wait_compl() throw () +{ + try + { + ::pthread_join(_deq_thread, 0); + ::pthread_join(_read_thread, 0); + ::pthread_join(_enq_thread, 0); + stop(true); + } + catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); } + catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); } + catch (...) { _tcrp->add_exception("Unknown exception"); panic(); } + _lpmgr.finalize(); + _tcrp->set_stop_time(); + _tcp->add_result(_tcrp); +} + +void +jrnl_instance::run_enq() throw () +{ + try + { + unsigned sleep_cnt = 0U; + while(_tcrp->num_enq() < _tcp->num_msgs() && !_tcrp->exception()) + { + dtok_ptr p(new mrg::journal::data_tok); + _dtok_master_enq_list.push_back(p); + const char* msgp = data_src::get_data(_tcrp->num_enq() % 10); + const std::size_t msg_size = _tcp->this_data_size(); + const std::size_t xid_size = _tcp->this_xid_size(); + const std::string xid(data_src::get_xid(xid_size)); + const bool external = _tcp->this_external(); + const bool transient = _tcp->this_transience(); + mrg::journal::iores res; + if (xid_size) + { + if (external) + res = enqueue_extern_txn_data_record(msg_size, p.get(), xid, transient); + else + res = enqueue_txn_data_record(msgp, msg_size, msg_size, p.get(), xid, + transient); + } + else + { + if (external) + res = enqueue_extern_data_record(msg_size, p.get(), transient); + else + res = enqueue_data_record(msgp, msg_size, msg_size, p.get(), transient); + } + switch (res) + { + case mrg::journal::RHM_IORES_SUCCESS: + sleep_cnt = 0U; + _tcrp->incr_num_enq(); + if (p->has_xid() && !_tcp->auto_deq()) + commit(p.get()); + break; + case mrg::journal::RHM_IORES_ENQCAPTHRESH: + if (++sleep_cnt > MAX_ENQCAPTHRESH_CNT) + { + _tcrp->add_exception("Timeout waiting for RHM_IORES_ENQCAPTHRESH to clear."); + panic(); + } + else if (get_wr_events(0) == 0) // *** GEV2 + { + mrg::journal::slock sl(_wr_full_mutex); + _wr_full_cv.waitintvl(MAX_WR_WAIT * 1000000); // MAX_WR_WAIT in ms + } + break; + default: + std::ostringstream oss; + oss << "ERROR: enqueue operation in journal \"" << _jid << "\" returned "; + oss << mrg::journal::iores_str(res) << "."; + _tcrp->add_exception(oss.str()); + } + } + flush(true); + } + catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); } + catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); } + catch (...) { _tcrp->add_exception("Unknown exception"); panic(); } +} + +void +jrnl_instance::run_read() throw () +{ + try + { + read_arg::read_mode_t rd_mode = _args_ptr->read_mode.val(); + if (rd_mode != read_arg::NONE) + { + while (_tcrp->num_rproc() < _tcp->num_msgs() && !_tcrp->exception()) + { + journal::data_tok* dtokp = 0; + { + mrg::journal::slock sl(_rd_list_mutex); + if (_dtok_rd_list.empty()) + _rd_list_cv.wait(); + if (!_dtok_rd_list.empty()) + { + dtokp = _dtok_rd_list.front(); + _dtok_rd_list.pop_front(); + } + } + if (dtokp) + { + _tcrp->incr_num_rproc(); + + bool do_read = true; + if (rd_mode == read_arg::RANDOM) + do_read = 1.0 * std::rand() / RAND_MAX < _args_ptr->read_prob / 100.0; + else if (rd_mode == read_arg::LAZYLOAD) + do_read = _tcrp->num_rproc() >= _args_ptr->lld_skip_num && + _tcrp->num_read() < _args_ptr->lld_rd_num; + bool read_compl = false; + while (do_read && !read_compl && !_tcrp->exception()) + { + void* dptr = 0; + std::size_t dsize = 0; + void* xptr = 0; + std::size_t xsize = 0; + bool tr = false; + bool ext = false; + mrg::journal::iores res = read_data_record(&dptr, dsize, &xptr, xsize, tr, + ext, dtokp); + switch (res) + { + case mrg::journal::RHM_IORES_SUCCESS: + { + mrg::journal::slock sl(_deq_list_mutex); + _dtok_deq_list.push_back(dtokp); + _deq_list_cv.broadcast(); + } + read_compl = true; + _tcrp->incr_num_read(); + + // clean up + if (xsize) + std::free(xptr); + else if (dsize) + std::free(dptr); + dptr = 0; + xptr = 0; + break; + case mrg::journal::RHM_IORES_PAGE_AIOWAIT: + if (get_rd_events(0) == 0) + { + mrg::journal::slock sl(_rd_aio_mutex); + _rd_aio_cv.waitintvl(MAX_RD_WAIT * 1000000); // MAX_RD_WAIT in ms + } + break; + default: + std::ostringstream oss; + oss << "ERROR: read operation in journal \"" << _jid; + oss << "\" returned " << mrg::journal::iores_str(res) << "."; + _tcrp->add_exception(oss.str()); + { + mrg::journal::slock sl(_deq_list_mutex); + _deq_list_cv.broadcast(); // wake up deq thread + } + } + } + } + } + } + } + catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); } + catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); } + catch (...) { _tcrp->add_exception("Unknown exception"); panic(); } +} + +void +jrnl_instance::run_deq() throw () +{ + try + { + if (_tcp->auto_deq()) + { + while(_tcrp->num_deq() < _tcp->num_msgs() && !_tcrp->exception()) + { + journal::data_tok* dtokp = 0; + { + mrg::journal::slock sl(_deq_list_mutex); + if (_dtok_deq_list.empty()) + _deq_list_cv.wait(); + if (!_dtok_deq_list.empty()) + { + dtokp = _dtok_deq_list.front(); + _dtok_deq_list.pop_front(); + } + } + if (dtokp) + { + mrg::journal::iores res; + if (dtokp->has_xid()) + res = dequeue_txn_data_record(dtokp, dtokp->xid()); + else + res = dequeue_data_record(dtokp); + if (res == mrg::journal::RHM_IORES_SUCCESS) + { + _tcrp->incr_num_deq(); + commit(dtokp); + } + else + { + std::ostringstream oss; + oss << "ERROR: dequeue operation in journal \"" << _jid; + oss << "\" returned " << mrg::journal::iores_str(res) << "."; + _tcrp->add_exception(oss.str()); + } + } + } + flush(true); + } + } + catch (const mrg::journal::jexception& e) { _tcrp->add_exception(e); panic(); } + catch (const std::exception& e) { _tcrp->add_exception(e.what()); panic(); } + catch (...) { _tcrp->add_exception("Unknown exception"); panic(); } +} + +void +jrnl_instance::abort(const mrg::journal::data_tok* dtokp) +{ + txn(dtokp, false); +} + +void +jrnl_instance::commit(const mrg::journal::data_tok* dtokp) +{ + txn(dtokp, true); +} + +void +jrnl_instance::txn(const mrg::journal::data_tok* dtokp, const bool commit) +{ + if (dtokp->has_xid()) + { + mrg::journal::data_tok* p = prep_txn_dtok(dtokp); + mrg::journal::iores res = commit ? txn_commit(p, p->xid()) : txn_abort(p, p->xid()); + if (res != mrg::journal::RHM_IORES_SUCCESS) + { + std::ostringstream oss; + oss << "ERROR: " << (commit ? "commit" : "abort") << " operation in journal \""; + oss << _jid << "\" returned " << mrg::journal::iores_str(res) << "."; + _tcrp->add_exception(oss.str()); + } + } +} + +mrg::journal::data_tok* +jrnl_instance::prep_txn_dtok(const mrg::journal::data_tok* dtokp) +{ + dtok_ptr p(new mrg::journal::data_tok); + _dtok_master_txn_list.push_back(p); + p->set_xid(dtokp->xid()); + return p.get(); +} + +void +jrnl_instance::panic() +{ + // In the event of a panic or exception condition, release all waiting CVs + _rd_aio_cv.broadcast(); + _wr_full_cv.broadcast(); + _rd_list_cv.broadcast(); + _deq_list_cv.broadcast(); +} + +// AIO callbacks + +void +jrnl_instance::wr_aio_cb(std::vector<journal::data_tok*>& dtokl) +{ + for (std::vector<journal::data_tok*>::const_iterator i=dtokl.begin(); i!=dtokl.end(); i++) + { + if ((*i)->wstate() == journal::data_tok::ENQ || (*i)->wstate() == journal::data_tok::DEQ) + { + journal::data_tok* dtokp = *i; + if (dtokp->wstate() == journal::data_tok::ENQ) + { + if (_args_ptr->read_mode.val() == read_arg::NONE) + { + mrg::journal::slock sl(_deq_list_mutex); + _dtok_deq_list.push_back(dtokp); + _deq_list_cv.broadcast(); + } + else + { + mrg::journal::slock sl(_rd_list_mutex); + _dtok_rd_list.push_back(dtokp); + _rd_list_cv.broadcast(); + } + } + else // DEQ + { + mrg::journal::slock sl(_wr_full_mutex); + _wr_full_cv.broadcast(); + } + } + } +} + +void +jrnl_instance::rd_aio_cb(std::vector<u_int16_t>& /*pil*/) +{ + mrg::journal::slock sl(_rd_aio_mutex); + _rd_aio_cv.broadcast(); +} + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.h new file mode 100644 index 0000000000..5003f39b24 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jrnl_instance.h @@ -0,0 +1,121 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_jrnl_instance_hpp +#define mrg_jtt_jrnl_instance_hpp + +#include "args.h" +#include "jrnl_init_params.h" +#include "test_case.h" + +#include <boost/shared_ptr.hpp> +#include "qpid/legacystore/jrnl/cvar.h" +#include "qpid/legacystore/jrnl/data_tok.h" +#include "qpid/legacystore/jrnl/jcntl.h" +#include "qpid/legacystore/jrnl/slock.h" +#include "qpid/legacystore/jrnl/smutex.h" +#include <list> +#include <vector> + +namespace mrg +{ +namespace jtt +{ + + class jrnl_instance : public mrg::journal::jcntl, public virtual mrg::journal::aio_callback + { + public: + typedef boost::shared_ptr<jrnl_instance> shared_ptr; + typedef boost::shared_ptr<journal::data_tok> dtok_ptr; + + private: + jrnl_init_params::shared_ptr _jpp; + const args* _args_ptr; + std::vector<dtok_ptr> _dtok_master_enq_list; + std::vector<dtok_ptr> _dtok_master_txn_list; + std::list<journal::data_tok*> _dtok_rd_list; + std::list<journal::data_tok*> _dtok_deq_list; + mrg::journal::smutex _rd_aio_mutex; ///< Mutex for read aio wait conditions + mrg::journal::cvar _rd_aio_cv; ///< Condition var for read aio wait conditions + mrg::journal::smutex _wr_full_mutex; ///< Mutex for write full conditions + mrg::journal::cvar _wr_full_cv; ///< Condition var for write full conditions + mrg::journal::smutex _rd_list_mutex; ///< Mutex for _dtok_rd_list + mrg::journal::cvar _rd_list_cv; ///< Condition var for _dtok_rd_list + mrg::journal::smutex _deq_list_mutex; ///< Mutex for _dtok_deq_list + mrg::journal::cvar _deq_list_cv; ///< Condition var for _dtok_deq_list + pthread_t _enq_thread; + pthread_t _deq_thread; + pthread_t _read_thread; + test_case::shared_ptr _tcp; + test_case_result::shared_ptr _tcrp; + + public: + jrnl_instance(const std::string& jid, const std::string& jdir, + const std::string& base_filename, + const u_int16_t num_jfiles = jrnl_init_params::def_num_jfiles, + const bool ae = jrnl_init_params::def_ae, + const u_int16_t ae_max_jfiles = jrnl_init_params::def_ae_max_jfiles, + const u_int32_t jfsize_sblks = jrnl_init_params::def_jfsize_sblks, + const u_int16_t wcache_num_pages = jrnl_init_params::def_wcache_num_pages, + const u_int32_t wcache_pgsize_sblks = jrnl_init_params::def_wcache_pgsize_sblks); + jrnl_instance(const jrnl_init_params::shared_ptr& params); + virtual ~jrnl_instance(); + + inline const jrnl_init_params::shared_ptr& params() const { return _jpp; } + inline const std::string& jid() const { return _jpp->jid(); } + + void init_tc(test_case::shared_ptr& tcp, const args* const args_ptr) throw (); + void run_tc() throw (); + void tc_wait_compl() throw (); + + // AIO callbacks + virtual void wr_aio_cb(std::vector<journal::data_tok*>& dtokl); + virtual void rd_aio_cb(std::vector<u_int16_t>& pil); + + private: + void run_enq() throw (); + inline static void* run_enq(void* p) + { static_cast<jrnl_instance*>(p)->run_enq(); return 0; } + + void run_read() throw (); + inline static void* run_read(void* p) + { static_cast<jrnl_instance*>(p)->run_read(); return 0; } + + void run_deq() throw (); + inline static void* run_deq(void* p) + { static_cast<jrnl_instance*>(p)->run_deq(); return 0; } + + void abort(const mrg::journal::data_tok* dtokp); + void commit(const mrg::journal::data_tok* dtokp); + void txn(const mrg::journal::data_tok* dtokp, const bool commit); + mrg::journal::data_tok* prep_txn_dtok(const mrg::journal::data_tok* dtokp); + + void panic(); + +// // static callbacks +// static void aio_rd_callback(jcntl* journal, std::vector<u_int16_t>& pil); +// static void aio_wr_callback(jcntl* journal, std::vector<journal::data_tok*>& dtokl); + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_jrnl_instance_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/jtt.csv b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jtt.csv new file mode 100644 index 0000000000..df523e3f97 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/jtt.csv @@ -0,0 +1,234 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +,,,,,,,"Msg size",,"Xid size",,,,,"enq-size",,"deq-size",,"txn-size",, +"Test #","tf","pf","amn","mn incr","#msgs","ms incr","Min","Max","Min","Max","auto-deq","transient","extern","bytes","dblks","bytes","dblks","bytes","dblks","comment" +,,,,,,,,,,,,,,,,,,,, +"Initialize only",,,,,,,,,,,,,,,,,,,, +0,"L",0,0,0,0,0,0,0,0,0,FALSE,FALSE,FALSE,44,1,0,0,0,0,"No messages - journal creation/initialization only" +,,,,,,,,,,,,,,,,,,,, +"Simple message combinations of persistent/deq transientueued/non-dequeued, transactional/non-transactional",,,,,,,,,,,,,,,,,,,, +1,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"1 * 10-byte message" +2,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,FALSE,54,1,0,0,0,0,"10 * 10-byte message" +3,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"1 * 10-byte message [transient]" +4,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,FALSE,54,1,0,0,0,0,"10 * 10-byte message [transient]" +5,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn]" +6,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn]" +7,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"1 * 10-byte message [txn transient]" +8,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,FALSE,64,1,0,0,0,0,"10 * 10-byte message [txn transient]" +9,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq]" +10,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq]" +11,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"1 * 10-byte message [deq transient]" +12,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,FALSE,54,1,32,1,0,0,"10 * 10-byte message [deq transient]" +13,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [deq txn]" +14,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [deq txn]" +15,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient]" +16,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,FALSE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient]" +17,"L",1,1,0,1,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [extern]" +18,"L",1,10,0,10,0,10,10,0,0,FALSE,FALSE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [extern]" +19,"L",1,1,0,1,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"1 * 10-byte message [transient extern]" +20,"L",1,10,0,10,0,10,10,0,0,FALSE,TRUE,TRUE,54,1,0,0,0,0,"10 * 10-byte message [transient extern]" +21,"L",1,1,0,1,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn extern]" +22,"L",1,10,0,10,0,10,10,10,10,FALSE,FALSE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn extern]" +23,"L",1,1,0,1,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"1 * 10-byte message [txn transient extern]" +24,"L",1,10,0,10,0,10,10,10,10,FALSE,TRUE,TRUE,64,1,0,0,0,0,"10 * 10-byte message [txn transient extern]" +25,"L",1,1,0,1,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq extern]" +26,"L",1,10,0,10,0,10,10,0,0,TRUE,FALSE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq extern]" +27,"L",1,1,0,1,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"1 * 10-byte message [deq transient extern]" +28,"L",1,10,0,10,0,10,10,0,0,TRUE,TRUE,TRUE,54,1,32,1,0,0,"10 * 10-byte message [deq transient extern]" +29,"L",1,1,0,1,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [deq txn extern]" +30,"L",1,10,0,10,0,10,10,10,10,TRUE,FALSE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [deq txn extern]" +31,"L",1,1,0,1,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"1 * 10-byte message [txn deq transient extern]" +32,"L",1,10,0,10,0,10,10,10,10,TRUE,TRUE,TRUE,64,1,54,1,46,1,"10 * 10-byte message [txn deq transient extern]" +,,,,,,,,,,,,,,,,,,,, +"Transition from one d-block to two per message",,,,,,,,,,,,,,,,,,,, +33,"L",1,10,0,10,0,84,84,0,0,FALSE,FALSE,FALSE,128,1,0,0,0,0,"1 dblk exact fit" +34,"L",1,10,0,10,1,85,85,0,0,FALSE,FALSE,FALSE,129,2,0,0,0,0,"1 dblk + 1 byte" +35,"L",1,10,0,10,0,58,58,26,26,FALSE,FALSE,FALSE,128,1,0,0,0,0,"1 dblk exact fit [txn]" +36,"L",1,10,0,10,1,59,59,26,26,FALSE,FALSE,FALSE,129,2,0,0,0,0,"1 dblk + 1 byte [txn]" +,,,,,,,,,,,,,,,,,,,, +"Transition from one s-block to two per message",,,,,,,,,,,,,,,,,,,, +37,"L",1,10,0,10,0,468,468,0,0,FALSE,FALSE,FALSE,512,4,0,0,0,0,"1 sblk exact fit" +38,"L",1,10,0,10,1,469,469,0,0,FALSE,FALSE,FALSE,513,5,0,0,0,0,"1 sblk + 1 byte" +39,"L",1,10,0,10,0,442,442,26,26,FALSE,FALSE,FALSE,512,4,0,0,0,0,"1 sblk exact fit [txn]" +40,"L",1,10,0,10,1,443,443,26,26,FALSE,FALSE,FALSE,513,5,0,0,0,0,"1 sblk + 1 byte [txn]" +,,,,,,,,,,,,,,,,,,,, +"Transition from first page to second",,,,,,,,,,,,,,,,,,,, +41,"L",1,8,0,8,0,4052,4052,0,0,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page" +42,"L",1,8,1,9,0,4052,4052,0,0,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page" +43,"L",1,8,0,8,1,4053,4053,0,0,FALSE,FALSE,FALSE,4097,33,0,0,0,0,"1/8 page + 1 byte" +44,"L",1,8,0,8,0,3796,3796,256,256,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page [txn]" +45,"L",1,8,1,9,0,3796,3796,256,256,FALSE,FALSE,FALSE,4096,32,0,0,0,0,"1/8 page [txn]" +46,"L",1,8,0,8,1,3797,3797,256,256,FALSE,FALSE,FALSE,4097,33,0,0,0,0,"1/8 page + 1 byte [txn]" +47,"L",1,8,0,8,0,3924,3924,0,0,TRUE,FALSE,FALSE,3968,31,32,1,0,0,"1/8 page incl deq [deq]" +48,"L",1,8,1,9,0,3924,3924,0,0,TRUE,FALSE,FALSE,3968,31,32,1,0,0,"1/8 page incl deq [deq]" +49,"L",1,8,0,8,1,3925,3925,0,0,TRUE,FALSE,FALSE,3969,32,32,1,0,0,"1/8 page incl deq + 1 byte [deq]" +50,"L",1,8,0,8,0,3028,3028,256,256,TRUE,FALSE,FALSE,3328,26,300,3,292,3,"1/8 page incl deq & txn [deq txn]" +51,"L",1,8,1,9,0,3028,3028,256,256,TRUE,FALSE,FALSE,3328,26,300,3,292,3,"1/8 page incl deq & txn [deq txn]" +52,"L",1,8,0,8,1,3029,3029,256,256,TRUE,FALSE,FALSE,3329,27,300,3,292,3,"1/8 page incl deq & txn + 1 byte [deq txn]" +,,,,,,,,,,,,,,,,,,,, +"Page cache rollover (from page 32 back to page 0)",,,,,,,,,,,,,,,,,,,, +53,"L",1,32,0,32,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page" +54,"L",1,32,1,33,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page" +55,"L",1,32,0,32,1,32725,32725,0,0,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte" +56,"L",1.5,22,0,22,0,49108,49108,0,0,FALSE,FALSE,FALSE,49152,384,0,0,0,0,"1.5 pages" +57,"L",1,32,0,32,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]" +58,"L",1,32,1,33,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]" +59,"L",1,32,0,32,1,32469,32469,256,256,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte [txn]" +60,"L",1.5,22,0,22,0,48852,48852,256,256,FALSE,FALSE,FALSE,49152,384,0,0,0,0,"1.5 pages [txn]" +61,"L",1,32,0,32,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]" +62,"L",1,32,1,33,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]" +63,"L",1,32,0,32,1,32597,32597,0,0,TRUE,FALSE,FALSE,32641,256,32,1,0,0,"1 page incl deq + 1 byte [deq]" +64,"L",1.5,22,0,22,0,48980,48980,0,0,TRUE,FALSE,FALSE,49024,383,32,1,0,0,"1.5 pages incl deq [deq]" +65,"L",1,32,0,32,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]" +66,"L",1,32,1,33,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]" +67,"L",1,32,0,32,1,31701,31701,256,256,TRUE,FALSE,FALSE,32001,251,300,3,292,3,"1 page incl deq & txn + 1 byte [deq txn]" +68,"L",1.5,22,0,22,0,48084,48084,256,256,TRUE,FALSE,FALSE,48384,378,300,3,292,3,"1.5 pages incl deq & txn [deq txn]" +,,,,,,,,,,,,,,,,,,,, +"File transition (from file 0000 to 0001)",,,,,,,,,,,,,,,,,,,, +69,"L",1,48,0,48,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page" +70,"L",1,48,1,49,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page" +71,"L",1,48,0,48,1,32725,32725,0,0,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte" +72,"L",2.5,20,0,20,0,81876,81876,0,0,FALSE,FALSE,FALSE,81920,640,0,0,0,0,"2.5 pages" +73,"L",1,48,0,48,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]" +74,"L",1,48,1,49,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"1 page [txn]" +75,"L",1,48,0,48,1,32469,32469,256,256,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"1 page + 1 byte [txn]" +76,"L",2.5,20,0,20,0,81620,81620,256,256,FALSE,FALSE,FALSE,81920,640,0,0,0,0,"2.5 pages [txn]" +77,"L",1,48,0,48,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]" +78,"L",1,48,1,49,0,32596,32596,0,0,TRUE,FALSE,FALSE,32640,255,32,1,0,0,"1 page incl deq [deq]" +79,"L",1,48,0,48,1,32597,32597,0,0,TRUE,FALSE,FALSE,32641,256,32,1,0,0,"1 page incl deq + 1 byte [deq]" +80,"L",2.5,20,0,20,0,81748,81748,0,0,TRUE,FALSE,FALSE,81792,639,32,1,0,0,"2.5 pages incl deq [deq]" +81,"L",1,48,0,48,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]" +82,"L",1,48,1,49,0,31700,31700,256,256,TRUE,FALSE,FALSE,32000,250,300,3,292,3,"1 page incl deq & txn [deq txn]" +83,"L",1,48,0,48,1,31701,31701,256,256,TRUE,FALSE,FALSE,32001,251,300,3,292,3,"1 page incl deq & txn + 1 byte [deq txn]" +84,"L",2.5,20,0,20,0,80852,80852,256,256,TRUE,FALSE,FALSE,81152,634,300,3,292,3,"2.5 pages incl deq & txn [deq txn]" +,,,,,,,,,,,,,,,,,,,, +"File rollover (from file 0007 to 0000) - RHM_WRONLY req'd for auto-dequeue == FALSE",,,,,,,,,,,,,,,,,,,, +85,"L",0.5,16,0,16,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]" +86,"L",0.5,16,1,17,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]" +87,"L",0.5,16,0,16,1,786261,786261,0,0,TRUE,FALSE,FALSE,786305,6144,32,1,0,0,"24 pages incl deq + 1 byte [deq]" +88,"L",0.5,16,0,16,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]" +89,"L",0.5,16,1,17,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]" +90,"L",0.5,16,0,16,1,785365,785365,256,256,TRUE,FALSE,FALSE,785665,6139,300,3,292,3,"24 pages incl deq & txn + 1 byte [deq txn]" +91,"L",0.25,32,0,32,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]" +92,"L",0.25,32,1,33,0,786260,786260,0,0,TRUE,FALSE,FALSE,786304,6143,32,1,0,0,"24 pages incl deq = ½ file [deq]" +93,"L",0.25,32,0,32,1,786261,786261,0,0,TRUE,FALSE,FALSE,786305,6144,32,1,0,0,"24 pages incl deq + 1 byte [deq]" +94,"L",0.25,32,0,32,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]" +95,"L",0.25,32,1,33,0,785364,785364,256,256,TRUE,FALSE,FALSE,785664,6138,300,3,292,3,"24 pages incl deq & txn = ½ file [deq txn]" +96,"L",0.25,32,0,32,1,785365,785365,256,256,TRUE,FALSE,FALSE,785665,6139,300,3,292,3,"24 pages incl deq & txn + 1 byte [deq txn]" +,,,,,,,,,,,,,,,,,,,, +"Multi-page messages (large messages) - tests various paths in encoder.",,,,,,,,,,,,,,,,,,,, +97,"L",1,16,0,16,0,32724,32724,0,0,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"data 1 page" +98,"L",1,16,0,16,1,32725,32725,0,0,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"data 1 page + 1 byte (tail split; 1 byte over page boundary)" +99,"L",1,16,0,16,11,32735,32735,0,0,FALSE,FALSE,FALSE,32779,257,0,0,0,0,"data 1 page + 11 bytes (tail split; 11 bytes over page boundary)" +100,"L",1,16,0,16,12,32736,32736,0,0,FALSE,FALSE,FALSE,32780,257,0,0,0,0,"data 1 page + 12 bytes (tail separated exactly onto next page)" +101,"L",1,16,0,16,13,32737,32737,0,0,FALSE,FALSE,FALSE,32781,257,0,0,0,0,"data 1 page + 13 bytes (data split; 1 byte over page boundary)" +102,"L",1,16,0,16,0,32468,32468,256,256,FALSE,FALSE,FALSE,32768,256,0,0,0,0,"data 1 page [txn]" +103,"L",1,16,0,16,1,32469,32469,256,256,FALSE,FALSE,FALSE,32769,257,0,0,0,0,"data 1 page + 1 byte (tail split; 1 byte over page boundary) [txn]" +104,"L",1,16,0,16,11,32479,32479,256,256,FALSE,FALSE,FALSE,32779,257,0,0,0,0,"data 1 page + 11 bytes (tail split; 11 bytes over page boundary) [txn]" +105,"L",1,16,0,16,12,32480,32480,256,256,FALSE,FALSE,FALSE,32780,257,0,0,0,0,"data 1 page + 12 bytes (tail separated exactly onto next page) [txn]" +106,"L",1,16,0,16,13,32481,32481,256,256,FALSE,FALSE,FALSE,32781,257,0,0,0,0,"data 1 page + 13 bytes (data split; 1 byte over page boundary) [txn]" +107,"L",2,16,0,16,0,65492,65492,0,0,FALSE,FALSE,FALSE,65536,512,0,0,0,0,"data 2 pages" +108,"L",2,16,0,16,1,65493,65493,0,0,FALSE,FALSE,FALSE,65537,513,0,0,0,0,"data 2 pages + 1 byte (tail split; 1 byte over page boundary)" +109,"L",2,16,0,16,11,65503,65503,0,0,FALSE,FALSE,FALSE,65547,513,0,0,0,0,"data 2 pages + 11 bytes (tail split; 11 bytes over page boundary)" +110,"L",2,16,0,16,12,65504,65504,0,0,FALSE,FALSE,FALSE,65548,513,0,0,0,0,"data 2 pages + 12 bytes (tail separated exactly onto next page)" +111,"L",2,16,0,16,13,65505,65505,0,0,FALSE,FALSE,FALSE,65549,513,0,0,0,0,"data 2 pages + 13 bytes (data split; 1 byte over page boundary)" +112,"L",2,16,0,16,0,65236,65236,256,256,FALSE,FALSE,FALSE,65536,512,0,0,0,0,"data 2 pages [txn]" +113,"L",2,16,0,16,1,65237,65237,256,256,FALSE,FALSE,FALSE,65537,513,0,0,0,0,"data 2 pages + 1 byte (tail split; 1 byte over page boundary) [txn]" +114,"L",2,16,0,16,11,65247,65247,256,256,FALSE,FALSE,FALSE,65547,513,0,0,0,0,"data 2 pages + 11 bytes (tail split; 11 bytes over page boundary) [txn]" +115,"L",2,16,0,16,12,65248,65248,256,256,FALSE,FALSE,FALSE,65548,513,0,0,0,0,"data 2 pages + 12 bytes (tail separated exactly onto next page) [txn]" +116,"L",2,16,0,16,13,65249,65249,256,256,FALSE,FALSE,FALSE,65549,513,0,0,0,0,"data 2 pages + 13 bytes (data split; 1 byte over page boundary) [txn]" +117,"L",4,16,0,16,0,131028,131028,0,0,FALSE,FALSE,FALSE,131072,1024,0,0,0,0,"data 4 pages" +118,"L",4,16,0,16,1,131029,131029,0,0,FALSE,FALSE,FALSE,131073,1025,0,0,0,0,"data 4 pages + 1 byte (tail split; 1 byte over page boundary)" +119,"L",4,16,0,16,11,131039,131039,0,0,FALSE,FALSE,FALSE,131083,1025,0,0,0,0,"data 4 pages + 11 bytes (tail split; 11 bytes over page boundary)" +120,"L",4,16,0,16,12,131040,131040,0,0,FALSE,FALSE,FALSE,131084,1025,0,0,0,0,"data 4 pages + 12 bytes (tail separated exactly onto next page)" +121,"L",4,16,0,16,13,131041,131041,0,0,FALSE,FALSE,FALSE,131085,1025,0,0,0,0,"data 4 pages + 13 bytes (data split; 1 byte over page boundary)" +122,"L",4,16,0,16,0,130772,130772,256,256,FALSE,FALSE,FALSE,131072,1024,0,0,0,0,"data 4 pages [txn]" +123,"L",4,16,0,16,1,130773,130773,256,256,FALSE,FALSE,FALSE,131073,1025,0,0,0,0,"data 4 pages + 1 byte (tail split; 1 byte over page boundary) [txn]" +124,"L",4,16,0,16,11,130783,130783,256,256,FALSE,FALSE,FALSE,131083,1025,0,0,0,0,"data 4 pages + 11 bytes (tail split; 11 bytes over page boundary) [txn]" +125,"L",4,16,0,16,12,130784,130784,256,256,FALSE,FALSE,FALSE,131084,1025,0,0,0,0,"data 4 pages + 12 bytes (tail separated exactly onto next page) [txn]" +126,"L",4,16,0,16,13,130785,130785,256,256,FALSE,FALSE,FALSE,131085,1025,0,0,0,0,"data 4 pages + 13 bytes (data split; 1 byte over page boundary) [txn]" +127,"L",3.5,16,0,16,0,114644,114644,0,0,FALSE,FALSE,FALSE,114688,896,0,0,0,0,"data 3.5 pages" +128,"L",3.5,16,0,16,1,114645,114645,0,0,FALSE,FALSE,FALSE,114689,897,0,0,0,0,"data 3.5 pages + 1 byte" +129,"L",3.5,16,0,16,0,114388,114388,256,256,FALSE,FALSE,FALSE,114688,896,0,0,0,0,"data 3.5 pages [txn]" +130,"L",3.5,16,0,16,1,114389,114389,256,256,FALSE,FALSE,FALSE,114689,897,0,0,0,0,"data 3.5 pages + 1 byte [txn]" +131,"L",1,16,0,16,-1,10,10,32735,32735,FALSE,FALSE,FALSE,32789,257,0,0,0,0,"xid 1 page – 1 byte; data 10 bytes (exact fit) [txn]" +132,"L",1,16,0,16,0,10,10,32736,32736,FALSE,FALSE,FALSE,32790,257,0,0,0,0,"xid 1 page; data 10 bytes (exact fit) [txn]" +133,"L",1,16,0,16,1,10,10,32737,32737,FALSE,FALSE,FALSE,32791,257,0,0,0,0,"xid 1 page + 1 byte; data 10 bytes (exact fit) [txn]" +134,"L",2,16,0,16,-1,10,10,65503,65503,FALSE,FALSE,FALSE,65557,513,0,0,0,0,"xid 2 pages – 1 byte; data 10 bytes (exact fit) [txn]" +135,"L",2,16,0,16,0,10,10,65504,65504,FALSE,FALSE,FALSE,65558,513,0,0,0,0,"xid 2 pages; data 10 bytes (exact fit) [txn]" +136,"L",2,16,0,16,1,10,10,65505,65505,FALSE,FALSE,FALSE,65559,513,0,0,0,0,"xid 2 pages + 1 byte; data 10 bytes (exact fit) [txn]" +137,"L",4,16,0,16,-1,10,10,131039,131039,FALSE,FALSE,FALSE,131093,1025,0,0,0,0,"xid 4 pages – 1 byte; data 10 bytes (exact fit) [txn]" +138,"L",4,16,0,16,0,10,10,131040,131040,FALSE,FALSE,FALSE,131094,1025,0,0,0,0,"xid 4 pages; data 10 bytes (exact fit) [txn]" +139,"L",4,16,0,16,1,10,10,131041,131041,FALSE,FALSE,FALSE,131095,1025,0,0,0,0,"xid 4 pages + 1 byte; data 10 bytes (exact fit) [txn]" +140,"L",3.5,16,0,16,0,10,10,114656,114656,FALSE,FALSE,FALSE,114710,897,0,0,0,0,"xid 3.5 pages; data 10 bytes (exact fit) [txn]" +141,"L",3.5,16,0,16,1,10,10,114657,114657,FALSE,FALSE,FALSE,114711,897,0,0,0,0,"xid 3.5 pages + 1 byte; data 10 bytes (exact fit) [txn]" +142,"L",1,16,0,16,-1,10,10,32735,32735,TRUE,FALSE,FALSE,32789,257,32779,257,32771,257,"xid 1 page – 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" +143,"L",1,16,0,16,0,10,10,32736,32736,TRUE,FALSE,FALSE,32790,257,32780,257,32772,257,"xid 1 page for enq rec; data 10 bytes (exact fit) [deq, txn]" +144,"L",1,16,0,16,1,10,10,32737,32737,TRUE,FALSE,FALSE,32791,257,32781,257,32773,257,"xid 1 page + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" +145,"L",2,16,0,16,-1,10,10,65503,65503,TRUE,FALSE,FALSE,65557,513,65547,513,65539,513,"xid 2 pages – 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" +146,"L",2,16,0,16,0,10,10,65504,65504,TRUE,FALSE,FALSE,65558,513,65548,513,65540,513,"xid 2 pages for enq rec; data 10 bytes (exact fit) [deq, txn]" +147,"L",2,16,0,16,1,10,10,65505,65505,TRUE,FALSE,FALSE,65559,513,65549,513,65541,513,"xid 2 pages + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" +148,"L",4,16,0,16,-1,10,10,131039,131039,TRUE,FALSE,FALSE,131093,1025,131083,1025,131075,1025,"xid 4 pages – 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" +149,"L",4,16,0,16,0,10,10,131040,131040,TRUE,FALSE,FALSE,131094,1025,131084,1025,131076,1025,"xid 4 pages for enq rec; data 10 bytes (exact fit) [deq, txn]" +150,"L",4,16,0,16,1,10,10,131041,131041,TRUE,FALSE,FALSE,131095,1025,131085,1025,131077,1025,"xid 4 pages + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" +151,"L",3.5,16,0,16,0,10,10,114656,114656,TRUE,FALSE,FALSE,114710,897,114700,897,114692,897,"xid 3.5 pages for enq rec; data 10 bytes (exact fit) [deq, txn]" +152,"L",3.5,16,0,16,1,10,10,114657,114657,TRUE,FALSE,FALSE,114711,897,114701,897,114693,897,"xid 3.5 pages + 1 byte for enq rec; data 10 bytes (exact fit) [deq, txn]" +153,"L",1,16,0,16,-1,10,10,32735,32735,TRUE,FALSE,FALSE,32789,257,32779,257,32771,257,"xid 1 page – 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" +154,"L",1,16,0,16,0,10,10,32736,32736,TRUE,FALSE,FALSE,32790,257,32780,257,32772,257,"xid 1 page for deq rec; data 10 bytes (exact fit) [deq, txn]" +155,"L",1,16,0,16,1,10,10,32737,32737,TRUE,FALSE,FALSE,32791,257,32781,257,32773,257,"xid 1 page + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" +156,"L",2,16,0,16,-1,10,10,65503,65503,TRUE,FALSE,FALSE,65557,513,65547,513,65539,513,"xid 2 pages – 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" +157,"L",2,16,0,16,0,10,10,65504,65504,TRUE,FALSE,FALSE,65558,513,65548,513,65540,513,"xid 2 pages for deq rec; data 10 bytes (exact fit) [deq, txn]" +158,"L",2,16,0,16,1,10,10,65505,65505,TRUE,FALSE,FALSE,65559,513,65549,513,65541,513,"xid 2 pages + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" +159,"L",4,16,0,16,-1,10,10,131039,131039,TRUE,FALSE,FALSE,131093,1025,131083,1025,131075,1025,"xid 4 pages – 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" +160,"L",4,16,0,16,0,10,10,131040,131040,TRUE,FALSE,FALSE,131094,1025,131084,1025,131076,1025,"xid 4 pages for deq rec; data 10 bytes (exact fit) [deq, txn]" +161,"L",4,16,0,16,1,10,10,131041,131041,TRUE,FALSE,FALSE,131095,1025,131085,1025,131077,1025,"xid 4 pages + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" +162,"L",3.5,16,0,16,0,10,10,114656,114656,TRUE,FALSE,FALSE,114710,897,114700,897,114692,897,"xid 3.5 pages for deq rec; data 10 bytes (exact fit) [deq, txn]" +163,"L",3.5,16,0,16,1,10,10,114657,114657,TRUE,FALSE,FALSE,114711,897,114701,897,114693,897,"xid 3.5 pages + 1 byte for deq rec; data 10 bytes (exact fit) [deq, txn]" +164,"L",1,16,0,16,-1,10,10,32743,32743,TRUE,FALSE,FALSE,32797,257,32787,257,32779,257,"xid 1 page – 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" +165,"L",1,16,0,16,0,10,10,32744,32744,TRUE,FALSE,FALSE,32798,257,32788,257,32780,257,"xid 1 page for txn rec; data 10 bytes (exact fit) [deq, txn]" +166,"L",1,16,0,16,1,10,10,32745,32745,TRUE,FALSE,FALSE,32799,257,32789,257,32781,257,"xid 1 page + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" +167,"L",2,16,0,16,-1,10,10,65511,65511,TRUE,FALSE,FALSE,65565,513,65555,513,65547,513,"xid 2 pages – 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" +168,"L",2,16,0,16,0,10,10,65512,65512,TRUE,FALSE,FALSE,65566,513,65556,513,65548,513,"xid 2 pages for txn rec; data 10 bytes (exact fit) [deq, txn]" +169,"L",2,16,0,16,1,10,10,65513,65513,TRUE,FALSE,FALSE,65567,513,65557,513,65549,513,"xid 2 pages + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" +170,"L",4,16,0,16,-1,10,10,131047,131047,TRUE,FALSE,FALSE,131101,1025,131091,1025,131083,1025,"xid 4 pages – 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" +171,"L",4,16,0,16,0,10,10,131048,131048,TRUE,FALSE,FALSE,131102,1025,131092,1025,131084,1025,"xid 4 pages for txn rec; data 10 bytes (exact fit) [deq, txn]" +172,"L",4,16,0,16,1,10,10,131049,131049,TRUE,FALSE,FALSE,131103,1025,131093,1025,131085,1025,"xid 4 pages + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" +173,"L",3.5,16,0,16,0,10,10,114664,114664,TRUE,FALSE,FALSE,114718,897,114708,897,114700,897,"xid 3.5 pages for txn rec; data 10 bytes (exact fit) [deq, txn]" +174,"L",3.5,16,0,16,1,10,10,114665,114665,TRUE,FALSE,FALSE,114719,897,114709,897,114701,897,"xid 3.5 pages + 1 byte for txn rec; data 10 bytes (exact fit) [deq, txn]" +,,,,,,,,,,,,,,,,,,,, +"High volume tests of random message lengths - RHM_WRONLY req'd for auto-dequeue == FALSE",,,,,,,,,,,,,,,,,,,, +#175,"M",1,5000000,0,5000000,0,0,84,0,0,TRUE,FALSE,FALSE,128,1,32,1,0,0,"1 dblk max [deq]" +#176,"M",3,3000000,0,3000000,0,0,340,0,0,TRUE,FALSE,FALSE,384,3,32,1,0,0,"3 dblks max [deq]" +#177,"M",10,1600000,0,1600000,0,0,1236,0,0,TRUE,FALSE,FALSE,1280,10,32,1,0,0,"10 dblks max [deq]" +#178,"M",30,600000,0,600000,0,0,3796,0,0,TRUE,FALSE,FALSE,3840,30,32,1,0,0,"30 dblks max [deq]" +#179,"M",100,200000,0,200000,0,0,12756,0,0,TRUE,FALSE,FALSE,12800,100,32,1,0,0,"100 dblks max [deq]" +#180,"M",300,60000,0,60000,0,0,38356,0,0,TRUE,FALSE,FALSE,38400,300,32,1,0,0,"300 dblks max [deq]" +#181,"M",1000,20000,0,20000,0,0,127956,0,0,TRUE,FALSE,FALSE,128000,1000,32,1,0,0,"1000 dblks max [deq]" +#182,"M",1,5000000,0,5000000,0,0,100,1,100,TRUE,FALSE,FALSE,244,2,144,2,136,2,"100 bytes xid max + 100 bytes data max [deq txn]" +#183,"M",3,3000000,0,3000000,0,0,300,1,300,TRUE,FALSE,FALSE,644,6,344,3,336,3,"300 bytes xid max + 300 bytes data max [deq txn]" +#184,"M",10,1600000,0,1600000,0,0,1000,1,1000,TRUE,FALSE,FALSE,2044,16,1044,9,1036,9,"1000 bytes xid max + 1000 bytes data max [deq txn]" +#185,"M",30,600000,0,600000,0,0,3000,1,3000,TRUE,FALSE,FALSE,6044,48,3044,24,3036,24,"3000 bytes xid max + 3000 bytes data max [deq txn]" +#186,"M",100,200000,0,200000,0,0,10000,1,10000,TRUE,FALSE,FALSE,20044,157,10044,79,10036,79,"10000 bytes xid max + 10000 bytes data max [deq txn]" +#187,"M",300,60000,0,60000,0,0,30000,1,30000,TRUE,FALSE,FALSE,60044,470,30044,235,30036,235,"30000 bytes xid max + 30000 bytes data max [deq txn]" +#188,"M",1000,20000,0,20000,0,0,100000,1,100000,TRUE,FALSE,FALSE,200044,1563,100044,782,100036,782,"100000 bytes xid max + 100000 bytes data max [deq txn]" +,,,,,,,,,,,,,,,,,,,, +"STANDARD PERFORMANCE BENCHMARK: 10,000,000 writes, data=212b (2 dblks)",,,,,,,,,,,,,,,,,,,, +#189,"M",1,10000000,0,10000000,0,212,212,0,0,TRUE,FALSE,FALSE,256,2,32,1,0,0,"212 bytes data (2 dblks enq + 1 dblk deq)" +#190,"M",1,10000000,0,10000000,0,148,148,64,64,TRUE,FALSE,FALSE,256,2,108,1,100,1,"148 bytes data + 64 bytes xid (2 dblks enq + 1 dblks deq + 1 dblks txn)" diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/main.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/main.cpp new file mode 100644 index 0000000000..c8a4642b1c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/main.cpp @@ -0,0 +1,57 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "test_mgr.h" + +#include "args.h" +#include <csignal> +#include <iostream> + +#define PACKAGE_NAME "Journal Test Tool" +#define VERSION "0.1" + +namespace po = boost::program_options; + +int main(int argc, char** argv) +{ + std::signal(SIGINT, mrg::jtt::test_mgr::signal_handler); + std::signal(SIGTERM, mrg::jtt::test_mgr::signal_handler); + + std::cout << PACKAGE_NAME << " v." << VERSION << std::endl; + + std::ostringstream oss; + oss << PACKAGE_NAME << " options"; + mrg::jtt::args args(oss.str()); + if (args.parse(argc, argv)) return 1; + + try + { + mrg::jtt::test_mgr tm(args); + tm.run(); + if (tm.error()) return 2; // One or more tests threw exceptions + } + catch (const std::exception& e) + { + std::cerr << e.what() << std::endl; + return 3; + } + return 0; +} diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.cpp new file mode 100644 index 0000000000..94a07c7005 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.cpp @@ -0,0 +1,93 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "read_arg.h" + +#include <cassert> +#include <boost/program_options.hpp> +namespace po = boost::program_options; + +namespace mrg +{ +namespace jtt +{ +std::map<std::string, read_arg::read_mode_t> read_arg::_map; +std::string read_arg::_description; +const bool read_arg::init = __init(); + +// static init fn +bool +read_arg::__init() +{ + // Set string versions of each enum option here + _map["NONE"] = NONE; + _map["ALL"] = ALL; + _map["RANDOM"] = RANDOM; + _map["LAZYLOAD"] = LAZYLOAD; + _description = "Determines if and when messages will be read prior to dequeueing. " + "Values: (NONE | ALL | RANDOM | LAZYLOAD)"; + return true; +} + +void +read_arg::parse(const std::string& str) +{ + std::map<std::string, read_arg::read_mode_t>::const_iterator i = _map.find(str); + if (i == _map.end()) + throw po::invalid_option_value(str); + _rm = i->second; +} + +// static fn +const std::string& +read_arg::str(const read_mode_t rm) +{ + std::map<std::string, read_mode_t>::const_iterator i = _map.begin(); + while (i->second != rm && i != _map.end()) i++; + assert(i != _map.end()); + return i->first; +} + +// static fn +const std::string& +read_arg::descr() +{ + return _description; +} + +std::ostream& +operator<<(std::ostream& os, const read_arg& ra) +{ + os << ra.str(); + return os; +} + +std::istream& +operator>>(std::istream& is, read_arg& ra) +{ + std::string s; + is >> s; + ra.parse(s); + return is; +} + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.h new file mode 100644 index 0000000000..a8fd6f198e --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/read_arg.h @@ -0,0 +1,62 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_read_arg_hpp +#define mrg_jtt_read_arg_hpp + +#include <string> +#include <map> + +namespace mrg +{ +namespace jtt +{ + +class read_arg +{ + public: + enum read_mode_t { NONE, ALL, RANDOM, LAZYLOAD}; + private: + static std::map<std::string, read_mode_t> _map; + static std::string _description; + static const bool init; + static bool __init(); + read_mode_t _rm; + public: + inline read_arg() : _rm(NONE) {} + inline read_arg(read_mode_t rm) : _rm(rm) {} + + inline read_mode_t val() const { return _rm; } + inline void set_val(const read_mode_t rm) { _rm = rm; } + void parse(const std::string& str); + + inline const std::string& str() const { return str(_rm); } + static const std::string& str(const read_mode_t rm); + static const std::string& descr(); + + friend std::ostream& operator<<(std::ostream& os, const read_arg& ra); + friend std::istream& operator>>(std::istream& is, read_arg& ra); +}; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_read_arg_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.cpp new file mode 100644 index 0000000000..e06e053504 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.cpp @@ -0,0 +1,179 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "test_case.h" + +#include <cstdlib> +#include <iomanip> +#include <sstream> + +namespace mrg +{ +namespace jtt +{ + +test_case::test_case(const unsigned test_case_num, const u_int32_t num_msgs, + const std::size_t min_data_size, const std::size_t max_data_size, const bool auto_deq, + const std::size_t min_xid_size, const std::size_t max_xid_size, const transient_t transient, + const external_t external, const std::string& comment): + _test_case_num(test_case_num), + _num_msgs(num_msgs), + _min_data_size(min_data_size), + _max_data_size(max_data_size), + _auto_dequeue(auto_deq), + _min_xid_size(min_xid_size), + _max_xid_size(max_xid_size), + _transient(transient), + _external(external), + _comment(comment), + _result_average(), + _result_jmap() +{} + +test_case::~test_case() +{} + +std::size_t +test_case::this_data_size() const +{ + if (_min_data_size == _max_data_size) + return _max_data_size; + std::size_t size_diff = _max_data_size - _min_data_size; + return _min_data_size + std::size_t(1.0 * std::rand() * size_diff/(RAND_MAX + 1.0)); +} + +std::size_t +test_case::this_xid_size() const +{ + // TODO: rework when probabilities are introduced. Assume 50% if _min_xid_size = 0 + if (_max_xid_size == 0) + return std::size_t(0); + if (_min_xid_size == 0) + { + if (1.0 * std::rand() / RAND_MAX < 0.5) + return std::size_t(0); + } + std::size_t size_diff = _max_xid_size - _min_xid_size; + return _min_xid_size + std::size_t(1.0 * std::rand() * size_diff/(RAND_MAX + 1.0)); +} + +bool +test_case::this_transience() const +{ + // TODO: rework when probabilities are introduced. Assume 50% if JTT_RANDOM + if (_transient == JTT_TRANSIENT) + return false; + if (_transient == JTT_PERSISTNET) + return true; + return 1.0 * std::rand() / RAND_MAX < 0.5; +} + +bool +test_case::this_external() const +{ + // TODO: rework when probabilities are introduced. Assume 50% if JDL_RANDOM + if (_external == JDL_INTERNAL) + return false; + if (_external == JDL_EXTERNAL) + return true; + return 1.0 * std::rand() / RAND_MAX < 0.5; +} + +void +test_case::add_result(test_case_result::shared_ptr& tcrp) +{ + _result_average.add_test_result(tcrp); + res_map_citr ari = _result_jmap.find(tcrp->jid()); + if (ari == _result_jmap.end()) + { + test_case_result_agregation::shared_ptr p(new test_case_result_agregation(tcrp->jid())); + p->add_test_result(tcrp); + _result_jmap.insert(res_map_pair(tcrp->jid(), p)); + } + else + ari->second->add_test_result(tcrp); +} + +void +test_case::set_fmt_chk_res(const bool res, const std::string& jid) +{ + _result_average.set_fmt_chk_res(res); + res_map_citr ari = _result_jmap.find(jid); + if (ari != _result_jmap.end()) + ari->second->set_fmt_chk_res(res); +} + +const test_case_result::shared_ptr +test_case::jmap_last(std::string& jid) const +{ + res_map_citr i = _result_jmap.find(jid); + if (i == _result_jmap.end()) + return test_case_result::shared_ptr(); + u_int32_t num_res = (*i).second->num_results(); + if (num_res) + return (*(*i).second)[num_res - 1]; + return test_case_result::shared_ptr(); +} + +void +test_case::clear() +{ + _result_average.clear(); + _result_jmap.clear(); +} + +const std::string +test_case::str() const +{ + std::ostringstream oss; + oss << "Test Parameters: Test case no. " << _test_case_num << ":" << std::endl; + oss << " Comment: " << _comment << std::endl; + oss << " Number of messages: " << _num_msgs << std::endl; + oss << " Data size: " << _min_data_size; + if (_min_data_size == _max_data_size) + oss << " bytes (fixed)" << std::endl; + else + oss << " - " << _max_data_size << " bytes" << std::endl; + oss << " XID size: " << _min_xid_size; + if (_min_xid_size == _max_xid_size) + oss << " bytes (fixed)" << std::endl; + else + oss << " - " << _max_xid_size << " bytes" << std::endl; + oss << " Auto-dequeue: " << (_auto_dequeue ? "true" : "false") << std::endl; + oss << " Persistence: "; + switch (_transient) + { + case JTT_TRANSIENT: oss << "TRANSIENT" << std::endl; break; + case JTT_PERSISTNET: oss << "PERSISTNET" << std::endl; break; + case JTT_RANDOM: oss << "RANDOM" << std::endl; break; + } + oss << " Message Data: "; + switch (_external) + { + case JDL_INTERNAL: oss << "INTERNAL"; break; + case JDL_EXTERNAL: oss << "EXTERNAL"; break; + case JDL_RANDOM: oss << "RANDOM"; break; + } + return oss.str(); +} + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.h new file mode 100644 index 0000000000..f72dd05f0c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case.h @@ -0,0 +1,110 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_test_case_hpp +#define mrg_jtt_test_case_hpp + +#include <boost/shared_ptr.hpp> +#include <cstddef> +#include <map> +#include "test_case_result.h" +#include "test_case_result_agregation.h" +#include <vector> + +namespace mrg +{ +namespace jtt +{ + + class test_case + { + public: + enum transient_type { JTT_TRANSIENT = 0, JTT_PERSISTNET, JTT_RANDOM }; + typedef transient_type transient_t; + + enum data_location { JDL_INTERNAL = 0, JDL_EXTERNAL, JDL_RANDOM }; + typedef data_location external_t; + + typedef boost::shared_ptr<test_case> shared_ptr; + + typedef std::map<std::string, test_case_result_agregation::shared_ptr> res_map; + typedef std::pair<std::string, test_case_result_agregation::shared_ptr> res_map_pair; + typedef res_map::const_iterator res_map_citr; + + private: + unsigned _test_case_num; + u_int32_t _num_msgs; + std::size_t _min_data_size; + std::size_t _max_data_size; + bool _auto_dequeue; + // TODO: add probability of transaction to these params + std::size_t _min_xid_size; + std::size_t _max_xid_size; + // TODO: change these enums (transient_t & external_t) to probabilities + transient_t _transient; + external_t _external; + std::string _comment; + + test_case_result_agregation _result_average; // overall average result + res_map _result_jmap; // map of per-journal averages + + public: + test_case(const unsigned test_case_num, const u_int32_t num_msgs, + const std::size_t min_data_size, const std::size_t max_data_size, + const bool auto_deq, const std::size_t min_xid_size, + const std::size_t max_xid_size, const transient_t transient, + const external_t external, const std::string& comment); + virtual ~test_case(); + + inline unsigned test_case_num() const { return _test_case_num; } + inline u_int32_t num_msgs() const { return _num_msgs; } + inline std::size_t min_data_size() const { return _min_data_size; } + inline std::size_t max_data_size() const { return _max_data_size; } + std::size_t this_data_size() const; + inline bool auto_deq() const { return _auto_dequeue; } + inline std::size_t min_xid_size() const { return _min_xid_size; } + inline std::size_t max_xid_size() const { return _max_xid_size; } + std::size_t this_xid_size() const; + inline transient_t transient() const { return _transient; } + bool this_transience() const; + inline external_t external() const { return _external; } + bool this_external() const; + inline const std::string& comment() const { return _comment; } + + void add_result(test_case_result::shared_ptr& p); + void set_fmt_chk_res(const bool res, const std::string& jid); + + inline const test_case_result_agregation& average() const { return _result_average; } + inline u_int32_t num_results() const { return _result_average.num_results(); } + inline unsigned num_jrnls() const { return _result_jmap.size(); } + inline res_map_citr jrnl_average(std::string& jid) const { return _result_jmap.find(jid); } + inline res_map_citr jmap_begin() const { return _result_jmap.begin(); } + inline res_map_citr jmap_end() const { return _result_jmap.end(); } + const test_case_result::shared_ptr jmap_last(std::string& jid) const; + + void clear(); + const std::string str() const; + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_test_case_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.cpp new file mode 100644 index 0000000000..2f88f265a5 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.cpp @@ -0,0 +1,201 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "test_case_result.h" + +#include <iomanip> +#include <sstream> + +namespace mrg +{ +namespace jtt +{ + +test_case_result::test_case_result(const std::string& jid): + _jid(jid), + _num_enq(0), + _num_deq(0), + _num_read(0), + _num_rproc(0), + _start_time(), + _stop_time(), + _stopped(false), + _test_time(), + _exception_list() +{} + +test_case_result::~test_case_result() +{} + +const std::string +test_case_result::test_time_str() const +{ + return _test_time.str(9); +} + +void +test_case_result::add_exception(const journal::jexception& e, const bool set_stop_time_flag) +{ + if (!_stopped && set_stop_time_flag) + { + set_stop_time(); + _stopped = true; + } + _exception_list.push_back(e.what()); +} + +void +test_case_result::add_exception(const std::string& err_str, const bool set_stop_time_flag) +{ + if (!_stopped && set_stop_time_flag) + { + set_stop_time(); + _stopped = true; + } + _exception_list.push_back(err_str); +} + +void +test_case_result::add_exception(const char* err_str, const bool set_stop_time_flag) +{ + if (!_stopped && set_stop_time_flag) + { + set_stop_time(); + _stopped = true; + } + _exception_list.push_back(err_str); +} + +void +test_case_result::clear() +{ + _num_enq = 0; + _num_deq = 0; + _num_read = 0; + _start_time.set_zero(); + _stop_time.set_zero(); + _test_time.set_zero(); + _exception_list.clear(); +} + +const std::string +test_case_result::str(const bool summary) const +{ + std::ostringstream oss; + if (summary) + { + oss << _jid << ":"; + oss << str_summary(); + if (_exception_list.size()) + oss << "; fail: " << _exception_list[0] << std::endl; + else + oss << "; ok" << std::endl; + } + else + { + oss << "--- Journal instance: jid=\"" << _jid << "\" ---" << std::endl; + oss << str_full(); + if (_exception_list.size()) + oss << " exception/error:" << _exception_list[0] << std::endl; + } + return oss.str(); +} + +const std::string +test_case_result::str_full() const +{ + const double t = _test_time.tv_sec + (_test_time.tv_nsec/1e9); + const bool no_exception = _exception_list.empty(); + std::ostringstream oss; + oss.setf(std::ios::fixed, std::ios::floatfield); + oss.precision(2); + if (no_exception) + { + oss.precision(6); + oss << " total test time: " << t << "s" << std::endl; + } + oss.precision(3); + oss << " total number enqueues: " << _num_enq; + if (no_exception) + oss << " (" << (_num_enq / t) << " enq/sec)"; + oss << std::endl; + oss << " total number dequeues: " << _num_deq; + if (no_exception) + oss << " (" << (_num_deq / t) << " deq/sec)"; + oss << std::endl; + oss << "total write operations: " << (_num_enq + _num_deq); + if (no_exception) + oss << " (" << ((_num_enq + _num_deq) / t) << " wrops/sec)"; + oss << std::endl; + oss << " total number reads: " << _num_read; + if (no_exception) + oss << " (" << (_num_read / t) << " rd/sec)"; + oss << std::endl; + oss << " total operations: " << (_num_enq + _num_deq + _num_read); + if (no_exception) + oss << " (" << ((_num_enq + _num_deq + _num_read) / t) << " ops/sec)"; + oss << std::endl; + oss << " overall result: " << (no_exception ? "PASS" : "*** FAIL ***") << std::endl; + return oss.str(); +} + +const std::string +test_case_result::str_summary() const +{ + const double t = _test_time.tv_sec + (_test_time.tv_nsec/1e9); + const bool no_exception = _exception_list.empty(); + std::ostringstream oss; + oss.setf(std::ios::fixed, std::ios::floatfield); + if (no_exception) + { + oss.precision(6); + oss << " t=" << t << "s;"; + } + else + oss << " exception"; + oss.precision(3); + oss << " enq=" << _num_enq; + if (no_exception) + oss << " (" << (_num_enq / t) << ")"; + oss << "; deq=" << _num_deq; + if (no_exception) + oss << " (" << (_num_deq / t) << ")"; + oss << "; wr=" << (_num_enq + _num_deq); + if (no_exception) + oss << " (" << ((_num_enq + _num_deq) / t) << ")"; + oss << "; rd=" << _num_read; + if (no_exception) + oss << " (" << (_num_read / t) << ")"; + oss << "; tot=" << (_num_enq + _num_deq + _num_read); + if (no_exception) + oss << " (" << ((_num_enq + _num_deq + _num_read) / t) << ")"; + return oss.str(); +} + +void +test_case_result::calc_test_time() +{ + if (!_start_time.is_zero() && _stop_time >= _start_time) + _test_time = _stop_time - _start_time; +} + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.h new file mode 100644 index 0000000000..d15f9d021d --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result.h @@ -0,0 +1,100 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_test_case_result_hpp +#define mrg_jtt_test_case_result_hpp + +#include <boost/shared_ptr.hpp> +#include <deque> +#include "qpid/legacystore/jrnl/jexception.h" +#include "qpid/legacystore/jrnl/time_ns.h" +#include <string> + +namespace mrg +{ +namespace jtt +{ + + class test_case_result + { + public: + typedef boost::shared_ptr<test_case_result> shared_ptr; + + typedef std::deque<std::string> elist; + typedef elist::const_iterator elist_citr; + + protected: + std::string _jid; + u_int32_t _num_enq; + u_int32_t _num_deq; + u_int32_t _num_read; // Messages actually read + u_int32_t _num_rproc; // Messages handled by read thread (not all are read) + journal::time_ns _start_time; + journal::time_ns _stop_time; + bool _stopped; + journal::time_ns _test_time; + elist _exception_list; + + public: + test_case_result(const std::string& jid); + virtual ~test_case_result(); + + inline const std::string& jid() const { return _jid; } + inline u_int32_t num_enq() const { return _num_enq; } + inline u_int32_t incr_num_enq() { return ++_num_enq; } + inline u_int32_t num_deq() const { return _num_deq; } + inline u_int32_t incr_num_deq() { return ++_num_deq; } + inline u_int32_t num_read() const { return _num_read; } + inline u_int32_t incr_num_read() { return ++_num_read; } + inline u_int32_t num_rproc() const { return _num_rproc; } + inline u_int32_t incr_num_rproc() { return ++_num_rproc; } + + inline const journal::time_ns& start_time() const { return _start_time; } + inline void set_start_time() { ::clock_gettime(CLOCK_REALTIME, &_start_time); } + inline const journal::time_ns& stop_time() const { return _stop_time; } + inline void set_stop_time() + { ::clock_gettime(CLOCK_REALTIME, &_stop_time); calc_test_time(); } + inline void set_test_time(const journal::time_ns& ts) { _test_time = ts; } + inline const journal::time_ns& test_time() const { return _test_time; } + const std::string test_time_str() const; + + void add_exception(const journal::jexception& e, const bool set_stop_time_flag = true); + void add_exception(const std::string& err_str, const bool set_stop_time_flag = true); + void add_exception(const char* err_str, const bool set_stop_time_flag = true); + inline bool exception() const { return _exception_list.size() > 0; } + inline unsigned exception_count() const { return _exception_list.size(); } + inline elist_citr begin() { return _exception_list.begin(); } + inline elist_citr end() { return _exception_list.end(); } + inline const std::string& operator[](unsigned i) { return _exception_list[i]; } + + void clear(); + const std::string str(const bool summary) const; + + protected: + const std::string str_full() const; + const std::string str_summary() const; + void calc_test_time(); + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_test_case_result_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.cpp new file mode 100644 index 0000000000..da439e71e8 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.cpp @@ -0,0 +1,185 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "test_case_result_agregation.h" + +#include <iomanip> +#include <sstream> + +namespace mrg +{ +namespace jtt +{ + +test_case_result_agregation::test_case_result_agregation(): + test_case_result("Average"), + _tc_average(true), + _fmt_chk_done(false), + _fmt_chk_err(false), + _res_list() +{ +} + +test_case_result_agregation::test_case_result_agregation(const std::string& jid): + test_case_result(jid), + _tc_average(false), + _fmt_chk_done(false), + _fmt_chk_err(false), + _res_list() +{} + +test_case_result_agregation::~test_case_result_agregation() +{} + +void +test_case_result_agregation::add_test_result(const test_case_result::shared_ptr& tcrp) +{ + if (_tc_average || _jid.compare(tcrp->jid()) == 0) + { + _num_enq += tcrp->num_enq(); + _num_deq += tcrp->num_deq(); + _num_read += tcrp->num_read(); + add_test_time(tcrp->test_time()); + _exception_list.insert(_exception_list.end(), tcrp->begin(), tcrp->end()); + _res_list.push_back(tcrp); + } +} + +bool +test_case_result_agregation::exception() const +{ + for (tcrp_list_citr i = _res_list.begin(); i < _res_list.end(); i++) + if ((*i)->exception()) + return true; + return false; +} + +unsigned +test_case_result_agregation::exception_count() const +{ + unsigned cnt = 0; + for (tcrp_list_citr i = _res_list.begin(); i < _res_list.end(); i++) + cnt += (*i)->exception_count(); + return cnt; +} + +void +test_case_result_agregation::clear() +{ + test_case_result::clear(); + _res_list.clear(); +} + +const std::string +test_case_result_agregation::str(const bool last_only, const bool summary) const +{ + std::ostringstream oss; + if (last_only) + oss << " " << _res_list.at(_res_list.size()-1)->str(summary); + else + { + for (tcrp_list_citr i=_res_list.begin(); i!=_res_list.end(); i++) + oss << " " << (*i)->str(summary); + } + if (_res_list.size() > 1) + oss << " " << (summary ? str_summary(last_only) : str_full(last_only)); + return oss.str(); +} + +const std::string +test_case_result_agregation::str_full(const bool /*last_only*/) const +{ + std::ostringstream oss; + oss.precision(2); + if (_tc_average) + oss << "Average across all journal instances:" << std::endl; + else + oss << "Average for jid=\"" << _jid << "\":" << std::endl; + oss << " total number results: " << _res_list.size() << std::endl; + oss << " number exceptions: " << _exception_list.size() << " (" << + (100.0 * _res_list.size() / _exception_list.size()) << "%)" << std::endl; + + oss << test_case_result::str_full(); + + if (_exception_list.size()) + { + unsigned n = 0; + oss << "List of exceptions/errors:" << std::endl; + for (elist_citr i = _exception_list.begin(); i != _exception_list.end(); i++, n++) + oss << " " << n << ". " << (*i) << std::endl; + } + + if (!_tc_average && _res_list.size() > 1) + { + oss << "Individual results:" << std::endl; + for (tcrp_list_citr i=_res_list.begin(); i!=_res_list.end(); i++) + oss << " " << (*i)->str(false) << std::endl; + oss << std::endl; + } + + return oss.str(); +} + +const std::string +test_case_result_agregation::str_summary(const bool /*last_only*/) const +{ + std::ostringstream oss; + if (_tc_average) + oss << "overall average [" << _res_list.size() << "]:"; + else + oss << "average (" << _res_list.size() << "):"; + + oss << test_case_result::str_summary(); + if (_fmt_chk_done) + oss << " fmt-chk=" << (_fmt_chk_err ? "fail" : "ok"); + + if (_exception_list.size()) + { + if (_tc_average) + oss << " fail: " << _exception_list.size() << " exception" + << (_exception_list.size()>1?"s":"") << std::endl; + else + { + if (_exception_list.size() == 1) + oss << " fail: " << *_exception_list.begin() << std::endl; + else + { + oss << std::endl; + unsigned n = 0; + for (elist_citr i = _exception_list.begin(); i != _exception_list.end(); i++, n++) + oss << " " << n << ". " << (*i) << std::endl; + } + } + } + else + oss << " ok" << std::endl; + return oss.str(); +} + +const journal::time_ns& +test_case_result_agregation::add_test_time(const journal::time_ns& t) +{ + _test_time += t; + return _test_time; +} + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.h new file mode 100644 index 0000000000..0b3998176c --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_result_agregation.h @@ -0,0 +1,81 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_test_case_result_agregation_hpp +#define mrg_jtt_test_case_result_agregation_hpp + +#include "test_case_result.h" + +#include <iostream> +#include <vector> + +namespace mrg +{ +namespace jtt +{ + + class test_case_result_agregation : public test_case_result + { + public: + typedef boost::shared_ptr<test_case_result_agregation> shared_ptr; + + typedef std::vector<test_case_result::shared_ptr> tcrp_list; + typedef tcrp_list::const_iterator tcrp_list_citr; + + private: + bool _tc_average; + bool _fmt_chk_done; + bool _fmt_chk_err; + tcrp_list _res_list; + + public: + test_case_result_agregation(); // used for average across jrnl instances + test_case_result_agregation(const std::string& jid); + virtual ~test_case_result_agregation(); + + void add_test_result(const test_case_result::shared_ptr& tcrp); + + inline bool tc_average_mode() const { return _tc_average; } + inline bool fmt_chk_done() const { return _fmt_chk_done; } + inline bool fmt_chk_res() const { return _fmt_chk_err; } + inline void set_fmt_chk_res(const bool err) + { _fmt_chk_done = true; _fmt_chk_err |= err; if (err) add_exception("Journal format error"); } + inline u_int32_t num_results() const { return _res_list.size(); } + inline tcrp_list_citr rlist_begin() const { return _res_list.begin(); } + inline tcrp_list_citr rlist_end() const { return _res_list.end(); } + inline const test_case_result::shared_ptr& operator[](unsigned i) const + { return _res_list[i]; } + bool exception() const; + unsigned exception_count() const; + + void clear(); + const std::string str(const bool last_only, const bool summary) const; + + private: + const std::string str_full(const bool last_only) const; + const std::string str_summary(const bool last_only) const; + const journal::time_ns& add_test_time(const journal::time_ns& t); + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_test_case_result_agregation_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.cpp new file mode 100644 index 0000000000..b818d6c7ae --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.cpp @@ -0,0 +1,169 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "test_case_set.h" + +#include <cstdlib> +#include <fstream> +#include <iostream> + +namespace mrg +{ +namespace jtt +{ + +test_case_set::test_case_set(): + _tc_list(), + _csv_ignored(0) +{} + +test_case_set::test_case_set(const std::string& csv_filename, const bool recover_mode, + const csv_map& cols): + _tc_list(), + _csv_ignored(0) +{ + append_from_csv(csv_filename, recover_mode, cols); +} + +test_case_set::~test_case_set() +{} + +void +test_case_set::append(const unsigned test_case_num, const u_int32_t num_msgs, + const std::size_t min_data_size, const std::size_t max_data_size, const bool auto_deq, + const std::size_t min_xid_size, const std::size_t max_xid_size, + const test_case::transient_t transient, const test_case::external_t external, + const std::string& comment) +{ + test_case::shared_ptr tcp(new test_case(test_case_num, num_msgs, min_data_size, + max_data_size, auto_deq, min_xid_size, max_xid_size, transient, external, comment)); + append(tcp); +} + + +#define CSV_BUFF_SIZE 2048 +void +test_case_set::append_from_csv(const std::string& csv_filename, const bool recover_mode, + const csv_map& cols) +{ + char buff[CSV_BUFF_SIZE]; + std::ifstream ifs(csv_filename.c_str()); + while (ifs.good()) + { + ifs.getline(buff, (std::streamsize)CSV_BUFF_SIZE); + if (ifs.gcount()) + { + test_case::shared_ptr tcp = get_tc_from_csv(buff, cols); + if (tcp.get()) + { + if (!recover_mode || tcp->auto_deq()) + append(tcp); + else + _csv_ignored++; + } + } + } +} + +test_case::shared_ptr +test_case_set::get_tc_from_csv(const std::string& csv_line, const csv_map& cols) +{ + unsigned test_case_num = 0; + u_int32_t num_msgs = 0; + std::size_t min_data_size = 0; + std::size_t max_data_size = 0; + bool auto_deq = false; + std::size_t min_xid_size = 0; + std::size_t max_xid_size = 0; + test_case::transient_t transient = test_case::JTT_TRANSIENT; + test_case::external_t external = test_case::JDL_INTERNAL; + std::string comment; + + csv_tok t(csv_line); + unsigned col_num = 0; + for (csv_tok_citr t_itr = t.begin(); t_itr != t.end(); ++t_itr, ++col_num) + { + const std::string& tok = *t_itr; + csv_map_citr m_citr = cols.find(col_num); + if (m_citr != cols.end()) + { + switch (m_citr->second) + { + case CSV_TC_NUM: + if (!tok.size() || tok[0] < '0' || tok[0] > '9') + return test_case::shared_ptr(); + test_case_num = unsigned(std::atol(tok.c_str())); + break; + case CSV_TC_NUM_MSGS: num_msgs = u_int32_t(std::atol(tok.c_str())); break; + case CSV_TC_MIN_DATA_SIZE: min_data_size = std::size_t(std::atol(tok.c_str())); break; + case CSV_TC_MAX_DATA_SIZE: max_data_size = std::size_t(std::atol(tok.c_str())); break; + case CSV_TC_AUTO_DEQ: + if (tok == "TRUE" || tok == "1") + auto_deq = true; + break; + case CSV_TC_MIN_XID_SIZE: min_xid_size = std::size_t(std::atol(tok.c_str())); break; + case CSV_TC_MAX_XID_SIZE: max_xid_size = std::size_t(std::atol(tok.c_str())); break; + case CSV_TC_TRANSIENT: + if (tok == "TRUE" || tok == "1") + transient = test_case::JTT_PERSISTNET; + else if (tok == "RANDOM" || tok == "-1") + transient = test_case::JTT_RANDOM; + break; + case CSV_TC_EXTERNAL: + if (tok == "TRUE" || tok == "1") + external = test_case::JDL_EXTERNAL; + else if (tok == "RANDOM" || tok == "-1") + external = test_case::JDL_RANDOM; + break; + case CSV_TC_COMMENT: comment = *t_itr; break; + } + } + } + if (col_num) + return test_case::shared_ptr(new test_case(test_case_num, num_msgs, min_data_size, + max_data_size, auto_deq, min_xid_size, max_xid_size, transient, external, comment)); + else + return test_case::shared_ptr(); +} + +// Static member initializations +// This csv_map is for use on the standard spreadsheet-derived test case csv files. +test_case_set::csv_map test_case_set::std_csv_map; +const bool test_case_set::_map_init = __init(); + +bool +test_case_set::__init() +{ + std_csv_map.insert(test_case_set::csv_pair(0, test_case_set::CSV_TC_NUM)); + std_csv_map.insert(test_case_set::csv_pair(5, test_case_set::CSV_TC_NUM_MSGS)); + std_csv_map.insert(test_case_set::csv_pair(7, test_case_set::CSV_TC_MIN_DATA_SIZE)); + std_csv_map.insert(test_case_set::csv_pair(8, test_case_set::CSV_TC_MAX_DATA_SIZE)); + std_csv_map.insert(test_case_set::csv_pair(11, test_case_set::CSV_TC_AUTO_DEQ)); + std_csv_map.insert(test_case_set::csv_pair(9, test_case_set::CSV_TC_MIN_XID_SIZE)); + std_csv_map.insert(test_case_set::csv_pair(10, test_case_set::CSV_TC_MAX_XID_SIZE)); + std_csv_map.insert(test_case_set::csv_pair(12, test_case_set::CSV_TC_TRANSIENT)); + std_csv_map.insert(test_case_set::csv_pair(13, test_case_set::CSV_TC_EXTERNAL)); + std_csv_map.insert(test_case_set::csv_pair(20, test_case_set::CSV_TC_COMMENT)); + return true; +} + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.h new file mode 100644 index 0000000000..94a1ee3172 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_case_set.h @@ -0,0 +1,99 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_test_case_set_hpp +#define mrg_jtt_test_case_set_hpp + +#include "test_case.h" + +#include <cstddef> +#include <boost/tokenizer.hpp> +#include <map> +#include <vector> + +namespace mrg +{ +namespace jtt +{ + + class test_case_set + { + public: + enum csv_col_enum { + CSV_TC_NUM = 0, + CSV_TC_NUM_MSGS, + CSV_TC_MIN_DATA_SIZE, + CSV_TC_MAX_DATA_SIZE, + CSV_TC_AUTO_DEQ, + CSV_TC_MIN_XID_SIZE, + CSV_TC_MAX_XID_SIZE, + CSV_TC_TRANSIENT, + CSV_TC_EXTERNAL, + CSV_TC_COMMENT }; + typedef std::pair<unsigned, csv_col_enum> csv_pair; + typedef std::map<unsigned, csv_col_enum> csv_map; + typedef csv_map::const_iterator csv_map_citr; + static csv_map std_csv_map; + + typedef std::vector<test_case::shared_ptr> tcl; + typedef tcl::iterator tcl_itr; + typedef tcl::const_iterator tcl_citr; + + typedef boost::tokenizer<boost::escaped_list_separator<char> > csv_tok; + typedef csv_tok::const_iterator csv_tok_citr; + + private: + tcl _tc_list; + static const bool _map_init; + unsigned _csv_ignored; + + public: + test_case_set(); + test_case_set(const std::string& csv_filename, const bool recover_mode, + const csv_map& cols = std_csv_map); + virtual ~test_case_set(); + + inline unsigned size() const { return _tc_list.size(); } + inline unsigned ignored() const { return _csv_ignored; } + inline bool empty() const { return _tc_list.empty(); } + + inline void append(const test_case::shared_ptr& tc) { _tc_list.push_back(tc); } + void append(const unsigned test_case_num, const u_int32_t num_msgs, + const std::size_t min_data_size, const std::size_t max_data_size, + const bool auto_deq, const std::size_t min_xid_size, + const std::size_t max_xid_size, const test_case::transient_t transient, + const test_case::external_t external, const std::string& comment); + void append_from_csv(const std::string& csv_filename, const bool recover_mode, + const csv_map& cols = std_csv_map); + inline tcl_itr begin() { return _tc_list.begin(); } + inline tcl_itr end() { return _tc_list.end(); } + inline const test_case::shared_ptr& operator[](unsigned i) { return _tc_list[i]; } + inline void clear() { _tc_list.clear(); } + + private: + test_case::shared_ptr get_tc_from_csv(const std::string& csv_line, const csv_map& cols); + static bool __init(); + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_test_case_set_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.cpp b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.cpp new file mode 100644 index 0000000000..de0b5dbfb9 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.cpp @@ -0,0 +1,218 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#include "test_mgr.h" + +#include <cstdlib> +#include <iostream> +#include <sys/stat.h> +#include "test_case_set.h" + +namespace mrg +{ +namespace jtt +{ + +test_mgr::test_mgr(args& args): + _ji_list(), + _args(args), + _err_flag(false), + _random_fn_ptr(random_fn) +{ + if (_args.seed) + std::srand(_args.seed); +} + +test_mgr::~test_mgr() +{} + +void +test_mgr::run() +{ + // TODO: complete tidy-up of non-summary (verbose) results, then pull through + // a command-line summary to control this. + // Idea: --summary: prints short results afterwards + // --verbose: prints long version as test progresses + // defualt: none of these, similar to current summary = true version. + const bool summary = true; + + std::cout << "CSV file: \"" << _args.test_case_csv_file_name << "\""; + test_case_set tcs(_args.test_case_csv_file_name, _args.recover_mode); + + if (tcs.size()) + { + std::cout << " (found " << tcs.size() << " test case" << (tcs.size() != 1 ? "s" : "") << + ")" << std::endl; + if (tcs.ignored()) + std::cout << "WARNING: " << tcs.ignored() << " test cases were ignored. (All test " + "cases without auto-dequeue are ignored when recover-mode is selected.)" << + std::endl; + _args.print_args(); + } + else if(tcs.ignored()) + { + std::cout << " WARNING: All " << tcs.ignored() << " test case(s) were ignored. (All test " + "cases without auto-dequeue are ignored when recover-mode is selected.)" << + std::endl; + } + else + std::cout << " (WARNING: This CSV file is empty or does not exist.)" << std::endl; + + do + { + unsigned u = 0; + if (_args.randomize) + random_shuffle(tcs.begin(), tcs.end(), _random_fn_ptr); + for (test_case_set::tcl_itr tci = tcs.begin(); tci != tcs.end(); tci++, u++) + { + if (summary) + std::cout << "Test case " << (*tci)->test_case_num() << ": \"" << + (*tci)->comment() << "\"" << std::endl; + else + std::cout << (*tci)->str() << std::endl; + if (!_args.reuse_instance || _ji_list.empty()) + initialize_jrnls(); + for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++) + (*jii)->init_tc(*tci, &_args); + for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++) + (*jii)->run_tc(); + for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++) + (*jii)->tc_wait_compl(); + + if (_args.format_chk) + { + for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++) + { + jrnl_init_params::shared_ptr jpp = (*jii)->params(); + std::string ja = _args.jfile_analyzer; + if (ja.empty()) ja = "./jfile_chk.py"; + if (!exists(ja)) + { + std::ostringstream oss; + oss << "ERROR: Validation program \"" << ja << "\" does not exist" << std::endl; + throw std::runtime_error(oss.str()); + } + std::ostringstream oss; + oss << ja << " -b " << jpp->base_filename(); + // TODO: When jfile_check.py can handle previously recovered journals for + // specific tests, then remove this exclusion. + if (!_args.recover_mode) + { + oss << " -c " << _args.test_case_csv_file_name; + oss << " -t " << (*tci)->test_case_num(); + } + oss << " -q " << jpp->jdir(); + bool res = system(oss.str().c_str()) != 0; + (*tci)->set_fmt_chk_res(res, jpp->jid()); + if (res) _err_flag = true; + } + } + + if (!_args.recover_mode && !_args.keep_jrnls) + for (ji_list_citr jii=_ji_list.begin(); jii!=_ji_list.end(); jii++) + try { mrg::journal::jdir::delete_dir((*jii)->jrnl_dir()); } + catch (...) {} // TODO - work out exception strategy for failure here... + + print_results(*tci, summary); + if ((*tci)->average().exception()) + _err_flag = true; + if (_abort || (!_args.repeat_flag && _signal)) + break; + if (_args.pause_secs && tci != tcs.end()) + ::usleep(_args.pause_secs * 1000000); + } + } + while (_args.repeat_flag && !_signal); +} + +// static fn: +void +test_mgr::signal_handler(int sig) +{ + if (_signal) + _abort = true; + _signal = sig; + std::cout << std::endl; + std::cout << "********************************" << std::endl; + std::cout << "Caught signal " << sig << std::endl; + if (_abort) + std::cout << "Aborting..." << std::endl; + else + std::cout << "Completing current test cycle..." << std::endl; + std::cout << "********************************" << std::endl << std::endl; +} + +bool +test_mgr::exists(std::string fname) +{ + struct stat s; + if (::stat(fname.c_str(), &s)) + { + if (errno == ENOENT) // No such dir or file + return false; + // Throw for any other condition + std::ostringstream oss; + oss << "ERROR: test_mgr::exists(): file=\"" << fname << "\": " << FORMAT_SYSERR(errno); + throw std::runtime_error(oss.str()); + } + return true; +} + +void +test_mgr::initialize_jrnls() +{ + _ji_list.clear(); + for (unsigned i=0; i<_args.num_jrnls; i++) + { + std::ostringstream jid; + jid << std::hex << std::setfill('0'); + jid << "test_" << std::setw(4) << std::hex << i; + std::ostringstream jdir; + jdir << _args.journal_dir << "/" << jid.str(); + jrnl_init_params::shared_ptr jpp(new jrnl_init_params(jid.str(), jdir.str(), jid.str())); + jrnl_instance::shared_ptr jip(new jrnl_instance(jpp)); + _ji_list.push_back(jip); + } +} + +void +test_mgr::print_results(test_case::shared_ptr tcp, const bool summary) +{ + if (!summary) + std::cout << " === Results ===" << std::endl; + +// TODO - the reporting is broken when --repeat is used. The following commented-out +// section was an attempt to fix it, but there are too many side-effects. +// for (test_case::res_map_citr i=tcp->jmap_begin(); i!=tcp->jmap_end(); i++) +// std::cout << (*i).second->str(summary, summary); +// if (tcp->num_jrnls() > 1) + std::cout << tcp->average().str(false, summary); + + if (!summary) + std::cout << std::endl; +} + +// static instances +volatile sig_atomic_t test_mgr::_signal = 0; +volatile bool test_mgr::_abort = false; + +} // namespace jtt +} // namespace mrg diff --git a/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.h b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.h new file mode 100644 index 0000000000..e608ac6280 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/jtt/test_mgr.h @@ -0,0 +1,68 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +#ifndef mrg_jtt_test_mgr_hpp +#define mrg_jtt_test_mgr_hpp + +#include "args.h" +#include <csignal> +#include <cstdlib> +#include "jrnl_instance.h" + +namespace mrg +{ +namespace jtt +{ + class test_mgr + { + public: + typedef std::vector<jrnl_instance::shared_ptr> ji_list; + typedef ji_list::iterator ji_list_itr; + typedef ji_list::const_iterator ji_list_citr; + + private: + ji_list _ji_list; + args& _args; + bool _err_flag; + ptrdiff_t (*_random_fn_ptr)(const ptrdiff_t i); + static volatile std::sig_atomic_t _signal; + static volatile bool _abort; + + public: + test_mgr(args& args); + virtual ~test_mgr(); + void run(); + inline bool error() const { return _err_flag; } + + static void signal_handler(int signal); + + private: + static bool exists(std::string file_name); + void initialize_jrnls(); + void print_results(test_case::shared_ptr tcp, const bool summary); + inline static ptrdiff_t random_fn(const ptrdiff_t i) + { return static_cast<ptrdiff_t>(1.0 * i * std::rand() / RAND_MAX); } + }; + +} // namespace jtt +} // namespace mrg + +#endif // ifndef mrg_jtt_test_mgr_hpp diff --git a/qpid/cpp/src/tests/legacystore/jrnl/prof b/qpid/cpp/src/tests/legacystore/jrnl/prof new file mode 100755 index 0000000000..2abe7baa4a --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/prof @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +mkdir -p profile +opcontrol --setup --no-vmlinux --separate=library +opcontrol --start +# -- Do stuff here -- +./jtest wtests.csv 264 +# -- End of stuff -- +opcontrol --stop +opcontrol --dump +opcontrol --shutdown +opreport -l ./jtest +opannotate --source --output-dir=profile ./jtest diff --git a/qpid/cpp/src/tests/legacystore/jrnl/run-journal-tests b/qpid/cpp/src/tests/legacystore/jrnl/run-journal-tests new file mode 100755 index 0000000000..e169e39c60 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/run-journal-tests @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +if test x${TMP_DATA_DIR} == x; then + export TMP_DATA_DIR=/tmp +fi +fail=0 +num_jrnls=3 + +# Run jtt using default test set +echo +echo "===== Mode 1: New journal instance, no recover =====" +jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --format-chk --num-jrnls ${num_jrnls} || fail=1 +rm -rf ${TMP_DATA_DIR}/test_0* +echo +echo "===== Mode 2: Re-use journal instance, no recover =====" +jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --reuse-instance --format-chk --num-jrnls ${num_jrnls} || fail=1 +rm -rf ${TMP_DATA_DIR}/test_0* +echo +echo "===== Mode 3: New journal instance, recover previous test journal =====" +jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --recover-mode --format-chk --num-jrnls ${num_jrnls} || fail=1 +rm -rf ${TMP_DATA_DIR}/test_0* +echo +echo "===== Mode 4: Re-use journal instance, recover previous test journal =====" +jtt/jtt --analyzer ../../tools/store_chk --jrnl-dir ${TMP_DATA_DIR} --csv jtt/jtt.csv --reuse-instance --recover-mode --format-chk --num-jrnls ${num_jrnls} || fail=1 +rm -rf ${TMP_DATA_DIR}/test_0* +echo + +exit $fail diff --git a/qpid/cpp/src/tests/legacystore/jrnl/tests.ods b/qpid/cpp/src/tests/legacystore/jrnl/tests.ods Binary files differnew file mode 100644 index 0000000000..d900374321 --- /dev/null +++ b/qpid/cpp/src/tests/legacystore/jrnl/tests.ods |