private XmlOutputData ReadXmlForOutputFiles(string xmlPath, string subFolder) { XmlOutputData outputData = new XmlOutputData(); try { // load in the xml file XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(xmlPath); // there should be at least one output node XmlNodeList outputNodes = xmlDoc.SelectNodes("//output"); if (outputNodes.Count > 0) { // create the list now and use the number of nodes as the initial capacity outputData.OutputFiles = new List <string>(outputNodes.Count); outputData.OutputMapFiles = new List <string>(outputNodes.Count); for (int ndx = 0; ndx < outputNodes.Count; ++ndx) { // get the output path attribute and add it to the output files list XmlAttribute pathAttribute = outputNodes[ndx].Attributes["path"]; // must exist and be non-empty for the purposes of this unit test because we // can't really check for stdout files in this batch mode if (pathAttribute == null || string.IsNullOrEmpty(pathAttribute.Value)) { Assert.Fail("XML <output> nodes without path attributes not supported in unit tests"); } // create the full path from the output folder, the subfolder, and the attribute. // don't check for existence here -- we haven't run the test yet string outputPath = GetJsPath( m_outputFolder, subFolder, pathAttribute.Value, false ); // if the output file exists, it must be from a previous run. // delete it now (the Delete method does not fail if the doesn't already exist, // but it WILL fail if the path to the file doesn't) if (File.Exists(outputPath)) { File.Delete(outputPath); } outputData.OutputFiles.Add(outputPath); XmlAttribute mapPathAttribute = outputNodes[ndx].Attributes["mappath"]; if (mapPathAttribute != null) { string mapPath = this.BuildFullPath( m_outputFolder, subFolder, mapPathAttribute.Value, ".xml", false ); if (File.Exists(mapPath)) { File.Delete(mapPath); } outputData.OutputMapFiles.Add(mapPath); } } } else { Assert.Fail("XML input file contains no <output> nodes"); } } catch (XmlException e) { Debug.WriteLine(e.ToString()); Assert.Fail("XML Exception processing XML input file: {0}", e.Message); } return(outputData); }
public void RunTest(bool inputExpected, string extraArguments, params string[] extraInputs) { // open the stack trace for this call StackTrace stackTrace = new StackTrace(); string testClass = null; string testName = null; // save the name of the current method (RunTest) string currentMethodName = MethodInfo.GetCurrentMethod().Name; // loop from the previous frame up until we get a method name that is not the // same as the current method name for (int ndx = 1; ndx < stackTrace.FrameCount; ++ndx) { // get the frame StackFrame stackFrame = stackTrace.GetFrame(ndx); // we have different entry points with the same name -- we're interested // in the first one that ISN'T the same name as our method MethodBase methodBase = stackFrame.GetMethod(); if (methodBase.Name != currentMethodName) { // the calling method's name is the test name - we use this as-is for the output file name // and we use any portion before an underscore as the input file testName = methodBase.Name; // get the method's class - we use this as the subfolder under input/output/expected testClass = methodBase.DeclaringType.Name; break; } } // we definitely should be able to find a function on the stack frame that // has a different name than this function, but just in case... Debug.Assert(testName != null && testClass != null, "Couldn't locate calling stack frame"); // the output file is just the full test name string outputFile = testName; string outputMapFile = null; List <string> outputFiles = null; List <string> outputMapFiles = null; // the input file is the portion of the test name before the underscore (if any) string inputFile = testName.Split('_')[0]; string inputPath = null; int inputCount = 0; // create a list we will append all our arguments to bool includeAnalysis = true; bool specifiesRename = false; LinkedList <string> args = new LinkedList <string>(); if (!string.IsNullOrEmpty(extraArguments)) { // split on spaces string[] options = extraArguments.Split(' '); // add each one to the args list for (int ndx = 0; ndx < options.Length; ++ndx) { string option = options[ndx]; // ignore empty strings if (option.Length > 0) { args.AddLast(option); if (string.Compare(option, "-analyze", StringComparison.OrdinalIgnoreCase) == 0) { // don't include it -- we already added it includeAnalysis = false; } else if (string.Compare(option, "-xml", StringComparison.OrdinalIgnoreCase) == 0) { // the next option should be an xml file name, so we'll add an option // that is the test name, the .xml suffix, and scope it to the input path. // set the inputPath variable to this path so we know we are going to use it // as the "input" inputPath = BuildFullPath( InputFolder, testClass, inputFile, ".xml", true ); args.AddLast(inputPath); ++inputCount; XmlOutputData outputData = ReadXmlForOutputFiles(inputPath, testClass); outputFiles = outputData.OutputFiles; outputMapFiles = outputData.OutputMapFiles; // generate the expected file names from the output file names, plus any test qualifiers } else if (string.Compare(option, "-rename", StringComparison.OrdinalIgnoreCase) == 0) { // rename with no param parts (a colon and other stuff) means the next // option should be an xml file name, so we'll add an option // that is the file scoped to the input path. string nextFile = options[++ndx]; string renamePath = BuildFullPath( InputFolder, testClass, Path.GetFileNameWithoutExtension(nextFile), Path.GetExtension(nextFile), true); // add that scoped path to the arguments args.AddLast(renamePath); } // the -r option can have a subpart, eg: -res:Strings, so only test to see if // the first two characters of the current option are "-res" else if (option.StartsWith("-res", StringComparison.OrdinalIgnoreCase)) { // the next option is a resource file name, so we'll need to scope it to the input path // FIRST we'll try to see if there's an existing compiled .RESOURCES file with the same // name as the current test. eg: if test name is "foo_h", look for foo.resources string resourcePath = BuildFullPath( InputFolder, testClass, inputFile, ".resources", false ); if (!File.Exists(resourcePath)) { // if there's not .RESOURCES file, look for a .RESX file with the same // name as the current test. eg: if test name is "foo_h", look for foo.resx resourcePath = BuildFullPath( InputFolder, testClass, inputFile, ".resx", false ); if (!File.Exists(resourcePath)) { // doesn't exist! Assert.Fail( "Expected resource file does not exist for test '{0}' in folder {1}", inputFile, Path.Combine(InputFolder, testClass) ); } } args.AddLast(resourcePath); } else if (option.StartsWith("-rename:", StringComparison.OrdinalIgnoreCase) && option.IndexOf('=') < 0 && option.IndexOf("prop", StringComparison.OrdinalIgnoreCase) < 0) { specifiesRename = true; } else if (option.StartsWith("-map", StringComparison.OrdinalIgnoreCase)) { outputMapFile = GetJsPath( m_outputFolder, testClass, outputFile, false ) + ".map"; if (File.Exists(outputMapFile)) { File.Delete(outputMapFile); } args.AddLast(outputMapFile); } } } } // if we haven't already specified analyze option if (includeAnalysis) { // add the -a option args.AddLast("-analyze"); } // if we haven't already specified a renaming option, we will // use -rename:none so we don't have to always figure out what the hypercrunch // should be if (!specifiesRename) { args.AddLast("-rename:none"); } string outputPath = null; // if we haven't already set an input path, then we want to calculate the input/output // paths automatically from the test name (normal case) if (inputPath == null) { // compute the path to the output file outputPath = GetJsPath( m_outputFolder, testClass, outputFile, false ); // if it exists already, delete it if (File.Exists(outputPath)) { File.Delete(outputPath); } // add the output parameter to the end args.AddLast("-out"); args.AddLast(outputPath); Trace.WriteLine("INPUT FILE(S):"); // calculate the input path inputPath = GetJsPath( InputFolder, testClass, inputFile, false ); // always add the input file to the command line args.AddLast(inputPath); if (File.Exists(inputPath)) { // but don't trace its contents unless it actually exists ++inputCount; TraceFileContents(inputPath); } else { Trace.WriteLine("[input file does not exist]"); } // if there are any extra input files, add them now if (extraInputs != null && extraInputs.Length > 0) { foreach (string extraInput in extraInputs) { if (extraInput.Length > 0) { // get the full path inputPath = GetJsPath( InputFolder, testClass, extraInput, true ); // add it to the list args.AddLast(inputPath); // output the file contents Trace.WriteLine(string.Empty); TraceFileContents(inputPath); ++inputCount; } } } } else { Trace.WriteLine("INPUT FILE:"); TraceFileContents(inputPath); } // create an array of strings the appropriate size string[] mainArguments = new string[args.Count]; // copy the arguments to the array args.CopyTo(mainArguments, 0); // show command-line args Trace.WriteLine(string.Empty); Trace.WriteLine("COMMAND-LINE SWITCHES:"); foreach (string arg in mainArguments) { if (arg.IndexOf(' ') >= 0) { // at least one space -- enclose the argument in quotes Trace.Write('"'); Trace.Write(arg); Trace.Write('"'); } else { // no spaces; don't need quotes Trace.Write(arg); } Trace.Write(' '); } Trace.WriteLine(string.Empty); // call the NUglify main function Trace.WriteLine(string.Empty); Trace.WriteLine("AJAXMIN Debug Spew:"); // call Main directly int retValue = MainClass.Main(mainArguments); Trace.Write("RETURN CODE: "); Trace.WriteLine(retValue); // after the run, if we had inputs and one output file... if (inputCount > 0 && !string.IsNullOrEmpty(outputPath)) { // compute the path to the expected file string expectedPath = GetJsPath( m_expectedFolder, testClass, outputFile, false ); Trace.WriteLine(string.Empty); Trace.WriteLine("odd \"" + expectedPath + "\" \"" + outputPath + "\""); Trace.WriteLine(string.Empty); Trace.WriteLine("EXPECTED OUTPUT FILE:"); if (File.Exists(expectedPath)) { // trace output contents TraceFileContents(expectedPath); } else { // no expected file means we expect the output to be empty Trace.WriteLine("File doesn't exist -- expect output file to be empty"); } // the output file BETTER exist (even if it's just empty)... if (File.Exists(outputPath)) { Trace.WriteLine(string.Empty); Trace.WriteLine("ACTUAL OUTPUT FILE:"); // trace output contents TraceFileContents(outputPath); // fail the test if the files do not match AssertCompareTextFiles(outputPath, expectedPath); //Assert.IsTrue(retValue == 0, "Run didn't succeed. Return code: {0}", retValue); } else if (File.Exists(expectedPath)) { // no output file, but we did expect an output! That is a failure Assert.Fail("Output file does not exist, but one was expected!"); } else { // input file(s) and output file, but can't find output Assert.IsTrue( retValue != 0, "Run shouldn't succeed if no output is generated. Return code: {0}; output file: {1}", retValue, outputPath ); } if (outputMapFile != null) { string expectedMapFile = this.BuildFullPath( m_expectedFolder, testClass, Path.GetFileName(outputMapFile), ".map", true ); this.CompareSymbolMapFiles(outputMapFile, expectedMapFile); } } else if (inputCount > 0) { if (outputFiles != null && outputFiles.Count > 0) { // get the test suffix, if any var testSuffix = string.Empty; var ndxUnderscore = testName.IndexOf('_'); if (ndxUnderscore > 0) { // we have one. Use it as a suffix for the expected file testSuffix = testName.Substring(ndxUnderscore); } // for each one... for (int ndx = 0; ndx < outputFiles.Count; ++ndx) { outputPath = outputFiles[ndx]; // compute the expected file path from the filename of the output path // with any test extension added string expectedPath = GetJsPath( m_expectedFolder, testClass, Path.GetFileNameWithoutExtension(outputPath) + testSuffix + Path.GetExtension(outputPath), false ); // trace the expected file contents Trace.WriteLine(string.Empty); Trace.WriteLine(string.Format("EXPECTED OUTPUT FILE {0}:", ndx + 1)); if (File.Exists(expectedPath)) { // trace output contents TraceFileContents(expectedPath); } else { // no expected file means we expect the output to be empty Trace.WriteLine("File doesn't exist -- expect output file to be empty"); } // trace the output file contents Trace.WriteLine(string.Empty); Trace.WriteLine(string.Format("ACTUAL OUTPUT FILE {0}:", ndx + 1)); // trace output contents if (File.Exists(outputPath)) { TraceFileContents(outputPath); } else { Trace.WriteLine("Output file doesn't exist"); } // fail the entire test if the files do not match AssertCompareTextFiles(outputPath, expectedPath); } } else { // input file(s), but no output file Assert.Fail("No output files"); } if (outputMapFiles != null) { for (int ndx = 0; ndx < outputMapFiles.Count; ++ndx) { outputMapFile = outputMapFiles[ndx]; string expectedMapFile = this.BuildFullPath( m_expectedFolder, testClass, Path.GetFileNameWithoutExtension(outputMapFile), ".xml", true ); this.CompareSymbolMapFiles(outputMapFile, expectedMapFile); } } } else { // no input file(s) Trace.WriteLine("No input file(s)."); // if we expected there to be input files, then we failed Assert.IsFalse(inputExpected, "Expected input files to exist"); // and if we didn't expect the input files to exist, we better have failed Assert.IsTrue(retValue != 0, "Run shouldn't succeed if no input file(s). Return code: {0}", retValue); } }