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