PageRenderTime 32ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/extras/functest/Analyze.cs

http://smokey.googlecode.com/
C# | 378 lines | 292 code | 57 blank | 29 comment | 76 complexity | 04b87e2937ee71730ad6a96d05bdf0ed MD5 | raw file
  1. // Copyright (C) 2007 Jesse Jones
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining
  4. // a copy of this software and associated documentation files (the
  5. // "Software"), to deal in the Software without restriction, including
  6. // without limitation the rights to use, copy, modify, merge, publish,
  7. // distribute, sublicense, and/or sell copies of the Software, and to
  8. // permit persons to whom the Software is furnished to do so, subject to
  9. // the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be
  12. // included in all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. using System;
  22. using System.Diagnostics;
  23. using System.Collections.Generic;
  24. using System.IO;
  25. using System.Reflection;
  26. using System.Text;
  27. using System.Xml;
  28. using System.Xml.Schema;
  29. namespace FuncTest
  30. {
  31. // This is the guy who does the real work of running the functional test.
  32. internal sealed class Analyze
  33. {
  34. public Analyze()
  35. {
  36. m_settings.ValidationEventHandler += DoValidationEvent;
  37. m_settings.ValidationType = ValidationType.Schema;
  38. m_settings.IgnoreComments = true;
  39. m_outFile = Path.Combine(Path.GetTempPath(), "ftest.results.xml");
  40. }
  41. // Returns false if the run wasn't as expected.
  42. public bool Run(string smokePath, string[] assemblies)
  43. {
  44. List<Expected> unexpectedErrors = new List<Expected>();
  45. bool append = false;
  46. foreach (string assembly in assemblies)
  47. {
  48. DoGetExpected(assembly);
  49. DoSmoke(smokePath, assembly, append);
  50. XmlDocument actual = DoGetActual();
  51. DoCheckResults(actual, unexpectedErrors);
  52. append = true;
  53. }
  54. bool good = DoReportErrors(unexpectedErrors);
  55. return good;
  56. }
  57. #region Internal types
  58. private struct Expected
  59. {
  60. public string checkID;
  61. public string fullName;
  62. public string typeName;
  63. public Expected(string checkID, string fullName, string typeName)
  64. {
  65. this.checkID = checkID;
  66. this.fullName = fullName;
  67. this.typeName = typeName;
  68. }
  69. public override bool Equals(object rhsObj)
  70. {
  71. if (rhsObj == null)
  72. return false;
  73. if (GetType() != rhsObj.GetType())
  74. return false;
  75. Expected rhs = (Expected) rhsObj;
  76. return this == rhs;
  77. }
  78. public bool Equals(Expected rhs)
  79. {
  80. return this == rhs;
  81. }
  82. public static bool operator==(Expected lhs, Expected rhs)
  83. {
  84. return lhs.checkID == rhs.checkID && lhs.fullName == rhs.fullName;
  85. }
  86. public static bool operator!=(Expected lhs, Expected rhs)
  87. {
  88. return !(lhs == rhs);
  89. }
  90. public override int GetHashCode()
  91. {
  92. int hash;
  93. unchecked
  94. {
  95. hash = checkID.GetHashCode() + fullName.GetHashCode();
  96. }
  97. return hash;
  98. }
  99. }
  100. #endregion
  101. #region Private methods
  102. private bool DoReportErrors(List<Expected> unexpectedErrors)
  103. {
  104. // didn't get an error we expected to see
  105. List<Expected> numMissingErrors = new List<Expected>();
  106. foreach (Expected error in m_expected)
  107. {
  108. if (m_actual.IndexOf(error) < 0)
  109. numMissingErrors.Add(error);
  110. }
  111. // got an error we didn't expect to see
  112. List<Expected> numUnexpectedErrors = new List<Expected>();
  113. foreach (Expected error in m_actual)
  114. {
  115. if (m_expected.IndexOf(error) < 0 && m_shouldPass.IndexOf(error) < 0 && m_shouldFail.IndexOf(error) < 0)
  116. numUnexpectedErrors.Add(error);
  117. }
  118. // error, but shouldFail
  119. List<Expected> numBrokenShouldFail = new List<Expected>();
  120. foreach (Expected error in m_shouldFail)
  121. {
  122. if (m_actual.IndexOf(error) >= 0)
  123. numBrokenShouldFail.Add(error);
  124. }
  125. // no error, but shouldPass
  126. List<Expected> numBrokenShouldPass = new List<Expected>();
  127. foreach (Expected error in m_shouldPass)
  128. {
  129. if (m_actual.IndexOf(error) < 0)
  130. numBrokenShouldPass.Add(error);
  131. }
  132. bool passes = numMissingErrors.Count == 0 && numUnexpectedErrors.Count == 0 && numBrokenShouldFail.Count == 0 && numBrokenShouldPass.Count == 0;
  133. if (passes)
  134. {
  135. if (m_shouldFail.Count + m_shouldPass.Count == 0)
  136. Console.WriteLine("Passed, found {0} errors and all were expected", m_actual.Count);
  137. else if (m_shouldFail.Count + m_shouldPass.Count == 1)
  138. Console.WriteLine("Passed, found {0} errors but missed one which is known to be broken", m_actual.Count);
  139. else
  140. Console.WriteLine("Passed, found {0} errors but missed {1} which are known to be broken", m_actual.Count, m_shouldFail.Count + m_shouldPass.Count);
  141. }
  142. else
  143. {
  144. Console.WriteLine("FAILED");
  145. if (numMissingErrors.Count > 0)
  146. {
  147. if (numMissingErrors.Count == 1)
  148. Console.WriteLine("Missed one error that was expected:");
  149. else
  150. Console.WriteLine("Missed {0} errors that were expected:", numMissingErrors.Count);
  151. Console.WriteLine(ToString(numMissingErrors));
  152. }
  153. if (numUnexpectedErrors.Count > 0)
  154. {
  155. if (numUnexpectedErrors.Count == 1)
  156. Console.WriteLine("Got one error that wasn't expected:");
  157. else
  158. Console.WriteLine("Got {0} errors that weren't expected:", numUnexpectedErrors.Count);
  159. Console.WriteLine(ToString(numUnexpectedErrors));
  160. }
  161. if (numBrokenShouldFail.Count > 0)
  162. {
  163. if (numBrokenShouldFail.Count == 1)
  164. Console.WriteLine("Got one error from a case that we thought was broken:");
  165. else
  166. Console.WriteLine("Got {0} errors from cases that we thought were broken:", numBrokenShouldFail.Count);
  167. Console.WriteLine(ToString(numBrokenShouldFail));
  168. }
  169. if (numBrokenShouldPass.Count > 0)
  170. {
  171. if (numBrokenShouldPass.Count == 1)
  172. Console.WriteLine("Missed one error from a case that we thought was broken:");
  173. else
  174. Console.WriteLine("Missed {0} errors from cases that we thought were broken:", numBrokenShouldPass.Count);
  175. Console.WriteLine(ToString(numBrokenShouldPass));
  176. }
  177. }
  178. return passes;
  179. }
  180. private string ToString(List<Expected> errors)
  181. {
  182. StringBuilder builder = new StringBuilder(80 * errors.Count);
  183. foreach (Expected error in errors)
  184. {
  185. builder.Append(string.Format(" {0}/{1} {2}", error.checkID, error.typeName, error.fullName));
  186. builder.Append(Environment.NewLine);
  187. }
  188. return builder.ToString();
  189. }
  190. private void DoCheckResults(XmlDocument actual, List<Expected> unexpectedErrors)
  191. {
  192. foreach (XmlNode child in actual.ChildNodes) // need this retarded loop because m_settings.IgnoreComments doesn't appear to do anything
  193. {
  194. if (child.Name == "Errors")
  195. {
  196. foreach (XmlNode grandchild in child.ChildNodes)
  197. {
  198. if (grandchild.Name == "Error")
  199. DoCheckActualError(grandchild, unexpectedErrors);
  200. }
  201. }
  202. }
  203. }
  204. private void DoCheckActualError(XmlNode error, List<Expected> unexpectedErrors)
  205. {
  206. string checkID = "", fullName = "", typeName = "";
  207. foreach (XmlNode child in error.ChildNodes)
  208. {
  209. if (child.Name == "Location")
  210. {
  211. fullName = child.Attributes["name"].Value;
  212. int i = fullName.IndexOf(' ');
  213. if (i >= 0)
  214. fullName = fullName.Substring(i + 1);
  215. }
  216. else if (child.Name == "Violation")
  217. {
  218. checkID = child.Attributes["checkID"].Value;
  219. typeName = child.Attributes["typeName"].Value;
  220. }
  221. }
  222. // Console.WriteLine("actual {0} {1}", checkID, fullName);
  223. Expected err = new Expected(checkID, fullName, typeName);
  224. m_actual.Add(err);
  225. }
  226. private XmlDocument DoGetActual()
  227. {
  228. Assembly assembly = Assembly.GetExecutingAssembly(); // TODO: this is a little retarded now that we support multiple assemblies
  229. Stream stream = assembly.GetManifestResourceStream("Smoke.schema.xml");
  230. XmlSchema schema = XmlSchema.Read(stream, DoValidationEvent);
  231. m_settings.Schemas.Add(schema);
  232. XmlReader reader = XmlReader.Create(m_outFile, m_settings);
  233. XmlDocument xml = new XmlDocument();
  234. xml.Load(reader);
  235. m_settings.Schemas.Remove(schema);
  236. return xml;
  237. }
  238. private void DoGetExpected(string evilPath)
  239. {
  240. Assembly assembly = Assembly.GetExecutingAssembly();
  241. Stream stream = assembly.GetManifestResourceStream("Expected.schema.xml");
  242. XmlSchema schema = XmlSchema.Read(stream, DoValidationEvent);
  243. m_settings.Schemas.Add(schema);
  244. assembly = Assembly.ReflectionOnlyLoadFrom(evilPath);
  245. string[] names = assembly.GetManifestResourceNames();
  246. if (names.Length != 1)
  247. throw new Exception(string.Format("{0} has {1} resources", evilPath, names.Length));
  248. stream = assembly.GetManifestResourceStream(names[0]);
  249. XmlReader reader = XmlReader.Create(stream, m_settings);
  250. XmlDocument xml = new XmlDocument();
  251. xml.Load(reader);
  252. m_settings.Schemas.Remove(schema);
  253. DoGetExpected(xml);
  254. }
  255. private void DoGetExpected(XmlDocument xml)
  256. {
  257. foreach (XmlNode child in xml.ChildNodes) // need this retarded loop because m_settings.IgnoreComments doesn't appear to do anything
  258. {
  259. if (child.Name == "Expectations")
  260. {
  261. foreach (XmlNode grandchild in child.ChildNodes)
  262. {
  263. if (grandchild.Name == "Expected")
  264. {
  265. string checkID = grandchild.Attributes["checkID"].Value;
  266. string fullName = grandchild.Attributes["fullName"].Value;
  267. string typeName = grandchild.Attributes["typeName"].Value;
  268. bool shouldPass = false, shouldFail = false;
  269. Expected e = new Expected(checkID, fullName, typeName);
  270. if (grandchild.Attributes["shouldPass"] != null)
  271. {
  272. string value = grandchild.Attributes["shouldPass"].Value;
  273. shouldPass = value == "true" || value == "1";
  274. }
  275. if (grandchild.Attributes["shouldFail"] != null)
  276. {
  277. string value = grandchild.Attributes["shouldFail"].Value;
  278. shouldFail = value == "true" || value == "1";
  279. }
  280. if (shouldPass)
  281. m_shouldPass.Add(e);
  282. else if (shouldFail)
  283. m_shouldFail.Add(e);
  284. else
  285. m_expected.Add(e);
  286. }
  287. }
  288. }
  289. }
  290. }
  291. private static void DoValidationEvent(object sender, ValidationEventArgs e)
  292. {
  293. if (e.Severity == XmlSeverityType.Warning)
  294. Console.WriteLine("{0}", e.Message);
  295. else
  296. throw e.Exception;
  297. }
  298. private void DoSmoke(string smokePath, string evilPath, bool append)
  299. {
  300. File.Delete(m_outFile); // make sure we're using the file we think we are
  301. string ap = append ? "-append" : string.Empty;
  302. string cmdLine = string.Format("--debug {0} -include-check:R1035 -exclude-check:C1030 -xml -out:{1} {2} {3}", smokePath, m_outFile, ap, evilPath);
  303. Process process = Process.Start("mono", cmdLine);
  304. if (!process.WaitForExit(60*1000))
  305. throw new ApplicationException("timed out waiting for " + smokePath);
  306. if (process.ExitCode < 0 || process.ExitCode > 1)
  307. throw new ApplicationException(smokePath + " failed with error " + process.ExitCode);
  308. }
  309. #endregion
  310. #region Fields
  311. private XmlReaderSettings m_settings = new XmlReaderSettings();
  312. private string m_outFile;
  313. private List<Expected> m_actual = new List<Expected>(); // errors which were reported
  314. private List<Expected> m_expected = new List<Expected>(); // errors we expect
  315. private List<Expected> m_shouldFail = new List<Expected>(); // no error, but we'd like to see one
  316. private List<Expected> m_shouldPass = new List<Expected>(); // error, but there shouldn't be
  317. #endregion
  318. }
  319. }
  320. // compare the xml with expected.xml
  321. // report results