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