PageRenderTime 56ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/src/NUnit/util/NUnitProject.cs

#
C# | 509 lines | 368 code | 77 blank | 64 comment | 82 complexity | 863c79c7b0baac9225688a606294e8cc MD5 | raw file
Possible License(s): GPL-2.0
  1. // ****************************************************************
  2. // This is free software licensed under the NUnit license. You
  3. // may obtain a copy of the license as well as information regarding
  4. // copyright ownership at http://nunit.org.
  5. // ****************************************************************
  6. using System;
  7. using System.Collections;
  8. using System.Xml;
  9. using System.Xml.Schema;
  10. using System.IO;
  11. using System.Threading;
  12. using NUnit.Core;
  13. namespace NUnit.Util
  14. {
  15. /// <summary>
  16. /// Class that represents an NUnit test project
  17. /// </summary>
  18. public class NUnitProject
  19. {
  20. #region Constants
  21. public static readonly string Extension = ".nunit";
  22. #endregion
  23. #region Instance variables
  24. /// <summary>
  25. /// Path to the file storing this project
  26. /// </summary>
  27. private string projectPath;
  28. /// <summary>
  29. /// Application Base for the project. Since this
  30. /// can be null, always fetch from the property
  31. /// rather than using the field directly.
  32. /// </summary>
  33. private string basePath;
  34. /// <summary>
  35. /// Whether the project is dirty
  36. /// </summary>
  37. private bool isDirty = false;
  38. /// <summary>
  39. /// Whether canges have been made requiring a reload
  40. /// </summary>
  41. private bool reloadRequired = false;
  42. /// <summary>
  43. /// Collection of configs for the project
  44. /// </summary>
  45. private ProjectConfigCollection configs;
  46. /// <summary>
  47. /// True for NUnit-related projects that follow the config
  48. /// of the NUnit build under which they are running.
  49. /// </summary>
  50. private bool autoConfig;
  51. /// <summary>
  52. /// The currently active configuration
  53. /// </summary>
  54. private ProjectConfig activeConfig;
  55. /// <summary>
  56. /// Flag indicating that this project is a
  57. /// temporary wrapper for an assembly.
  58. /// </summary>
  59. private bool isAssemblyWrapper = false;
  60. /// <summary>
  61. /// The ProcessModel to be used in loading this project
  62. /// </summary>
  63. private ProcessModel processModel;
  64. /// <summary>
  65. /// The DomainUsage setting to be used in loading this project
  66. /// </summary>
  67. private DomainUsage domainUsage;
  68. #endregion
  69. #region Constructor
  70. public NUnitProject( string projectPath )
  71. {
  72. this.projectPath = Path.GetFullPath( projectPath );
  73. configs = new ProjectConfigCollection( this );
  74. }
  75. #endregion
  76. #region Properties and Events
  77. /// <summary>
  78. /// The path to which a project will be saved.
  79. /// </summary>
  80. public string ProjectPath
  81. {
  82. get { return projectPath; }
  83. set
  84. {
  85. projectPath = Path.GetFullPath( value );
  86. isDirty = true;
  87. }
  88. }
  89. public string DefaultBasePath
  90. {
  91. get { return Path.GetDirectoryName( projectPath ); }
  92. }
  93. /// <summary>
  94. /// Indicates whether a base path was specified for the project
  95. /// </summary>
  96. public bool BasePathSpecified
  97. {
  98. get
  99. {
  100. return basePath != null && basePath != string.Empty;
  101. }
  102. }
  103. /// <summary>
  104. /// The base path for the project. Constructor sets
  105. /// it to the directory part of the project path.
  106. /// </summary>
  107. public string BasePath
  108. {
  109. get
  110. {
  111. if ( !BasePathSpecified )
  112. return DefaultBasePath;
  113. return basePath;
  114. }
  115. set
  116. {
  117. basePath = value;
  118. if (basePath != null && basePath != string.Empty
  119. && !Path.IsPathRooted(basePath))
  120. {
  121. basePath = Path.Combine(
  122. DefaultBasePath,
  123. basePath);
  124. }
  125. basePath = PathUtils.Canonicalize(basePath);
  126. HasChangesRequiringReload = IsDirty = true;
  127. }
  128. }
  129. /// <summary>
  130. /// The name of the project.
  131. /// </summary>
  132. public string Name
  133. {
  134. get { return Path.GetFileNameWithoutExtension( projectPath ); }
  135. }
  136. public bool AutoConfig
  137. {
  138. get { return autoConfig; }
  139. set { autoConfig = value; }
  140. }
  141. public ProjectConfig ActiveConfig
  142. {
  143. get
  144. {
  145. // In case the previous active config was removed
  146. if ( activeConfig != null && !configs.Contains( activeConfig ) )
  147. activeConfig = null;
  148. // In case no active config is set or it was removed
  149. if (activeConfig == null && configs.Count > 0)
  150. activeConfig = configs[0];
  151. return activeConfig;
  152. }
  153. }
  154. // Safe access to name of the active config
  155. public string ActiveConfigName
  156. {
  157. get
  158. {
  159. ProjectConfig config = ActiveConfig;
  160. return config == null ? null : config.Name;
  161. }
  162. }
  163. public bool IsLoadable
  164. {
  165. get
  166. {
  167. return ActiveConfig != null &&
  168. ActiveConfig.Assemblies.Count > 0;
  169. }
  170. }
  171. // A project made from a single assembly is treated
  172. // as a transparent wrapper for some purposes until
  173. // a change is made to it.
  174. public bool IsAssemblyWrapper
  175. {
  176. get { return isAssemblyWrapper; }
  177. set { isAssemblyWrapper = value; }
  178. }
  179. public string ConfigurationFile
  180. {
  181. get
  182. {
  183. // TODO: Check this
  184. return isAssemblyWrapper
  185. ? Path.GetFileName( projectPath ) + ".config"
  186. : Path.GetFileNameWithoutExtension( projectPath ) + ".config";
  187. }
  188. }
  189. public bool IsDirty
  190. {
  191. get { return isDirty; }
  192. set
  193. {
  194. isDirty = value;
  195. if (isAssemblyWrapper && value == true)
  196. {
  197. projectPath = Path.ChangeExtension(projectPath, ".nunit");
  198. isAssemblyWrapper = false;
  199. HasChangesRequiringReload = true;
  200. }
  201. }
  202. }
  203. public bool HasChangesRequiringReload
  204. {
  205. get { return reloadRequired; }
  206. set { reloadRequired = value; }
  207. }
  208. public ProcessModel ProcessModel
  209. {
  210. get { return processModel; }
  211. set
  212. {
  213. processModel = value;
  214. HasChangesRequiringReload = IsDirty = true;
  215. }
  216. }
  217. public DomainUsage DomainUsage
  218. {
  219. get { return domainUsage; }
  220. set
  221. {
  222. domainUsage = value;
  223. HasChangesRequiringReload = IsDirty = true;
  224. }
  225. }
  226. public ProjectConfigCollection Configs
  227. {
  228. get { return configs; }
  229. }
  230. #endregion
  231. #region Static Methods
  232. public static bool IsNUnitProjectFile(string path)
  233. {
  234. return Path.GetExtension(path) == Extension;
  235. }
  236. public static string ProjectPathFromFile(string path)
  237. {
  238. string fileName = Path.GetFileNameWithoutExtension(path) + NUnitProject.Extension;
  239. return Path.Combine(Path.GetDirectoryName(path), fileName);
  240. }
  241. #endregion
  242. #region Instance Methods
  243. public void SetActiveConfig( int index )
  244. {
  245. activeConfig = configs[index];
  246. HasChangesRequiringReload = IsDirty = true;
  247. }
  248. public void SetActiveConfig( string name )
  249. {
  250. foreach( ProjectConfig config in configs )
  251. {
  252. if ( config.Name == name )
  253. {
  254. activeConfig = config;
  255. HasChangesRequiringReload = IsDirty = true;
  256. break;
  257. }
  258. }
  259. }
  260. public void Add( VSProject vsProject )
  261. {
  262. foreach( VSProjectConfig vsConfig in vsProject.Configs )
  263. {
  264. string name = vsConfig.Name;
  265. if ( !configs.Contains( name ) )
  266. configs.Add( name );
  267. ProjectConfig config = this.Configs[name];
  268. foreach ( string assembly in vsConfig.Assemblies )
  269. config.Assemblies.Add( assembly );
  270. }
  271. }
  272. public void Load()
  273. {
  274. XmlTextReader reader = new XmlTextReader( projectPath );
  275. string activeConfigName = null;
  276. ProjectConfig currentConfig = null;
  277. try
  278. {
  279. reader.MoveToContent();
  280. if ( reader.NodeType != XmlNodeType.Element || reader.Name != "NUnitProject" )
  281. throw new ProjectFormatException(
  282. "Invalid project format: <NUnitProject> expected.",
  283. reader.LineNumber, reader.LinePosition );
  284. while( reader.Read() )
  285. if ( reader.NodeType == XmlNodeType.Element )
  286. switch( reader.Name )
  287. {
  288. case "Settings":
  289. if ( reader.NodeType == XmlNodeType.Element )
  290. {
  291. activeConfigName = reader.GetAttribute( "activeconfig" );
  292. string autoConfig = reader.GetAttribute("autoconfig");
  293. if (autoConfig != null)
  294. this.AutoConfig = autoConfig.ToLower() == "true";
  295. if (this.AutoConfig)
  296. activeConfigName = NUnitConfiguration.BuildConfiguration;
  297. string appbase = reader.GetAttribute( "appbase" );
  298. if ( appbase != null )
  299. this.BasePath = appbase;
  300. string processModel = reader.GetAttribute("processModel");
  301. if (processModel != null)
  302. this.ProcessModel = (ProcessModel)Enum.Parse(typeof(ProcessModel), processModel);
  303. string domainUsage = reader.GetAttribute("domainUsage");
  304. if (domainUsage != null)
  305. this.DomainUsage = (DomainUsage)Enum.Parse(typeof(DomainUsage), domainUsage);
  306. }
  307. break;
  308. case "Config":
  309. if ( reader.NodeType == XmlNodeType.Element )
  310. {
  311. string configName = reader.GetAttribute( "name" );
  312. currentConfig = new ProjectConfig( configName );
  313. currentConfig.BasePath = reader.GetAttribute( "appbase" );
  314. currentConfig.ConfigurationFile = reader.GetAttribute( "configfile" );
  315. string binpath = reader.GetAttribute( "binpath" );
  316. currentConfig.PrivateBinPath = binpath;
  317. string type = reader.GetAttribute( "binpathtype" );
  318. if ( type == null )
  319. if ( binpath == null )
  320. currentConfig.BinPathType = BinPathType.Auto;
  321. else
  322. currentConfig.BinPathType = BinPathType.Manual;
  323. else
  324. currentConfig.BinPathType = (BinPathType)Enum.Parse( typeof( BinPathType ), type, true );
  325. string runtime = reader.GetAttribute("runtimeFramework");
  326. if ( runtime != null )
  327. currentConfig.RuntimeFramework = RuntimeFramework.Parse(runtime);
  328. Configs.Add(currentConfig);
  329. if ( configName == activeConfigName )
  330. activeConfig = currentConfig;
  331. }
  332. else if ( reader.NodeType == XmlNodeType.EndElement )
  333. currentConfig = null;
  334. break;
  335. case "assembly":
  336. if ( reader.NodeType == XmlNodeType.Element && currentConfig != null )
  337. {
  338. string path = reader.GetAttribute( "path" );
  339. currentConfig.Assemblies.Add(
  340. Path.Combine( currentConfig.BasePath, path ) );
  341. }
  342. break;
  343. default:
  344. break;
  345. }
  346. this.IsDirty = false;
  347. this.reloadRequired = false;
  348. }
  349. catch( FileNotFoundException )
  350. {
  351. throw;
  352. }
  353. catch( XmlException e )
  354. {
  355. throw new ProjectFormatException(
  356. string.Format( "Invalid project format: {0}", e.Message ),
  357. e.LineNumber, e.LinePosition );
  358. }
  359. catch( Exception e )
  360. {
  361. throw new ProjectFormatException(
  362. string.Format( "Invalid project format: {0} Line {1}, Position {2}",
  363. e.Message, reader.LineNumber, reader.LinePosition ),
  364. reader.LineNumber, reader.LinePosition );
  365. }
  366. finally
  367. {
  368. reader.Close();
  369. }
  370. }
  371. public void Save()
  372. {
  373. projectPath = ProjectPathFromFile( projectPath );
  374. XmlTextWriter writer = new XmlTextWriter( projectPath, System.Text.Encoding.UTF8 );
  375. writer.Formatting = Formatting.Indented;
  376. writer.WriteStartElement( "NUnitProject" );
  377. if ( configs.Count > 0 || this.BasePath != this.DefaultBasePath )
  378. {
  379. writer.WriteStartElement( "Settings" );
  380. if ( configs.Count > 0 )
  381. writer.WriteAttributeString( "activeconfig", ActiveConfigName );
  382. if ( this.BasePath != this.DefaultBasePath )
  383. writer.WriteAttributeString( "appbase", this.BasePath );
  384. if (this.AutoConfig)
  385. writer.WriteAttributeString("autoconfig", "true");
  386. if (this.ProcessModel != ProcessModel.Default)
  387. writer.WriteAttributeString("processModel", this.ProcessModel.ToString());
  388. if (this.DomainUsage != DomainUsage.Default)
  389. writer.WriteAttributeString("domainUsage", this.DomainUsage.ToString());
  390. writer.WriteEndElement();
  391. }
  392. foreach( ProjectConfig config in Configs )
  393. {
  394. writer.WriteStartElement( "Config" );
  395. writer.WriteAttributeString( "name", config.Name );
  396. string appbase = config.BasePath;
  397. if ( !PathUtils.SamePathOrUnder( this.BasePath, appbase ) )
  398. writer.WriteAttributeString( "appbase", appbase );
  399. else if ( config.RelativeBasePath != null )
  400. writer.WriteAttributeString( "appbase", config.RelativeBasePath );
  401. string configFile = config.ConfigurationFile;
  402. if ( configFile != null && configFile != this.ConfigurationFile )
  403. writer.WriteAttributeString( "configfile", config.ConfigurationFile );
  404. if ( config.BinPathType == BinPathType.Manual )
  405. writer.WriteAttributeString( "binpath", config.PrivateBinPath );
  406. else
  407. writer.WriteAttributeString( "binpathtype", config.BinPathType.ToString() );
  408. if (config.RuntimeFramework != null)
  409. writer.WriteAttributeString("runtimeFramework", config.RuntimeFramework.ToString());
  410. foreach( string assembly in config.Assemblies )
  411. {
  412. writer.WriteStartElement( "assembly" );
  413. writer.WriteAttributeString( "path", PathUtils.RelativePath( config.BasePath, assembly ) );
  414. writer.WriteEndElement();
  415. }
  416. writer.WriteEndElement();
  417. }
  418. writer.WriteEndElement();
  419. writer.Close();
  420. this.IsDirty = false;
  421. // Once we save a project, it's no longer
  422. // loaded as an assembly wrapper on reload.
  423. this.isAssemblyWrapper = false;
  424. }
  425. public void Save( string projectPath )
  426. {
  427. this.ProjectPath = projectPath;
  428. Save();
  429. }
  430. #endregion
  431. }
  432. }