/// <summary> /// Instantiate a new SourceLineNumberCollection from an array of SourceLineNumber objects. /// </summary> /// <param name="sourceLineNumbers">The SourceLineNumber objects.</param> public SourceLineNumberCollection(SourceLineNumber[] sourceLineNumbers) { if (null == sourceLineNumbers) { throw new ArgumentNullException("sourceLineNumbers"); } this.sourceLineNumbers = sourceLineNumbers; }
/// <summary> /// Creates a new preprocesor. /// </summary> public Preprocessor() { this.extensionMessages = new ExtensionMessages(this); this.variables = new Hashtable(); this.includeSearchPaths = new StringCollection(); this.extensionTypes = new Hashtable(); this.currentLineNumber = null; this.sourceStack = new Stack(); this.includeNextStack = new Stack(); this.foundError = false; this.preprocessOut = null; // add the system variables this.variables.Add("sys.CURRENTDIR", String.Concat(Directory.GetCurrentDirectory(), "\\")); }
public XmlDocument Process(string sourceFile, Hashtable variables) { Stream processed = new MemoryStream(); XmlDocument sourceDocument = new XmlDocument(); try { this.core = new PreprocessorCore(this.extensionsByPrefix, this.Message, sourceFile, variables); this.core.ResolvedVariableHandler = this.ResolvedVariable; this.core.CurrentPlatform = this.currentPlatform; this.currentLineNumberWritten = false; this.currentLineNumber = new SourceLineNumber(sourceFile); this.currentFileStack.Clear(); this.currentFileStack.Push(this.core.GetVariableValue(this.GetCurrentSourceLineNumbers(), "sys", "SOURCEFILEDIR")); // open the source file for processing using (Stream sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read, FileShare.Read)) { // inspect the raw source InspectorCore inspectorCore = new InspectorCore(this.Message); foreach (InspectorExtension inspectorExtension in this.inspectorExtensions) { inspectorExtension.Core = inspectorCore; inspectorExtension.InspectSource(sourceStream); // reset inspectorExtension.Core = null; sourceStream.Position = 0; } if (inspectorCore.EncounteredError) { return null; } XmlReader reader = new XmlTextReader(sourceStream); XmlTextWriter writer = new XmlTextWriter(processed, Encoding.UTF8); // process the reader into the writer try { foreach (PreprocessorExtension extension in this.extensions) { extension.Core = this.core; extension.InitializePreprocess(); } this.PreprocessReader(false, reader, writer, 0); } catch (XmlException e) { this.UpdateInformation(reader, 0); throw new WixException(WixErrors.InvalidXml(this.GetCurrentSourceLineNumbers(), "source", e.Message)); } writer.Flush(); } // fire event with processed stream ProcessedStreamEventArgs args = new ProcessedStreamEventArgs(sourceFile, processed); this.OnProcessedStream(args); // create an XML Document from the post-processed memory stream XmlTextReader xmlReader = null; try { // create an XmlReader with the path to the original file // to properly set the BaseURI property of the XmlDocument processed.Position = 0; xmlReader = new XmlTextReader(new Uri(Path.GetFullPath(sourceFile)).AbsoluteUri, processed); sourceDocument.Load(xmlReader); } catch (XmlException e) { this.UpdateInformation(xmlReader, 0); throw new WixException(WixErrors.InvalidXml(this.GetCurrentSourceLineNumbers(), "source", e.Message)); } finally { if (null != xmlReader) { xmlReader.Close(); } } // preprocess the generated XML Document foreach (PreprocessorExtension extension in this.extensions) { extension.PreprocessDocument(sourceDocument); } } finally { // finalize the preprocessing foreach (PreprocessorExtension extension in this.extensions) { extension.FinalizePreprocess(); extension.Core = null; } } if (this.core.EncounteredError) { return null; } else { if (null != this.preprocessOut) { sourceDocument.Save(this.preprocessOut); this.preprocessOut.Flush(); } return sourceDocument; } }
/// <summary> /// Pops a file name from the stack of included files. /// </summary> private void PopInclude() { this.currentLineNumber = (SourceLineNumber)this.sourceStack.Pop(); this.currentFileStack.Pop(); this.includeNextStack.Pop(); }
/// <summary> /// Pushes a file name on the stack of included files. /// </summary> /// <param name="fileName">Name to push on to the stack of included files.</param> private void PushInclude(string fileName) { this.currentFileStack.Push(fileName); this.sourceStack.Push(this.currentLineNumber); this.currentLineNumber = new SourceLineNumber(fileName); this.includeNextStack.Push(true); }
/// <summary> /// Main running method for the application. /// </summary> /// <param name="args">Commandline arguments to the application.</param> /// <returns>Returns the application error code.</returns> private int Run(string[] args) { int beginTickCount = Environment.TickCount; try { this.tempFileCollection = new TempFileCollection(); Environment.SetEnvironmentVariable("WixUnitTempDir", this.tempFileCollection.BasePath, EnvironmentVariableTarget.Process); this.ParseCommandline(args); // get the assemblies Assembly wixUnitAssembly = this.GetType().Assembly; FileVersionInfo fv = FileVersionInfo.GetVersionInfo(wixUnitAssembly.Location); if (this.showHelp) { Console.WriteLine("WixUnit version {0}", fv.FileVersion); Console.WriteLine("Copyright (C) .NET Foundation and contributors. All rights reserved."); Console.WriteLine(); Console.WriteLine(" usage: WixUnit [-?] tests.xml"); Console.WriteLine(); Console.WriteLine(" -env:<var>=<value> Sets an environment variable to the value for the current process"); Console.WriteLine(" -notidy Do not delete temporary files (for checking results)"); Console.WriteLine(" -rf Re-run the failed test from the last run"); Console.WriteLine(" -st Run the tests on a single thread"); Console.WriteLine(" -test:<Test_name> Run only the specified test (may use wildcards)"); Console.WriteLine(" -update Prompt user to auto-update a test if expected and actual output files do not match"); Console.WriteLine(" -v Verbose output"); Console.WriteLine(" -val Run MSI validation for light unit tests"); return 0; } // set the environment variables for the process only foreach (KeyValuePair<string, string> environmentVariable in this.environmentVariables) { Environment.SetEnvironmentVariable(environmentVariable.Key, environmentVariable.Value, EnvironmentVariableTarget.Process); } // load the schema XmlReader schemaReader = null; XmlSchemaCollection schemas = null; try { schemas = new XmlSchemaCollection(); schemaReader = new XmlTextReader(wixUnitAssembly.GetManifestResourceStream("Microsoft.Tools.WindowsInstallerXml.Unit.unitTests.xsd")); XmlSchema schema = XmlSchema.Read(schemaReader, null); schemas.Add(schema); } finally { if (schemaReader != null) { schemaReader.Close(); } } // load the unit tests XmlTextReader reader = null; XmlDocument doc = new XmlDocument(); try { reader = new XmlTextReader(this.unitTestsFile); XmlValidatingReader validatingReader = new XmlValidatingReader(reader); validatingReader.Schemas.Add(schemas); // load the xml into a DOM doc.Load(validatingReader); } catch (XmlException e) { SourceLineNumber sourceLineNumber = new SourceLineNumber(this.unitTestsFile, e.LineNumber); SourceLineNumberCollection sourceLineNumbers = new SourceLineNumberCollection(new SourceLineNumber[] { sourceLineNumber }); throw new WixException(WixErrors.InvalidXml(sourceLineNumbers, "unitTests", e.Message)); } catch (XmlSchemaException e) { SourceLineNumber sourceLineNumber = new SourceLineNumber(this.unitTestsFile, e.LineNumber); SourceLineNumberCollection sourceLineNumbers = new SourceLineNumberCollection(new SourceLineNumber[] { sourceLineNumber }); throw new WixException(WixErrors.SchemaValidationFailed(sourceLineNumbers, e.Message, e.LineNumber, e.LinePosition)); } finally { if (reader != null) { reader.Close(); } } // check the document element if ("UnitTests" != doc.DocumentElement.LocalName || XmlNamespace != doc.DocumentElement.NamespaceURI) { throw new InvalidOperationException("Unrecognized document element."); } // create a regular expression of the selected tests Regex selectedUnitTests = new Regex(String.Concat("^", String.Join("$|^", (string[])this.unitTests.ToArray(typeof(string))), "$"), RegexOptions.IgnoreCase | RegexOptions.Singleline); // find the unit tests foreach (XmlNode node in doc.DocumentElement) { if (XmlNodeType.Element == node.NodeType) { switch (node.LocalName) { case "UnitTest": XmlElement unitTestElement = (XmlElement)node; string unitTestName = unitTestElement.GetAttribute("Name"); if (selectedUnitTests.IsMatch(unitTestName)) { unitTestElement.SetAttribute("TempDirectory", this.tempFileCollection.BasePath); this.unitTestElements.Enqueue(node); } break; } } } if (this.unitTests.Count > 0) { this.totalUnitTests = this.unitTestElements.Count; int numThreads; if (this.updateTests || this.singleThreaded) { // If the tests are running with the -update switch, they must run on one thread // so that all execution is paused when the user is prompted to update a test. numThreads = 1; } else { // create a thread for each processor numThreads = Convert.ToInt32(Environment.GetEnvironmentVariable("NUMBER_OF_PROCESSORS"), CultureInfo.InvariantCulture); } Thread[] threads = new Thread[numThreads]; for (int i = 0; i < threads.Length; i++) { threads[i] = new Thread(new ThreadStart(this.RunUnitTests)); threads[i].Start(); } // wait for all threads to finish foreach (Thread thread in threads) { thread.Join(); } // report the results Console.WriteLine(); int elapsedTime = (Environment.TickCount - beginTickCount) / 1000; if (0 < this.failedUnitTests.Count) { Console.WriteLine("Summary of failed tests:"); Console.WriteLine(); // Put the failed tests into an ArrayList, which will get serialized ArrayList serializedFailedTests = new ArrayList(); foreach (string failedTest in this.failedUnitTests.Keys) { serializedFailedTests.Add(failedTest); Console.WriteLine("{0}. {1}", this.failedUnitTests[failedTest], failedTest); } Console.WriteLine(); Console.WriteLine("Re-run the failed tests with the -rf option"); Console.WriteLine(); Console.WriteLine("Failed {0} out of {1} unit test{2} ({3} seconds).", this.failedUnitTests.Count, this.totalUnitTests, (1 != this.completedUnitTests ? "s" : ""), elapsedTime); using (XmlWriter writer = XmlWriter.Create(this.failedTestsFile)) { XmlSerializer serializer = new XmlSerializer(serializedFailedTests.GetType()); serializer.Serialize(writer, serializedFailedTests); writer.Close(); } } else { Console.WriteLine("Successful unit tests: {0} ({1} seconds).", this.completedUnitTests, elapsedTime); } Console.WriteLine(); } else { Console.WriteLine("No unit tests were selected."); } } catch (WixException we) { this.messageHandler.Display(this, we.Error); } catch (Exception e) { this.messageHandler.Display(this, WixErrors.UnexpectedException(e.Message, e.GetType().ToString(), e.StackTrace)); if (e is NullReferenceException) { throw; } } finally { if (this.noTidy) { Console.WriteLine(); Console.WriteLine("The notidy option was specified, temporary files can be found at:"); Console.WriteLine(this.tempFileCollection.BasePath); } else { // try three times and give up with a warning if the temp files aren't gone by then const int RetryLimit = 3; for (int i = 0; i < RetryLimit; i++) { try { Directory.Delete(this.tempFileCollection.BasePath, true); // toast the whole temp directory break; // no exception means we got success the first time } catch (UnauthorizedAccessException) { if (0 == i) // should only need to unmark readonly once - there's no point in doing it again and again { RecursiveFileAttributes(this.tempFileCollection.BasePath, FileAttributes.ReadOnly, false); // toasting will fail if any files are read-only. Try changing them to not be. } else { break; } } catch (DirectoryNotFoundException) { // if the path doesn't exist, then there is nothing for us to worry about break; } catch (IOException) // directory in use { if (i == (RetryLimit - 1)) // last try failed still, give up { break; } Thread.Sleep(300); // sleep a bit before trying again } } } } return this.failedUnitTests.Count; }
/// <summary> /// Pushes a file name on the stack of included files. /// </summary> /// <param name="fileName">Name to push on to the stack of included files.</param> private void PushInclude(string fileName) { if (1023 < this.currentFileStack.Count) { throw new WixException(WixErrors.TooDeeplyIncluded(this.GetCurrentSourceLineNumbers(), this.currentFileStack.Count)); } this.currentFileStack.Push(fileName); this.sourceStack.Push(this.currentLineNumber); this.currentLineNumber = new SourceLineNumber(fileName); this.includeNextStack.Push(true); }
/// <summary> /// Clone the object. /// </summary> /// <returns>Returns a new instance of the object with the same values.</returns> public SourceLineNumber Clone() { SourceLineNumber newSourceLineNumber = new SourceLineNumber(this.fileName); newSourceLineNumber.lineNumber = this.lineNumber; newSourceLineNumber.hasLineNumber = this.hasLineNumber; return newSourceLineNumber; }
/// <summary> /// Preprocesses a file. /// </summary> /// <param name="sourcePath">Path to the file to preprocess.</param> /// <returns>XmlDocument with the postprocessed data validated by any schemas set in the preprocessor.</returns> public XmlDocument Process(string sourcePath) { FileInfo sourceFile = new FileInfo(sourcePath); StringWriter processed = new StringWriter(); this.currentLineNumberWritten = false; this.currentLineNumber = new SourceLineNumber(sourceFile.FullName); this.foundError = false; // add the current source file and current source path to the system variables this.variables["sys.SOURCEFILEPATH"] = sourceFile.FullName; this.variables["sys.SOURCEFILEDIR"] = String.Concat(sourceFile.DirectoryName, "\\"); // open the source file for processing using (Stream sourceStream = new FileStream(sourceFile.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) { XmlReader reader = new XmlTextReader(sourceStream); XmlTextWriter writer = new XmlTextWriter(processed); writer.Formatting = Formatting.Indented; // process the reader into the writer try { foreach (PreprocessorExtension extension in this.extensionTypes.Values) { extension.Variables = this.variables; extension.Messages = this.extensionMessages; extension.InitializePreprocess(); } this.PreprocessReader(false, reader, writer, 0); } catch (XmlException e) { this.UpdateInformation(reader, 0); throw new WixInvalidXmlException(this.GetCurrentSourceLineNumbers(), e); } writer.Close(); } foreach (PreprocessorExtension extension in this.extensionTypes.Values) { processed = extension.PreprocessDocument(processed); } // do not continue processing if an error was encountered in one of the extensions if (this.foundError) { return null; } if (this.preprocessOut != null) { this.preprocessOut.WriteLine(processed.ToString()); this.preprocessOut.Flush(); } // create an XML Document from the post-processed memory stream XmlDocument sourceDocument = new XmlDocument(); using (StringReader reader = new StringReader(processed.ToString())) { try { sourceDocument.Load(reader); } catch (XmlException) { this.OnMessage(WixErrors.SP1ProbablyNotInstalled()); } } return (this.foundError ? null : sourceDocument); }