From e85458393975ce93ecd470e8fc3fd7906927b959 Mon Sep 17 00:00:00 2001 From: Robert Greig Date: Wed, 10 Jan 2007 13:02:45 +0000 Subject: Qpid-257 patch applied. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@494803 13f79535-47bb-0310-9956-ffa450edef68 --- .../Common/BaseMessagingTestFixture.cs | 25 +- .../HeadersExchange/HeadersExchangeTest.cs | 270 +++++++++++++++++++++ .../HeadersExchange/HeadersMatchingConsumer.cs | 105 -------- .../HeadersExchange/HeadersMatchingProducer.cs | 113 --------- dotnet/Qpid.Client.Tests/Qpid.Client.Tests.csproj | 2 +- 5 files changed, 293 insertions(+), 222 deletions(-) create mode 100644 dotnet/Qpid.Client.Tests/HeadersExchange/HeadersExchangeTest.cs delete mode 100644 dotnet/Qpid.Client.Tests/HeadersExchange/HeadersMatchingConsumer.cs delete mode 100644 dotnet/Qpid.Client.Tests/HeadersExchange/HeadersMatchingProducer.cs diff --git a/dotnet/Qpid.Client.Tests/Common/BaseMessagingTestFixture.cs b/dotnet/Qpid.Client.Tests/Common/BaseMessagingTestFixture.cs index e27174909c..fae610eb85 100644 --- a/dotnet/Qpid.Client.Tests/Common/BaseMessagingTestFixture.cs +++ b/dotnet/Qpid.Client.Tests/Common/BaseMessagingTestFixture.cs @@ -26,19 +26,32 @@ using Qpid.Client.qms; namespace Qpid.Client.Tests { + /// + /// Provides a basis for writing Unit tests that communicate with an AMQ protocol broker. By default it creates a connection + /// to a message broker running on localhost on the standard AMQ port, 5672, using guest:guest login credentials, on the default exchange, + /// 'test' queue. + /// public class BaseMessagingTestFixture { private static ILog _logger = LogManager.GetLogger(typeof(BaseMessagingTestFixture)); + /// The default AMQ connection URL to use for tests. const string connectionUri = "amqp://guest:guest@default/test?brokerlist='tcp://localhost:5672'"; + /// Holds the test connection. protected IConnection _connection; + /// Holds the test channel. protected IChannel _channel; + /// + /// Creates the test connection and channel. + /// [SetUp] public virtual void Init() { + _logger.Info("public virtual void Init(): called"); + try { ConnectionInfo connectionInfo = QpidConnectionInfo.FromUrl(connectionUri); @@ -52,14 +65,20 @@ namespace Qpid.Client.Tests } } + /// + /// Disposes the test connection. This is called manually because the connection is a field so dispose will not be automatically + /// called on it. + /// [TearDown] - public void Shutdown() + public virtual void Shutdown() { - Console.WriteLine("Shutdown"); + _logger.Info("public virtual void Shutdown(): called"); + if (_connection != null) { - Console.WriteLine("Disposing connection"); + _logger.Info("Disposing connection."); _connection.Dispose(); + _logger.Info("Connection disposed."); } } } diff --git a/dotnet/Qpid.Client.Tests/HeadersExchange/HeadersExchangeTest.cs b/dotnet/Qpid.Client.Tests/HeadersExchange/HeadersExchangeTest.cs new file mode 100644 index 0000000000..fe44bc8639 --- /dev/null +++ b/dotnet/Qpid.Client.Tests/HeadersExchange/HeadersExchangeTest.cs @@ -0,0 +1,270 @@ +/* + * + * 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. + * + */ +using System; +using System.Threading; +using log4net; +using NUnit.Framework; +using Qpid.Framing; +using Qpid.Messaging; + +namespace Qpid.Client.Tests +{ + /// + /// Sets up a producer/consumer pair to send test messages through a header exchange. The header exchange matching pattern is tested to + /// verify that it correctly matches or filters out messages based on their headers. + /// + /// Check that a message matching all fields of a headers exchange is passed by the exchange. + /// Check that a message containing values for empty fields of a headers exchange is passed by the exchange. + /// Check that a message matching only some fields of a headers exhcnage is not passed by the exchange. + /// Check that a message with additional fields to the correct matching fields of a headers exchange is passed by the exchange. + /// + /// + /// Remove the HeadersMatchingProducer class and rename this to HeaderExchangeTest. The producer and consumer are implemented + /// in a single test class to make running this as part of an automated test suite possible. + /// + /// Consider not using a delegate to callback the OnMessage method. Easier to just call receive on the consumer but using the + /// callback does demonstrate how to do so. + [TestFixture] + public class HeadersExchangeTest : BaseMessagingTestFixture + { + private static ILog _logger = LogManager.GetLogger(typeof(HeadersExchangeTest)); + + /// Holds the default test timeout for broker communications before tests give up. + private static readonly int TIMEOUT = 1000; + + /// Holds the name of the headers exchange to create to send test messages on. + private string _exchangeName = "ServiceQ1"; + + /// Used to preserve the most recent exception in case test cases need to examine it. + private Exception _lastException = null; + + /// Used to preserve the most recent message from the test consumer. + private IMessage _lastMessage = null; + + /// The test consumer to get messages from the broker with. + private IMessageConsumer _consumer; + + private IMessagePublisher _publisher; + + private AutoResetEvent _evt = new AutoResetEvent(false); + + private MessageReceivedDelegate _msgRecDelegate; + private ExceptionListenerDelegate _exceptionDelegate; + + [SetUp] + public override void Init() + { + // Ensure that the base init method is called. It establishes a connection with the broker. + base.Init(); + + _logger.Info("Starting..."); + _logger.Info("Exchange name is '" + _exchangeName + "'..."); + + // Register this to listen for exceptions on the test connection. + _exceptionDelegate = new ExceptionListenerDelegate(OnException); + _connection.ExceptionListener += _exceptionDelegate; + + // Declare a new headers exchange with the name of the test service. + _channel.DeclareExchange(_exchangeName, ExchangeClassConstants.HEADERS); + + // Create a non-durable, temporary (aka auto-delete), exclusive queue. + string queueName = _channel.GenerateUniqueName(); + _channel.DeclareQueue(queueName, false, true, true); + + // Bind the queue to the new headers exchange, setting up some header patterns for the exchange to match. + _channel.Bind(queueName, _exchangeName, null, CreatePatternAsFieldTable()); + + // Create a test consumer to consume messages from the test exchange. + _consumer = _channel.CreateConsumerBuilder(queueName) + .WithPrefetchLow(100) + .WithPrefetchHigh(500) + .WithNoLocal(true) + .Create(); + + // Register this to listen for messages on the consumer. + _msgRecDelegate = new MessageReceivedDelegate(OnMessage); + _consumer.OnMessage += _msgRecDelegate; + + // Clear the most recent message and exception. + _lastException = null; + _lastMessage = null; + + _publisher = _channel.CreatePublisherBuilder() + .WithExchangeName(_exchangeName) + .WithMandatory(true) + .Create(); + + _publisher.DeliveryMode = DeliveryMode.NonPersistent; + + // Start all channel + _connection.Start(); + } + + /// + /// Deregisters the on message delegate before closing the connection. + /// + [TearDown] + public override void Shutdown() + { + _logger.Info("public void Shutdown(): called"); + + //_consumer.OnMessage -= _msgRecDelegate; + //_connection.ExceptionListener -= _exceptionDelegate; + + _connection.Stop(); + + base.Shutdown(); + } + + /// + /// Callback method that is passed any messages received on the test channel. + /// + /// + /// The received message. + public void OnMessage(IMessage message) + { + _logger.Debug(string.Format("message.Type = {0}", message.GetType())); + _logger.Debug("Got message '" + message + "'"); + + // Preserve the most recent exception so that test cases can examine it. + _lastMessage = message; + + // Notify any waiting threads that a message has been received. + _evt.Set(); + } + + /// Callback method to handle any exceptions raised by the test connection. + /// + /// The connection exception. + public void OnException(Exception e) + { + // Preserve the most recent exception in case test cases need to examine it. + _lastException = e; + + // Notify any waiting threads that an exception event has occurred. + _evt.Set(); + } + + /// Check that a message matching all fields of a headers exchange is passed by the exchange. + [Test] + public void TestMatchAll() + { + IMessage msg = _channel.CreateTextMessage("matches match2=''"); + msg.Headers["match1"] = "foo"; + msg.Headers["match2"] = ""; + + // Use the SendTestMessage helper method to verify that the message was sent and received. + SendTestMessage(msg, true); + } + + /// Check that a message containing values for empty fields of a headers exchange is passed by the exchange. + [Test] + public void TestMatchEmptyMatchesAnything() + { + // Send a test message that matches the headers exchange. + IMessage msg = _channel.CreateTextMessage("matches match1='foo' and match2='bar'"); + msg.Headers["match1"] = "foo"; + msg.Headers["match2"] = "bar"; + + // Use the SendTestMessage helper method to verify that the message was sent and received. + SendTestMessage(msg, true); + } + + /// Check that a message matching only some fields of a headers exhcnage is not passed by the exchange. + [Test] + public void TestMatchOneFails() + { + IMessage msg = _channel.CreateTextMessage("not match - only match1"); + msg.Headers["match1"] = "foo"; + + // Use the SendTestMessage helper method to verify that the message was sent and not received. + SendTestMessage(msg, false); + } + + /// + /// Check that a message with additional fields to the correct matching fields of a headers exchange is passed by + /// the exchange. + /// + [Test] + public void TestMatchExtraFields() + { + IMessage msg = _channel.CreateTextMessage("matches - extra headers"); + msg.Headers["match1"] = "foo"; + msg.Headers["match2"] = "bar"; + msg.Headers["match3"] = "not required"; + + // Use the SendTestMessage helper method to verify that the message was sent and received. + SendTestMessage(msg, true); + } + + /// + /// Sends the specified message to the test publisher, and confirms that it was received by the test consumer or not + /// depending on whether or not the message should be received by the consumer. + /// + /// Any exceptions raised by the connection will cause an Assert failure exception to be raised. + /// + /// + /// The message to send. + /// A flag to indicate whether or not the message should be received by the consumer. + private void SendTestMessage(IMessage msgSend, bool shouldPass) + { + _publisher.Send(msgSend); + _evt.WaitOne(TIMEOUT, true); + + // Check that an exception other than not routable was raised in which case re-raise it as a test error. + if (_lastException != null && !(_lastException.InnerException is AMQUndeliveredException)) + { + Assert.Fail("Exception {0} was raised by the broker connection.", _lastException); + } + // Check that a message was returned if the test is expecting the message to pass. + else if (shouldPass) + { + Assert.IsNotNull(_lastMessage, "Did not get a matching message from the headers exchange."); + } + // Check that a not routable exception was raised if the test is expecting the message to fail. + else if (_lastException != null && _lastException.InnerException is AMQUndeliveredException) + { + Assert.IsNull(_lastMessage, "Message could not be routed so consumer should not have received it."); + } + // The broker did not respond within the test timeout so fail the test. + else + { + Assert.Fail("The test timed out without a response from the broker."); + } + } + + /// + /// Returns a field table containing patterns to match the test header exchange against. + /// + /// + /// A field table containing test patterns. + private FieldTable CreatePatternAsFieldTable() + { + FieldTable matchTable = new FieldTable(); + + // Currently all String matching must be prefixed by an "S" ("S" for string because of a failing of the FieldType definition). + matchTable["Smatch1"] = "foo"; + matchTable["Smatch2"] = ""; + + return matchTable; + } + } +} diff --git a/dotnet/Qpid.Client.Tests/HeadersExchange/HeadersMatchingConsumer.cs b/dotnet/Qpid.Client.Tests/HeadersExchange/HeadersMatchingConsumer.cs deleted file mode 100644 index 1b27f920b8..0000000000 --- a/dotnet/Qpid.Client.Tests/HeadersExchange/HeadersMatchingConsumer.cs +++ /dev/null @@ -1,105 +0,0 @@ -/* - * - * 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. - * - */ -using System; -using System.Threading; -using log4net; -using NUnit.Framework; -using Qpid.Framing; -using Qpid.Messaging; - -namespace Qpid.Client.Tests -{ - [TestFixture] - public class HeadersMatchingConsumer : BaseMessagingTestFixture - { - private static ILog _logger = LogManager.GetLogger(typeof(HeadersMatchingConsumer)); - - private string _serviceName = "ServiceQ1"; - - private AutoResetEvent _evt = new AutoResetEvent(false); - - [SetUp] - public override void Init() - { - base.Init(); - - _logger.Info("Starting..."); - - _logger.Info("Service (queue) name is '" + _serviceName + "'..."); - - _connection.ExceptionListener = new ExceptionListenerDelegate(OnException); - - // Declare a new HeadersExchange with the name of the service. - _channel.DeclareExchange(_serviceName, ExchangeClassConstants.HEADERS); - - // Create non-durable, temporary (aka auto-delete), exclusive queue. - string queueName = _channel.GenerateUniqueName(); - _channel.DeclareQueue(queueName, false, true, true); - - // Bind our queue to the new HeadersExchange. - _channel.Bind(queueName, _serviceName, null, CreatePatternAsFieldTable()); - - IMessageConsumer consumer = _channel.CreateConsumerBuilder(queueName) - .WithPrefetchLow(100) - .WithPrefetchHigh(500) - .WithNoLocal(true) - .Create(); - - consumer.OnMessage = new MessageReceivedDelegate(OnMessage); - } - - [Test] - public void Test() - { - _connection.Start(); - _logger.Info("Waiting..."); - _evt.WaitOne(); - } - - public void OnMessage(IMessage message) - { - _logger.Info(string.Format("message.Type = {0}", message.GetType())); - _logger.Info("Got message '" + message + "'"); - } - - private FieldTable CreatePatternAsFieldTable() - { - FieldTable matchTable = new FieldTable(); - // Currently all String matching must be prefixed by an "S" ("S" for string because of a failing of the FieldType definition). - matchTable["Smatch1"] = "foo"; - matchTable["Smatch2"] = ""; - return matchTable; - } - - public void OnException(Exception e) - { - if (e is QpidException && e.InnerException is AMQDisconnectedException) - { - _logger.Error("Broker closed connection"); - } - else - { - _logger.Error("Connection exception occurred: " + e); - } - _evt.Set(); - } - } -} diff --git a/dotnet/Qpid.Client.Tests/HeadersExchange/HeadersMatchingProducer.cs b/dotnet/Qpid.Client.Tests/HeadersExchange/HeadersMatchingProducer.cs deleted file mode 100644 index c748ef8840..0000000000 --- a/dotnet/Qpid.Client.Tests/HeadersExchange/HeadersMatchingProducer.cs +++ /dev/null @@ -1,113 +0,0 @@ -/* - * - * 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. - * - */ -using log4net; -using NUnit.Framework; -using Qpid.Messaging; - -namespace Qpid.Client.Tests -{ - [TestFixture] - public class HeadersMatchingProducer : BaseMessagingTestFixture - { - private static ILog _logger = LogManager.GetLogger(typeof(HeadersMatchingProducer)); - - private string _commandExchangeName = "ServiceQ1"; - - private int _messageCount = 12; - - private IMessagePublisher _publisher; - - [SetUp] - public override void Init() - { - base.Init(); - - try - { - _publisher = _channel.CreatePublisherBuilder() - .WithExchangeName(_commandExchangeName) - .WithMandatory(true) - .Create(); - - // Disabling timestamps - a performance optimisation where timestamps and TTL/expiration - // are not required. - _publisher.DisableMessageTimestamp = true; - - _publisher.DeliveryMode = DeliveryMode.NonPersistent; - } - catch (QpidException e) - { - _logger.Error("Error: " + e, e); - } - } - - [Test] - public void SendMessages() - { - _connection.Start(); - for (int i = 0; i < _messageCount; i++) - { - int rem = i % 6; - IMessage msg = null; - switch (rem) - { - case 0: - msg = _channel.CreateTextMessage("matches match2='bar'"); - msg.Headers["match1"] = "foo"; - msg.Headers["match2"] = "bar"; - break; - - case 1: - msg = _channel.CreateTextMessage("not match - only match1"); - msg.Headers["match1"] = "foo"; - break; - - case 2: - msg = _channel.CreateTextMessage("not match - only match2"); - msg.Headers["match2"] = "bar"; - break; - - case 3: - msg = _channel.CreateTextMessage("matches match2=''"); - msg.Headers["match1"] = "foo"; - msg.Headers["match2"] = ""; - break; - - case 4: - msg = _channel.CreateTextMessage("matches - extra headers"); - msg.Headers["match1"] = "foo"; - msg.Headers["match2"] = "bar"; - msg.Headers["match3"] = "not required"; - break; - - case 5: - msg = _channel.CreateTextMessage("5: no match"); - msg.Headers["match1"] = "foo1"; - msg.Headers["match2"] = "bar"; - msg.Headers["match3"] = "not required"; - break; - } - _publisher.Send(msg); - } - _logger.Info("Finished sending " + _messageCount + " messages"); - } - } -} diff --git a/dotnet/Qpid.Client.Tests/Qpid.Client.Tests.csproj b/dotnet/Qpid.Client.Tests/Qpid.Client.Tests.csproj index a7b6e49879..401bbce75d 100644 --- a/dotnet/Qpid.Client.Tests/Qpid.Client.Tests.csproj +++ b/dotnet/Qpid.Client.Tests/Qpid.Client.Tests.csproj @@ -47,7 +47,7 @@ - + -- cgit v1.2.1