1 // 2 // 3 // Authors: 4 // Marek Habersack (mhabersack@novell.com) 5 // 6 // (C) 2010 Novell, Inc http://novell.com/ 7 // 8 9 // 10 // Permission is hereby granted, free of charge, to any person obtaining 11 // a copy of this software and associated documentation files (the 12 // "Software"), to deal in the Software without restriction, including 13 // without limitation the rights to use, copy, modify, merge, publish, 14 // distribute, sublicense, and/or sell copies of the Software, and to 15 // permit persons to whom the Software is furnished to do so, subject to 16 // the following conditions: 17 // 18 // The above copyright notice and this permission notice shall be 19 // included in all copies or substantial portions of the Software. 20 // 21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 // 29 using System; 30 using System.Collections.Generic; 31 using System.IO; 32 using System.Reflection; 33 using System.Text; 34 using System.Web; 35 using System.Web.Hosting; 36 37 using Mono.Options; 38 39 using StandAloneRunnerSupport; 40 41 namespace StandAloneRunner 42 { 43 sealed class StandAloneRunner 44 { 45 static string testsAssembly; 46 Usage(int exitCode, OptionSet options)47 static void Usage (int exitCode, OptionSet options) 48 { 49 Console.WriteLine (@"Usage: standalone-runner [OPTIONS] <TESTS_ASSEMBLY>"); 50 if (options != null) { 51 Console.WriteLine (); 52 Console.WriteLine ("Available options:"); 53 options.WriteOptionDescriptions (Console.Out); 54 } 55 Console.WriteLine (); 56 57 Environment.Exit (exitCode); 58 } 59 Die(string format, params object[] args)60 public static void Die (string format, params object[] args) 61 { 62 Console.WriteLine ("Standalone test failure in assembly '{0}'.", testsAssembly); 63 Console.WriteLine (); 64 65 if (args == null || args.Length == 0) 66 Console.WriteLine ("Error: " + format); 67 else 68 Console.WriteLine ("Error: " + format, args); 69 70 Environment.Exit (1); 71 } 72 Main(string[] args)73 static void Main (string[] args) 74 { 75 try { 76 var success = Run (args); 77 78 if (!success) 79 Environment.Exit (1); 80 } catch (Exception ex) { 81 Die ("Exception caught:{0}{1}", Environment.NewLine, ex.ToString ()); 82 } 83 } 84 Run(string[] args)85 static bool Run (string[] args) 86 { 87 bool showHelp = false; 88 string testName = null; 89 string outputName = null; 90 bool verbose = false; 91 92 var options = new OptionSet () { 93 {"?|h|help", "Show short usage screen.", v => showHelp = true}, 94 {"t=|test=", "Run this test only (full type name)", (string s) => testName = s}, 95 {"o=|output=", "Output test results to the console and to the indicated file", (string s) => outputName = s}, 96 {"v|verbose", "Print the running test name", v => verbose = true} 97 }; 98 99 List <string> extra = options.Parse (args); 100 int extraCount = extra.Count; 101 102 if (showHelp || extraCount < 1) 103 Usage (showHelp ? 0 : 1, options); 104 105 testsAssembly = extra [0]; 106 107 if (!File.Exists (testsAssembly)) 108 Die ("Tests assembly '{0}' does not exist.", testsAssembly); 109 110 Assembly asm = Assembly.LoadFrom (testsAssembly); 111 var tests = new List <StandaloneTest> (); 112 113 LoadTestsFromAssembly (asm, tests); 114 if (tests.Count == 0) 115 Die ("No tests present in the '{0}' assembly. Tests must be public types decorated with the TestCase attribute and implementing the ITestCase interface.", testsAssembly); 116 117 ApplicationManager appMan = ApplicationManager.GetApplicationManager (); 118 int runCounter = 0; 119 int failedCounter = 0; 120 var reports = new List <string> (); 121 122 Console.WriteLine ("Running tests:"); 123 DateTime start = DateTime.Now; 124 125 foreach (StandaloneTest test in tests) { 126 if (test.Info.Disabled) 127 continue; 128 129 if (!String.IsNullOrEmpty (testName)) { 130 if (String.Compare (test.TestType.FullName, testName) != 0) 131 continue; 132 } 133 134 test.Run (appMan, verbose); 135 runCounter++; 136 if (!test.Success) { 137 failedCounter++; 138 reports.Add (FormatReport (test)); 139 } 140 } 141 142 DateTime end = DateTime.Now; 143 Console.WriteLine (); 144 StreamWriter writer; 145 146 if (!String.IsNullOrEmpty (outputName)) 147 writer = new StreamWriter (outputName); 148 else 149 writer = null; 150 151 try { 152 string report; 153 if (reports.Count > 0) { 154 int repCounter = 0; 155 int numWidth = reports.Count.ToString ().Length; 156 string indent = String.Empty.PadLeft (numWidth + 2); 157 string numFormat = "{0," + numWidth + "}) "; 158 159 foreach (string r in reports) { 160 repCounter++; 161 Console.Write (numFormat, repCounter); 162 report = FormatLines (indent, r, Environment.NewLine, true); 163 Console.WriteLine (report); 164 if (writer != null) { 165 writer.Write (numFormat, repCounter); 166 writer.WriteLine (report); 167 } 168 } 169 } else 170 Console.WriteLine (); 171 172 report = String.Format ("Tests run: {0}; Total tests: {1}; Failed: {2}; Not run: {3}; Time taken: {4}", 173 runCounter, tests.Count, failedCounter, tests.Count - runCounter, end - start); 174 Console.WriteLine (report); 175 if (writer != null) 176 writer.WriteLine (report); 177 } finally { 178 if (writer != null) { 179 writer.Close (); 180 writer.Dispose (); 181 } 182 } 183 184 return failedCounter == 0; 185 } 186 FormatReport(StandaloneTest test)187 static string FormatReport (StandaloneTest test) 188 { 189 var sb = new StringBuilder (); 190 string newline = Environment.NewLine; 191 192 sb.AppendFormat ("{0}{1}", test.Info.Name, newline); 193 sb.AppendFormat ("{0,16}: {1}{2}", "Test", test.TestType, newline); 194 195 if (!String.IsNullOrEmpty (test.Info.Description)) 196 sb.AppendFormat ("{0,16}: {1}{2}", "Description", test.Info.Description, newline); 197 198 if (!String.IsNullOrEmpty (test.FailedUrl)) 199 sb.AppendFormat ("{0,16}: {1}{2}", "Failed URL", test.FailedUrl, newline); 200 201 if (!String.IsNullOrEmpty (test.FailedUrlCallbackName)) 202 sb.AppendFormat ("{0,16}: {1}{2}", "Callback method", test.FailedUrlCallbackName, newline); 203 204 if (!String.IsNullOrEmpty (test.FailedUrlDescription)) 205 sb.AppendFormat ("{0,16}: {1}{2}", "URL description", test.FailedUrlDescription, newline); 206 207 if (!String.IsNullOrEmpty (test.FailureDetails)) 208 sb.AppendFormat ("{0,16}:{2}{1}{2}", "Failure details", FormatLines (" ", test.FailureDetails, newline, false), newline); 209 210 if (test.Exception != null) 211 sb.AppendFormat ("{0,16}:{2}{1}{2}", "Exception", FormatLines (" ", test.Exception.ToString (), newline, false), newline); 212 213 return sb.ToString (); 214 } 215 FormatLines(string indent, string text, string newline, bool skipFirst)216 static string FormatLines (string indent, string text, string newline, bool skipFirst) 217 { 218 var sb = new StringBuilder (); 219 bool first = true; 220 221 foreach (string s in text.Split (new string[] { newline }, StringSplitOptions.None)) { 222 if (skipFirst && first) 223 first = false; 224 else 225 sb.Append (indent); 226 sb.Append (s); 227 sb.Append (newline); 228 } 229 230 sb.Length -= newline.Length; 231 return sb.ToString (); 232 } 233 LoadTestsFromAssembly(Assembly asm, List <StandaloneTest> tests)234 static void LoadTestsFromAssembly (Assembly asm, List <StandaloneTest> tests) 235 { 236 Type[] types = asm.GetExportedTypes (); 237 if (types.Length == 0) 238 return; 239 240 object[] attributes; 241 foreach (var t in types) { 242 if (!t.IsClass || t.IsAbstract || t.IsGenericTypeDefinition) 243 continue; 244 245 attributes = t.GetCustomAttributes (typeof (TestCaseAttribute), false); 246 if (attributes.Length == 0) 247 continue; 248 249 if (!typeof (ITestCase).IsAssignableFrom (t)) 250 continue; 251 252 tests.Add (new StandaloneTest (t, attributes [0] as TestCaseAttribute)); 253 } 254 } 255 } 256 } 257