summaryrefslogtreecommitdiff
path: root/qpid/cpp/src/tests
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/cpp/src/tests')
-rw-r--r--qpid/cpp/src/tests/InitialStatusMap.cpp76
-rw-r--r--qpid/cpp/src/tests/StoreStatus.cpp109
-rw-r--r--qpid/cpp/src/tests/cluster.mk3
-rwxr-xr-xqpid/cpp/src/tests/cluster_tests.py105
-rwxr-xr-xqpid/cpp/src/tests/clustered_replication_test6
-rwxr-xr-xqpid/cpp/src/tests/run_cluster_tests1
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