From 5aadc56b66c2d8504e9177b553359d2c01b367f4 Mon Sep 17 00:00:00 2001 From: James Page Date: Tue, 14 Oct 2014 09:34:59 +0100 Subject: [PATCH] Add basic tests for 0mq matchmakers Basic unit tests for in-memory (localhost), ring and Redis based matchmakers. Redis tests are opportunitic based on a redis-server being installed and running. Test are currently skipped under py3 due to direct dependency on eventlet. Co-Authored-By: Kapil Thangavelu Change-Id: I59c4d095033370ab94f645af0d50e2502530f816 Partial-bug: #1302941 --- test-requirements.txt | 3 + tests/drivers/test_matchmaker.py | 69 +++++++++++++++++++++++ tests/drivers/test_matchmaker_redis.py | 78 ++++++++++++++++++++++++++ tests/drivers/test_matchmaker_ring.py | 73 ++++++++++++++++++++++++ 4 files changed, 223 insertions(+) create mode 100644 tests/drivers/test_matchmaker.py create mode 100644 tests/drivers/test_matchmaker_redis.py create mode 100644 tests/drivers/test_matchmaker_ring.py diff --git a/test-requirements.txt b/test-requirements.txt index dae910de3..c60917857 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -18,6 +18,9 @@ oslotest>=1.2.0 # Apache-2.0 # for test_qpid qpid-python +# for test_matchmaker_redis +redis>=2.10.0 + # when we can require tox>= 1.4, this can go into tox.ini: # [testenv:cover] # deps = {[testenv]deps} coverage diff --git a/tests/drivers/test_matchmaker.py b/tests/drivers/test_matchmaker.py new file mode 100644 index 000000000..0de2e6d2f --- /dev/null +++ b/tests/drivers/test_matchmaker.py @@ -0,0 +1,69 @@ +# Copyright 2014 Canonical, Ltd. +# +# 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. + +import testtools + +from oslo.utils import importutils +from tests import utils as test_utils + +# NOTE(jamespage) matchmaker tied directly to eventlet +# which is not yet py3 compatible - skip if import fails +matchmaker = ( + importutils.try_import('oslo.messaging._drivers.matchmaker')) + + +@testtools.skipIf(not matchmaker, "matchmaker/eventlet unavailable") +class MatchmakerTest(test_utils.BaseTestCase): + + def test_fanout_binding(self): + matcher = matchmaker.MatchMakerBase() + matcher.add_binding( + matchmaker.FanoutBinding(), matchmaker.DirectExchange()) + self.assertEqual(matcher.queues('hello.world'), []) + self.assertEqual( + matcher.queues('fanout~fantasy.unicorn'), + [('fanout~fantasy.unicorn', 'unicorn')]) + self.assertEqual( + matcher.queues('fanout~fantasy.pony'), + [('fanout~fantasy.pony', 'pony')]) + + def test_topic_binding(self): + matcher = matchmaker.MatchMakerBase() + matcher.add_binding( + matchmaker.TopicBinding(), matchmaker.StubExchange()) + self.assertEqual( + matcher.queues('hello-world'), [('hello-world', None)]) + + def test_direct_binding(self): + matcher = matchmaker.MatchMakerBase() + matcher.add_binding( + matchmaker.DirectBinding(), matchmaker.StubExchange()) + self.assertEqual( + matcher.queues('hello.server'), [('hello.server', None)]) + self.assertEqual(matcher.queues('hello-world'), []) + + def test_localhost_match(self): + matcher = matchmaker.MatchMakerLocalhost() + self.assertEqual( + matcher.queues('hello.server'), [('hello.server', 'server')]) + + # Gets remapped due to localhost exchange + # all bindings default to first match. + self.assertEqual( + matcher.queues('fanout~testing.server'), + [('fanout~testing.localhost', 'localhost')]) + + self.assertEqual( + matcher.queues('hello-world'), + [('hello-world.localhost', 'localhost')]) diff --git a/tests/drivers/test_matchmaker_redis.py b/tests/drivers/test_matchmaker_redis.py new file mode 100644 index 000000000..1c20dabde --- /dev/null +++ b/tests/drivers/test_matchmaker_redis.py @@ -0,0 +1,78 @@ +# Copyright 2014 Canonical, Ltd. +# +# 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. + +import testtools + +from oslo.utils import importutils +from tests import utils as test_utils + +redis = importutils.try_import('redis') +matchmaker_redis = ( + importutils.try_import('oslo.messaging._drivers.matchmaker_redis')) + + +def redis_available(): + '''Helper to see if local redis server is running''' + if not redis: + return False + try: + c = redis.StrictRedis(socket_timeout=1) + c.ping() + return True + except redis.exceptions.ConnectionError: + return False + + +@testtools.skipIf(not matchmaker_redis, "matchmaker/eventlet unavailable") +@testtools.skipIf(not redis_available(), "redis unavailable") +class RedisMatchMakerTest(test_utils.BaseTestCase): + + def setUp(self): + super(RedisMatchMakerTest, self).setUp() + self.ring_data = { + "conductor": ["controller1", "node1", "node2", "node3"], + "scheduler": ["controller1", "node1", "node2", "node3"], + "network": ["controller1", "node1", "node2", "node3"], + "cert": ["controller1"], + "console": ["controller1"], + "consoleauth": ["controller1"]} + self.matcher = matchmaker_redis.MatchMakerRedis() + self.populate() + + def tearDown(self): + super(RedisMatchMakerTest, self).tearDown() + c = redis.StrictRedis() + c.flushdb() + + def populate(self): + for k, hosts in self.ring_data.items(): + for h in hosts: + self.matcher.register(k, h) + + def test_direct(self): + self.assertEqual( + self.matcher.queues('cert.controller1'), + [('cert.controller1', 'controller1')]) + + def test_register(self): + self.matcher.register('cert', 'keymaster') + self.assertEqual( + sorted(self.matcher.redis.smembers('cert')), + ['cert.controller1', 'cert.keymaster']) + + def test_unregister(self): + self.matcher.unregister('conductor', 'controller1') + self.assertEqual( + sorted(self.matcher.redis.smembers('conductor')), + ['conductor.node1', 'conductor.node2', 'conductor.node3']) diff --git a/tests/drivers/test_matchmaker_ring.py b/tests/drivers/test_matchmaker_ring.py new file mode 100644 index 000000000..c6d81e6eb --- /dev/null +++ b/tests/drivers/test_matchmaker_ring.py @@ -0,0 +1,73 @@ +# Copyright 2014 Canonical, Ltd. +# +# 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. + +import testtools + +from oslo.utils import importutils +from tests import utils as test_utils + +# NOTE(jamespage) matchmaker tied directly to eventlet +# which is not yet py3 compatible - skip if import fails +matchmaker_ring = ( + importutils.try_import('oslo.messaging._drivers.matchmaker_ring')) + + +@testtools.skipIf(not matchmaker_ring, "matchmaker/eventlet unavailable") +class MatchmakerRingTest(test_utils.BaseTestCase): + + def setUp(self): + super(MatchmakerRingTest, self).setUp() + self.ring_data = { + "conductor": ["controller1", "node1", "node2", "node3"], + "scheduler": ["controller1", "node1", "node2", "node3"], + "network": ["controller1", "node1", "node2", "node3"], + "cert": ["controller1"], + "console": ["controller1"], + "consoleauth": ["controller1"]} + self.matcher = matchmaker_ring.MatchMakerRing(self.ring_data) + + def test_direct(self): + self.assertEqual( + self.matcher.queues('cert.controller1'), + [('cert.controller1', 'controller1')]) + self.assertEqual( + self.matcher.queues('conductor.node1'), + [('conductor.node1', 'node1')]) + + def test_fanout(self): + self.assertEqual( + self.matcher.queues('fanout~conductor'), + [('fanout~conductor.controller1', 'controller1'), + ('fanout~conductor.node1', 'node1'), + ('fanout~conductor.node2', 'node2'), + ('fanout~conductor.node3', 'node3')]) + + def test_bare_topic(self): + # Round robins through the hosts on the topic + self.assertEqual( + self.matcher.queues('scheduler'), + [('scheduler.controller1', 'controller1')]) + self.assertEqual( + self.matcher.queues('scheduler'), + [('scheduler.node1', 'node1')]) + self.assertEqual( + self.matcher.queues('scheduler'), + [('scheduler.node2', 'node2')]) + self.assertEqual( + self.matcher.queues('scheduler'), + [('scheduler.node3', 'node3')]) + # Cycles loop + self.assertEqual( + self.matcher.queues('scheduler'), + [('scheduler.controller1', 'controller1')])