PageRenderTime 14ms CodeModel.GetById 1ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/src/NUnit/util/Services/TestAgency.cs

#
C# | 381 lines | 279 code | 61 blank | 41 comment | 27 complexity | 8c3b758ba50cc3980ea5482358978be8 MD5 | raw file
  1// ****************************************************************
  2// Copyright 2008, Charlie Poole
  3// This is free software licensed under the NUnit license. You may
  4// obtain a copy of the license at http://nunit.org
  5// ****************************************************************
  6
  7using System;
  8using System.IO;
  9using System.Threading;
 10using System.Diagnostics;
 11using System.Collections;
 12using System.Collections.Specialized;
 13using System.Runtime.Remoting;
 14using System.Runtime.Remoting.Services;
 15using System.Runtime.Remoting.Channels;
 16using System.Runtime.Remoting.Channels.Tcp;
 17using NUnit.Core;
 18
 19namespace NUnit.Util
 20{
 21	/// <summary>
 22	/// Enumeration used to report AgentStatus
 23	/// </summary>
 24	public enum AgentStatus
 25	{
 26		Unknown,
 27		Starting,
 28		Ready,
 29		Busy,
 30		Stopping
 31	}
 32
 33	/// <summary>
 34	/// The TestAgency class provides RemoteTestAgents
 35	/// on request and tracks their status. Agents
 36	/// are wrapped in an instance of the TestAgent
 37	/// class. Multiple agent types are supported
 38	/// but only one, ProcessAgent is implemented
 39	/// at this time.
 40	/// </summary>
 41	public class TestAgency : ServerBase, IAgency, IService
 42	{
 43		static Logger log = InternalTrace.GetLogger(typeof(TestAgency));
 44
 45		#region Private Fields
 46		private AgentDataBase agentData = new AgentDataBase();
 47		#endregion
 48
 49		#region Constructors
 50		public TestAgency() : this( "TestAgency", 0 ) { }
 51
 52		public TestAgency( string uri, int port ) : base( uri, port ) { }
 53		#endregion
 54
 55		#region ServerBase Overrides
 56		public override void Stop()
 57		{
 58			foreach( AgentRecord r in agentData )
 59			{
 60				if ( !r.Process.HasExited )
 61				{
 62					if ( r.Agent != null )
 63					{
 64						r.Agent.Stop();
 65						r.Process.WaitForExit(10000);
 66					}
 67
 68					if ( !r.Process.HasExited )
 69						r.Process.Kill();
 70				}
 71			}
 72
 73			agentData.Clear();
 74
 75			base.Stop ();
 76		}
 77		#endregion
 78
 79		#region Public Methods - Called by Agents
 80		public void Register( TestAgent agent )
 81		{
 82			AgentRecord r = agentData[agent.Id];
 83			if ( r == null )
 84                throw new ArgumentException(
 85                    string.Format("Agent {0} is not in the agency database", agent.Id),
 86                    "agentId");
 87            r.Agent = agent;
 88		}
 89
 90		public void ReportStatus( Guid agentId, AgentStatus status )
 91		{
 92			AgentRecord r = agentData[agentId];
 93
 94			if ( r == null )
 95                throw new ArgumentException(
 96                    string.Format("Agent {0} is not in the agency database", agentId),
 97                    "agentId" );
 98
 99			r.Status = status;
100		}
101		#endregion
102
103		#region Public Methods - Called by Clients
104		public TestAgent GetAgent()
105		{
106			return GetAgent( RuntimeFramework.CurrentFramework, Timeout.Infinite );
107		}
108
109        public TestAgent GetAgent(int waitTime)
110        {
111            return GetAgent(RuntimeFramework.CurrentFramework, waitTime);
112        }
113
114        public TestAgent GetAgent(RuntimeFramework framework, int waitTime)
115        {
116            return GetAgent(framework, waitTime, false);
117        }
118
119        public TestAgent GetAgent(RuntimeFramework framework, int waitTime, bool enableDebug)
120        {
121            log.Info("Getting agent for use under {0}", framework);
122 
123            if (!framework.IsAvailable)
124                throw new ArgumentException(
125                    string.Format("The {0} framework is not available", framework),
126                    "framework");
127
128            // TODO: Decide if we should reuse agents
129            //AgentRecord r = FindAvailableRemoteAgent(type);
130            //if ( r == null )
131            //    r = CreateRemoteAgent(type, framework, waitTime);
132            return CreateRemoteAgent(framework, waitTime, enableDebug);
133		}
134
135		public void ReleaseAgent( TestAgent agent )
136		{
137			AgentRecord r = agentData[agent.Id];
138			if ( r == null )
139				log.Error( string.Format( "Unable to release agent {0} - not in database", agent.Id ) );
140			else
141			{
142				r.Status = AgentStatus.Ready;
143				log.Debug( "Releasing agent " + agent.Id.ToString() );
144			}
145		}
146
147        //public void DestroyAgent( ITestAgent agent )
148        //{
149        //    AgentRecord r = agentData[agent.Id];
150        //    if ( r != null )
151        //    {
152        //        if( !r.Process.HasExited )
153        //            r.Agent.Stop();
154        //        agentData[r.Id] = null;
155        //    }
156        //}
157		#endregion
158
159		#region Helper Methods
160		private Guid LaunchAgentProcess(RuntimeFramework targetRuntime, bool enableDebug)
161		{
162            string agentExePath = NUnitConfiguration.GetTestAgentExePath(targetRuntime.ClrVersion);
163
164            if (agentExePath == null)
165                throw new ArgumentException(
166                    string.Format("NUnit components for version {0} of the CLR are not installed",
167                    targetRuntime.ClrVersion.ToString()), "targetRuntime");
168
169            log.Debug("Using nunit-agent at " + agentExePath);
170
171			Process p = new Process();
172			p.StartInfo.UseShellExecute = false;
173            Guid agentId = Guid.NewGuid();
174            string arglist = agentId.ToString() + " " + ServerUrl;
175            if (enableDebug)
176                arglist += " --pause";
177
178            switch( targetRuntime.Runtime )
179            {
180                case RuntimeType.Mono:
181                    p.StartInfo.FileName = NUnitConfiguration.MonoExePath;
182                    if (enableDebug)
183                        p.StartInfo.Arguments = string.Format("--debug \"{0}\" {1}", agentExePath, arglist);
184                    else
185                        p.StartInfo.Arguments = string.Format("\"{0}\" {1}", agentExePath, arglist);
186                    break;
187                case RuntimeType.Net:
188                    p.StartInfo.FileName = agentExePath;
189
190                    if (targetRuntime.ClrVersion.Build < 0)
191                        targetRuntime = RuntimeFramework.GetBestAvailableFramework(targetRuntime);
192
193                    string envVar = "v" + targetRuntime.ClrVersion.ToString(3);
194                    p.StartInfo.EnvironmentVariables["COMPLUS_Version"] = envVar;
195
196                    p.StartInfo.Arguments = arglist;
197                    break;
198                default:
199				    p.StartInfo.FileName = agentExePath;
200                    p.StartInfo.Arguments = arglist;
201                    break;
202			}
203			
204            //p.Exited += new EventHandler(OnProcessExit);
205            p.Start();
206            log.Info("Launched Agent process {0} - see nunit-agent_{0}.log", p.Id);
207            log.Info("Command line: \"{0}\" {1}", p.StartInfo.FileName, p.StartInfo.Arguments);
208
209			agentData.Add( new AgentRecord( agentId, p, null, AgentStatus.Starting ) );
210		    return agentId;
211		}
212
213        //private void OnProcessExit(object sender, EventArgs e)
214        //{
215        //    Process p = sender as Process;
216        //    if (p != null)
217        //        agentData.Remove(p.Id);
218        //}
219
220		private AgentRecord FindAvailableAgent()
221		{
222			foreach( AgentRecord r in agentData )
223				if ( r.Status == AgentStatus.Ready)
224				{
225					log.Debug( "Reusing agent {0}", r.Id );
226					r.Status = AgentStatus.Busy;
227					return r;
228				}
229
230			return null;
231		}
232
233		private TestAgent CreateRemoteAgent(RuntimeFramework framework, int waitTime, bool enableDebug)
234		{
235            Guid agentId = LaunchAgentProcess(framework, enableDebug);
236
237			log.Debug( "Waiting for agent {0} to register", agentId.ToString("B") );
238
239            int pollTime = 200;
240            bool infinite = waitTime == Timeout.Infinite;
241
242			while( infinite || waitTime > 0 )
243			{
244				Thread.Sleep( pollTime );
245				if ( !infinite ) waitTime -= pollTime;
246                TestAgent agent = agentData[agentId].Agent;
247				if ( agent != null )
248				{
249					log.Debug( "Returning new agent {0}", agentId.ToString("B") );
250                    return agent;
251				}
252			}
253
254			return null;
255		}
256		#endregion
257
258		#region IService Members
259
260		public void UnloadService()
261		{
262			this.Stop();
263		}
264
265		public void InitializeService()
266		{
267			this.Start();
268		}
269
270		#endregion
271
272		#region Nested Class - AgentRecord
273		private class AgentRecord
274		{
275			public Guid Id;
276			public Process Process;
277			public TestAgent Agent;
278			public AgentStatus Status;
279
280			public AgentRecord( Guid id, Process p, TestAgent a, AgentStatus s )
281			{
282				this.Id = id;
283				this.Process = p;
284				this.Agent = a;
285				this.Status = s;
286			}
287
288		}
289		#endregion
290
291		#region Nested Class - AgentDataBase
292		/// <summary>
293		///  A simple class that tracks data about this
294		///  agencies active and available agents
295		/// </summary>
296		private class AgentDataBase : IEnumerable
297		{
298			private ListDictionary agentData = new ListDictionary();
299
300			public AgentRecord this[Guid id]
301			{
302				get { return (AgentRecord)agentData[id]; }
303				set
304				{
305					if ( value == null )
306						agentData.Remove( id );
307					else
308						agentData[id] = value;
309				}
310			}
311
312			public AgentRecord this[TestAgent agent]
313			{
314				get
315				{
316					foreach( System.Collections.DictionaryEntry entry in agentData )
317					{
318						AgentRecord r = (AgentRecord)entry.Value;
319						if ( r.Agent == agent )
320							return r;
321					}
322
323					return null;
324				}
325			}
326
327			public void Add( AgentRecord r )
328			{
329				agentData[r.Id] = r;
330			}
331
332            public void Remove(Guid agentId)
333            {
334                agentData.Remove(agentId);
335            }
336
337			public void Clear()
338			{
339				agentData.Clear();
340			}
341
342			#region IEnumerable Members
343			public IEnumerator GetEnumerator()
344			{
345				return new AgentDataEnumerator( agentData );
346			}
347			#endregion
348
349			#region Nested Class - AgentDataEnumerator
350			public class AgentDataEnumerator : IEnumerator
351			{
352				IEnumerator innerEnum;
353
354				public AgentDataEnumerator( IDictionary list )
355				{
356					innerEnum = list.GetEnumerator();
357				}
358
359				#region IEnumerator Members
360				public void Reset()
361				{
362					innerEnum.Reset();
363				}
364
365				public object Current
366				{
367					get { return ((DictionaryEntry)innerEnum.Current).Value; }
368				}
369
370				public bool MoveNext()
371				{
372					return innerEnum.MoveNext();
373				}
374				#endregion
375			}
376			#endregion
377		}
378
379		#endregion
380	}
381}