diff options
Diffstat (limited to 'qpid/cpp/src/tests')
-rw-r--r-- | qpid/cpp/src/tests/InitialStatusMap.cpp | 76 | ||||
-rw-r--r-- | qpid/cpp/src/tests/StoreStatus.cpp | 109 | ||||
-rw-r--r-- | qpid/cpp/src/tests/cluster.mk | 3 | ||||
-rwxr-xr-x | qpid/cpp/src/tests/cluster_tests.py | 105 | ||||
-rwxr-xr-x | qpid/cpp/src/tests/clustered_replication_test | 6 | ||||
-rwxr-xr-x | qpid/cpp/src/tests/run_cluster_tests | 1 |
6 files changed, 241 insertions, 59 deletions
diff --git a/qpid/cpp/src/tests/InitialStatusMap.cpp b/qpid/cpp/src/tests/InitialStatusMap.cpp index c3587965e5..e6a3ec1620 100644 --- a/qpid/cpp/src/tests/InitialStatusMap.cpp +++ b/qpid/cpp/src/tests/InitialStatusMap.cpp @@ -26,6 +26,7 @@ using namespace std; using namespace qpid::cluster; using namespace qpid::framing; +using namespace qpid::framing::cluster; using namespace boost::assign; namespace qpid { @@ -35,8 +36,19 @@ QPID_AUTO_TEST_SUITE(InitialStatusMapTestSuite) typedef InitialStatusMap::Status Status; -Status activeStatus(const Uuid& id=Uuid()) { return Status(ProtocolVersion(), true, false, id, 0, ""); } -Status newcomerStatus(const Uuid& id=Uuid()) { return Status(ProtocolVersion(), false, false, id, 0, ""); } +Status activeStatus(const Uuid& id=Uuid()) { + return Status(ProtocolVersion(), 0, true, id, + STORE_STATE_NO_STORE, Uuid(), Uuid()); +} + +Status newcomerStatus(const Uuid& id=Uuid()) { + return Status(ProtocolVersion(), 0, false, id, + STORE_STATE_NO_STORE, Uuid(), Uuid()); +} + +Status storeStatus(bool active, StoreState state, Uuid start=Uuid(), Uuid stop=Uuid()) { + return Status(ProtocolVersion(), 0, active, Uuid(), state, start, stop); +} QPID_AUTO_TEST_CASE(testFirstInCluster) { // Single member is first in cluster. @@ -173,6 +185,66 @@ QPID_AUTO_TEST_CASE(testInitialSize) { BOOST_CHECK(map.isComplete()); } +QPID_AUTO_TEST_CASE(testAllCleanNoUpdate) { + InitialStatusMap map(MemberId(0), 3); + map.configChange(list_of<MemberId>(0)(1)(2)); + map.received(MemberId(0), storeStatus(false, STORE_STATE_CLEAN_STORE)); + map.received(MemberId(1), storeStatus(false, STORE_STATE_CLEAN_STORE)); + map.received(MemberId(2), storeStatus(false, STORE_STATE_CLEAN_STORE)); + BOOST_CHECK(!map.isUpdateNeeded()); +} + +QPID_AUTO_TEST_CASE(testAllEmptyNoUpdate) { + InitialStatusMap map(MemberId(0), 3); + map.configChange(list_of<MemberId>(0)(1)(2)); + map.received(MemberId(0), storeStatus(false, STORE_STATE_EMPTY_STORE)); + map.received(MemberId(1), storeStatus(false, STORE_STATE_EMPTY_STORE)); + map.received(MemberId(2), storeStatus(false, STORE_STATE_EMPTY_STORE)); + BOOST_CHECK(map.isComplete()); + BOOST_CHECK(!map.isUpdateNeeded()); +} + +QPID_AUTO_TEST_CASE(testAllNoStoreNoUpdate) { + InitialStatusMap map(MemberId(0), 3); + map.configChange(list_of<MemberId>(0)(1)(2)); + map.received(MemberId(0), storeStatus(false, STORE_STATE_NO_STORE)); + map.received(MemberId(1), storeStatus(false, STORE_STATE_NO_STORE)); + map.received(MemberId(2), storeStatus(false, STORE_STATE_NO_STORE)); + BOOST_CHECK(map.isComplete()); + BOOST_CHECK(!map.isUpdateNeeded()); +} + +QPID_AUTO_TEST_CASE(testDirtyNeedUpdate) { + InitialStatusMap map(MemberId(0), 3); + map.configChange(list_of<MemberId>(0)(1)(2)); + map.received(MemberId(0), storeStatus(false, STORE_STATE_DIRTY_STORE)); + map.received(MemberId(1), storeStatus(false, STORE_STATE_CLEAN_STORE)); + map.received(MemberId(2), storeStatus(false, STORE_STATE_CLEAN_STORE)); + BOOST_CHECK(map.transitionToComplete()); + BOOST_CHECK(map.isUpdateNeeded()); +} + +QPID_AUTO_TEST_CASE(testEmptyNeedUpdate) { + InitialStatusMap map(MemberId(0), 3); + map.configChange(list_of<MemberId>(0)(1)(2)); + map.received(MemberId(0), storeStatus(false, STORE_STATE_EMPTY_STORE)); + map.received(MemberId(1), storeStatus(false, STORE_STATE_CLEAN_STORE)); + map.received(MemberId(2), storeStatus(false, STORE_STATE_CLEAN_STORE)); + BOOST_CHECK(map.transitionToComplete()); + BOOST_CHECK(map.isUpdateNeeded()); +} + +QPID_AUTO_TEST_CASE(testEmptyAlone) { + InitialStatusMap map(MemberId(0), 1); + map.configChange(list_of<MemberId>(0)); + map.received(MemberId(0), storeStatus(false, STORE_STATE_EMPTY_STORE)); + BOOST_CHECK(map.transitionToComplete()); + BOOST_CHECK(!map.isUpdateNeeded()); +} + +// FIXME aconway 2009-11-20: consistency tests for mixed stores, +// tests for manual intervention case. + QPID_AUTO_TEST_SUITE_END() }} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/StoreStatus.cpp b/qpid/cpp/src/tests/StoreStatus.cpp new file mode 100644 index 0000000000..37ba19e34a --- /dev/null +++ b/qpid/cpp/src/tests/StoreStatus.cpp @@ -0,0 +1,109 @@ + /* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * Licensed 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 "test_tools.h" +#include "qpid/cluster/StoreStatus.h" +#include "qpid/framing/Uuid.h" +#include <boost/assign.hpp> +#include <boost/filesystem/operations.hpp> + +using namespace std; +using namespace qpid::cluster; +using namespace qpid::framing; +using namespace qpid::framing::cluster; +using namespace boost::assign; +using namespace boost::filesystem; + + +namespace qpid { +namespace tests { + +QPID_AUTO_TEST_SUITE(StoreStatusTestSuite) + +const char* TEST_DIR = "StoreStatus.tmp"; + +QPID_AUTO_TEST_CASE(testLoadEmpty) { + create_directory(TEST_DIR); + StoreStatus ss(TEST_DIR); + BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_NO_STORE); + BOOST_CHECK(!ss.getStart()); + BOOST_CHECK(!ss.getStop()); + ss.load(); + BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_EMPTY_STORE); + BOOST_CHECK(!ss.getStop()); + remove_all(TEST_DIR); +} + +QPID_AUTO_TEST_CASE(testSaveLoadDirty) { + create_directory(TEST_DIR); + Uuid start = Uuid(true); + StoreStatus ss(TEST_DIR); + ss.load(); + ss.dirty(start); + BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_DIRTY_STORE); + + StoreStatus ss2(TEST_DIR); + ss2.load(); + BOOST_CHECK_EQUAL(ss2.getState(), STORE_STATE_DIRTY_STORE); + BOOST_CHECK_EQUAL(ss2.getStart(), start); + BOOST_CHECK(!ss2.getStop()); + remove_all(TEST_DIR); +} + +QPID_AUTO_TEST_CASE(testSaveLoadClean) { + create_directory(TEST_DIR); + Uuid start = Uuid(true); + Uuid stop = Uuid(true); + StoreStatus ss(TEST_DIR); + ss.load(); + ss.dirty(start); + ss.clean(stop); + BOOST_CHECK_EQUAL(ss.getState(), STORE_STATE_CLEAN_STORE); + + StoreStatus ss2(TEST_DIR); + ss2.load(); + BOOST_CHECK_EQUAL(ss2.getState(), STORE_STATE_CLEAN_STORE); + BOOST_CHECK_EQUAL(ss2.getStart(), start); + BOOST_CHECK_EQUAL(ss2.getStop(), stop); + remove_all(TEST_DIR); +} + +QPID_AUTO_TEST_CASE(testMarkDirty) { + // Save clean then mark to dirty. + create_directory(TEST_DIR); + Uuid start = Uuid(true); + Uuid stop = Uuid(true); + StoreStatus ss(TEST_DIR); + ss.load(); + ss.dirty(start); + ss.clean(stop); + ss.dirty(start); + + StoreStatus ss2(TEST_DIR); + ss2.load(); + BOOST_CHECK_EQUAL(ss2.getState(), STORE_STATE_DIRTY_STORE); + BOOST_CHECK_EQUAL(ss2.getStart(), start); + BOOST_CHECK(!ss2.getStop()); + remove_all(TEST_DIR); +} + +QPID_AUTO_TEST_SUITE_END() + +}} // namespace qpid::tests diff --git a/qpid/cpp/src/tests/cluster.mk b/qpid/cpp/src/tests/cluster.mk index f33f87ee62..20053788e4 100644 --- a/qpid/cpp/src/tests/cluster.mk +++ b/qpid/cpp/src/tests/cluster.mk @@ -76,7 +76,8 @@ cluster_test_SOURCES = \ ForkedBroker.cpp \ PartialFailure.cpp \ ClusterFailover.cpp \ - InitialStatusMap.cpp + InitialStatusMap.cpp \ + StoreStatus.cpp cluster_test_LDADD=$(lib_client) $(lib_broker) ../cluster.la -lboost_unit_test_framework diff --git a/qpid/cpp/src/tests/cluster_tests.py b/qpid/cpp/src/tests/cluster_tests.py index ed39277f77..65c91b1d81 100755 --- a/qpid/cpp/src/tests/cluster_tests.py +++ b/qpid/cpp/src/tests/cluster_tests.py @@ -18,13 +18,18 @@ # under the License. # -import os, signal, sys, time +import os, signal, sys, time, imp from qpid import datatypes, messaging from qpid.brokertest import * from qpid.harness import Skipped from qpid.messaging import Message from threading import Thread +from logging import getLogger +log = getLogger("qpid.cluster_tests") + +# Import scripts as modules +qpid_cluster=import_script(checkenv("QPID_CLUSTER_EXEC")) class ShortTests(BrokerTest): """Short cluster functionality tests.""" @@ -34,8 +39,8 @@ class ShortTests(BrokerTest): # Start a cluster, send some messages to member 0. cluster = self.cluster(2) s0 = cluster[0].connect().session() - s0.sender("q; {create:always}").send(messaging.Message("x")) - s0.sender("q; {create:always}").send(messaging.Message("y")) + s0.sender("q; {create:always}").send(Message("x")) + s0.sender("q; {create:always}").send(Message("y")) s0.connection.close() # Verify messages available on member 1. @@ -52,35 +57,6 @@ class ShortTests(BrokerTest): self.assertEqual("y", m.content) s2.connection.close() - def test_cluster_size(self): - """Verify cluster startup waits for N brokers if --cluster-size=N""" - class ConnectThread(Thread): - def __init__(self, broker): - Thread.__init__(self) - self.broker=broker - self.connected = False - self.error = None - - def run(self): - try: - self.broker.connect() - self.connected = True - except Exception, e: self.error = RethrownException(e) - - cluster = self.cluster(1, args=["--cluster-size=3"], wait_for_start=False) - c = ConnectThread(cluster[0]) - c.start() - time.sleep(.01) - assert not c.connected - cluster.start(wait_for_start=False) - time.sleep(.01) - assert not c.connected - cluster.start(wait_for_start=False) - c.join(1) - assert not c.isAlive() # Join didn't time out - assert c.connected - if c.error: raise c.error - class LongTests(BrokerTest): """Tests that can run for a long time if -DDURATION=<minutes> is set""" def duration(self): @@ -120,20 +96,22 @@ class StoreTests(BrokerTest): """ Cluster tests that can only be run if there is a store available. """ - args = ["--load-module",BrokerTest.store_lib] + def args(self): + assert BrokerTest.store_lib + return ["--load-module", BrokerTest.store_lib] def test_store_loaded(self): """Ensure we are indeed loading a working store""" - broker = self.broker(self.args, name="recoverme", expect=EXPECT_EXIT_FAIL) - m = messaging.Message("x", durable=True) + broker = self.broker(self.args(), name="recoverme", expect=EXPECT_EXIT_FAIL) + m = Message("x", durable=True) broker.send_message("q", m) broker.kill() - broker = self.broker(self.args, name="recoverme") + broker = self.broker(self.args(), name="recoverme") self.assertEqual("x", broker.get_message("q").content) def test_kill_restart(self): """Verify we can kill/resetart a broker with store in a cluster""" - cluster = self.cluster(1, self.args) + cluster = self.cluster(1, self.args()) cluster.start("restartme", expect=EXPECT_EXIT_FAIL).kill() # Send a message, retrieve from the restarted broker @@ -141,19 +119,42 @@ class StoreTests(BrokerTest): m = cluster.start("restartme").get_message("q") self.assertEqual("x", m.content) - def test_total_shutdown(self): - """Test we use the correct store to recover after total shutdown""" - cluster = self.cluster(2, args=self.args, expect=EXPECT_EXIT_FAIL) - cluster[0].send_message("q", Message("a", durable=True)) - cluster[0].kill() - self.assertEqual("a", cluster[1].get_message("q").content) - cluster[1].send_message("q", Message("b", durable=True)) - cluster[1].kill() - - # Start 1 first, we should see its store used. - cluster.start(name=cluster.name+"-1") - cluster.start(name=cluster.name+"-0") - self.assertEqual("b", cluster[2].get_message("q").content) - + def test_persistent_restart(self): + """Verify persistent cluster shutdown/restart scenarios""" + cluster = self.cluster(0, args=self.args() + ["--cluster-size=3"]) + a = cluster.start("a", expect=EXPECT_EXIT_OK, wait_for_start=False) + b = cluster.start("b", expect=EXPECT_EXIT_OK, wait_for_start=False) + c = cluster.start("c", expect=EXPECT_EXIT_FAIL, wait_for_start=True) + a.send_message("q", Message("1", durable=True)) + # Kill & restart one member. + c.kill() + self.assertEqual(a.get_message("q").content, "1") + a.send_message("q", Message("2", durable=True)) + c = cluster.start("c", expect=EXPECT_EXIT_OK) + self.assertEqual(c.get_message("q").content, "2") + # Shut down the entire cluster cleanly and bring it back up + a.send_message("q", Message("3", durable=True)) + qpid_cluster.main(["qpid-cluster", "-kf", a.host_port()]) + a = cluster.start("a", wait_for_start=False) + b = cluster.start("b", wait_for_start=False) + c = cluster.start("c", wait_for_start=True) + self.assertEqual(a.get_message("q").content, "3") + + def test_persistent_partial_failure(self): + # Kill 2 members, shut down the last cleanly then restart + # Ensure we use the clean database + cluster = self.cluster(0, args=self.args() + ["--cluster-size=3"]) + a = cluster.start("a", expect=EXPECT_EXIT_FAIL, wait_for_start=False) + b = cluster.start("b", expect=EXPECT_EXIT_FAIL, wait_for_start=False) + c = cluster.start("c", expect=EXPECT_EXIT_OK, wait_for_start=True) + a.send_message("q", Message("4", durable=True)) + a.kill() + b.kill() + self.assertEqual(c.get_message("q").content, "4") + c.send_message("q", Message("clean", durable=True)) + qpid_cluster.main(["qpid-cluster", "-kf", c.host_port()]) + a = cluster.start("a", wait_for_start=False) + b = cluster.start("b", wait_for_start=False) + c = cluster.start("c", wait_for_start=True) + self.assertEqual(a.get_message("q").content, "clean") - diff --git a/qpid/cpp/src/tests/clustered_replication_test b/qpid/cpp/src/tests/clustered_replication_test index 4f13b4672c..49d788f41e 100755 --- a/qpid/cpp/src/tests/clustered_replication_test +++ b/qpid/cpp/src/tests/clustered_replication_test @@ -54,10 +54,10 @@ if test -d $PYTHON_DIR; then . $srcdir/ais_check #todo: these cluster names need to be unique to prevent clashes - PRIMARY_CLUSTER=PRIMARY_$(hostname)_$(pwd) - DR_CLUSTER=DR_$(hostname)_$(pwd) + PRIMARY_CLUSTER=PRIMARY_$(hostname)_$$ + DR_CLUSTER=DR_$(hostname)_$$ - GENERAL_OPTS="--auth no --no-module-dir --no-data-dir --daemon --port 0 --log-enable notice+ --log-to-stderr false" + GENERAL_OPTS="--auth no --no-module-dir --no-data-dir --daemon --port 0 --log-to-stderr false" PRIMARY_OPTS="--load-module ../.libs/replicating_listener.so --create-replication-queue true --replication-queue REPLICATION_QUEUE --load-module ../.libs/cluster.so --cluster-name $PRIMARY_CLUSTER" DR_OPTS="--load-module ../.libs/replication_exchange.so --load-module ../.libs/cluster.so --cluster-name $DR_CLUSTER" diff --git a/qpid/cpp/src/tests/run_cluster_tests b/qpid/cpp/src/tests/run_cluster_tests index 9546ddf938..b6c144bb05 100755 --- a/qpid/cpp/src/tests/run_cluster_tests +++ b/qpid/cpp/src/tests/run_cluster_tests @@ -37,7 +37,6 @@ mkdir -p $OUTDIR CLUSTER_TESTS_IGNORE=${CLUSTER_TESTS_IGNORE:--i cluster_tests.StoreTests.* -I $srcdir/cluster_tests.fail} CLUSTER_TESTS=${CLUSTER_TESTS:-$*} -set -x with_ais_group $TEST_EXEC -DOUTDIR=$OUTDIR -m cluster_tests $CLUSTER_TESTS_IGNORE $CLUSTER_TESTS || exit 1 rm -rf $OUTDIR #exit 0 |