/*
 * 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.Diagnostics;
using System.Threading;
using Apache.NMS.Test;
using Apache.NMS.Util;
using NUnit.Framework;

namespace Apache.NMS.ActiveMQ.Test
{
	[TestFixture]
	public class MaxInactivityDurationTest : NMSTestSupport
	{
		protected static string DESTINATION_NAME = "TEST.MaxInactivityDuration";
		protected static string CORRELATION_ID = "MaxInactivityCorrelationID";

		[Test]
		public void TestMaxInactivityDuration()
		{
			string testuri = "activemq:tcp://${activemqhost}:61616" +
										"?wireFormat.maxInactivityDurationInitialDelay=5000" +
										"&wireFormat.maxInactivityDuration=10000" +
										"&connection.asyncClose=false";

			NMSConnectionFactory factory = new NMSConnectionFactory(ReplaceEnvVar(testuri));
			using(IConnection connection = factory.CreateConnection("", ""))
			{
				connection.Start();
				using(ISession session = connection.CreateSession(AcknowledgementMode.AutoAcknowledge))
				{
					IDestination destination = SessionUtil.GetDestination(session, DESTINATION_NAME);
					using(IMessageConsumer consumer = session.CreateConsumer(destination))
					using(IMessageProducer producer = session.CreateProducer(destination))
					{
						SendMessage(producer);

						IMessage receivedMsg = consumer.Receive(TimeSpan.FromSeconds(5));
						Assert.AreEqual(CORRELATION_ID, receivedMsg.NMSCorrelationID, "Invalid correlation ID.");

						// Go inactive...
						Thread.Sleep(TimeSpan.FromSeconds(30));

						// Send another message.
						SendMessage(producer);
						receivedMsg = consumer.Receive(TimeSpan.FromSeconds(5));
						Assert.AreEqual(CORRELATION_ID, receivedMsg.NMSCorrelationID, "Invalid correlation ID.");
					}
				}
			}
		}

		protected void SendMessage(IMessageProducer producer)
		{
			IMessage request = producer.CreateMessage();
			request.NMSCorrelationID = CORRELATION_ID;
			request.NMSType = "Test";
			producer.Send(request);
		}

		[Test, Sequential]
		public void TestInactivityMonitorThreadLeak(
			[Values(0, 1000)]
			int inactivityDuration)
		{
			Process currentProcess = Process.GetCurrentProcess();
			Tracer.InfoFormat("Beginning thread count: {0}, handle count: {1}", currentProcess.Threads.Count, currentProcess.HandleCount);

			string testuri = string.Format("activemq:tcp://${{activemqhost}}:61616?wireFormat.maxInactivityDuration={0}", inactivityDuration);
	
			NMSConnectionFactory factory = new NMSConnectionFactory(ReplaceEnvVar(testuri));

			// We measure the initial resource counts, and then allow a certain fudge factor for the resources
			// to fluctuate at run-time.  We allow for a certain amount of fluctuation, but if the counts
			// grow outside the safe boundaries of delayed garbage collection, then we fail the test.
			currentProcess = Process.GetCurrentProcess();
			int beginThreadCount = currentProcess.Threads.Count;
			int beginHandleCount = currentProcess.HandleCount;
			int maxThreadGrowth = 10;
			int maxHandleGrowth = 500;

			for(int i = 0; i < 200; i++)
			{
				using(IConnection connection = factory.CreateConnection("ResourceLeakTest", "Password"))
				{
					using(ISession session = connection.CreateSession())
					{
						IDestination destination = SessionUtil.GetDestination(session, "topic://TEST.NMSResourceLeak");
						using(IMessageConsumer consumer = session.CreateConsumer(destination))
						{
							connection.Start();
						}
					}
				}

				currentProcess = Process.GetCurrentProcess();
				int endThreadCount = currentProcess.Threads.Count;
				int endHandleCount = currentProcess.HandleCount;

				Assert.Less(endThreadCount, beginThreadCount + maxThreadGrowth, string.Format("Thread count grew beyond maximum of {0} on iteration #{1}.", maxThreadGrowth, i));
				Assert.Less(endHandleCount, beginHandleCount + maxHandleGrowth, string.Format("Handle count grew beyond maximum of {0} on iteration #{1}.", maxHandleGrowth, i));
			}
		}
	}
}
