PageRenderTime 116ms CodeModel.GetById 33ms RepoModel.GetById 2ms app.codeStats 0ms

/AccCheckConsole/Program.cs

#
C# | 499 lines | 422 code | 41 blank | 36 comment | 54 complexity | fb9c9ad6023d9847196cb9edf657fd1e MD5 | raw file
  1. // (c) Copyright Microsoft Corporation.
  2. // This source is subject to the Microsoft Permissive License.
  3. // See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
  4. // All other rights reserved.
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Text;
  8. using System.IO;
  9. using System.Diagnostics;
  10. using System.Reflection;
  11. using System.Threading;
  12. using System.Globalization;
  13. using System.Drawing;
  14. using AccCheck;
  15. using AccCheck.Logging;
  16. using AccCheck.Verification;
  17. namespace AccCheckConsole
  18. {
  19. static class Program
  20. {
  21. private static IntPtr s_hwnd;
  22. private static bool s_quiet;
  23. private static EventLevel s_loglevel = EventLevel.Information;
  24. private static List<String> s_enabledRoutines = new List<String>();
  25. private static List<String> s_disabledRoutines = new List<String>();
  26. private static bool s_list = false;
  27. private static bool s_showHelp = false;
  28. private static VerificationManager s_vm;
  29. // stores a list of filenames specified on the command line
  30. private static List<string> s_routineLibs = new List<string>();
  31. private static List<String> s_xmlLoggerFileNames = new List<String>();
  32. private static XMLSerializingLogger s_xmlLogger;
  33. static BaseLogger s_consoleLogger = null;
  34. static FailureCode s_failure = FailureCode.NoErrorsNoWarnings;
  35. static bool s_helpShown = false;
  36. /// <summary>Return error codes for application</summary>
  37. enum FailureCode
  38. {
  39. NoErrorsNoWarnings = 0,
  40. ShowUsage,
  41. ErrorsButNoWarnings,
  42. ErrorsAndWarnings,
  43. NoErrorsButWarnings,
  44. CommandLineArgument,
  45. }
  46. static void ParseArguments(string[] args)
  47. {
  48. s_xmlLogger = new XMLSerializingLogger();
  49. s_vm.AddLogger(s_xmlLogger);
  50. int i = 0;
  51. try
  52. {
  53. while (i < args.Length)
  54. {
  55. switch (args[i].ToLower())
  56. {
  57. case "-break":
  58. {
  59. Debug.Assert(false);
  60. break;
  61. }
  62. case "-?":
  63. case "-help":
  64. {
  65. s_showHelp = true;
  66. break;
  67. }
  68. case "-list":
  69. {
  70. s_list = true;
  71. break;
  72. }
  73. case "-log":
  74. if (i + 1 < args.Length)
  75. {
  76. // look at the next argument (is it what we expected?)
  77. switch (args[i + 1].ToLower())
  78. {
  79. case "trace":
  80. s_loglevel = EventLevel.Trace;
  81. break;
  82. case "info":
  83. case "information":
  84. s_loglevel = EventLevel.Information;
  85. break;
  86. case "warn":
  87. case "warning":
  88. s_loglevel = EventLevel.Warning;
  89. break;
  90. case "err":
  91. case "error":
  92. s_loglevel = EventLevel.Error;
  93. break;
  94. default:
  95. throw new ArgumentException("-log loglevel not valid. Should be either 'info', 'warn' or 'err'.");
  96. }
  97. i++;
  98. }
  99. else
  100. {
  101. throw new ArgumentException("-log not followed by log level.");
  102. }
  103. break;
  104. case "-logfile":
  105. if (i + 1 < args.Length)
  106. {
  107. String filename = args[i + 1];
  108. if (Path.GetExtension(filename).ToLower() == ".xml")
  109. {
  110. // since the XML logger needs to have it's SerializeToFile() method called
  111. // to output data, we need to store it away, so we can call that method
  112. // when we are done
  113. s_xmlLoggerFileNames.Add(Path.GetFullPath(filename));
  114. }
  115. else
  116. {
  117. // text file loggers output their data right away, so we only have to
  118. // add these to the loglist
  119. s_vm.AddLogger(new TextFileLogger(Path.GetFullPath(filename)));
  120. }
  121. i++;
  122. }
  123. else
  124. {
  125. throw new ArgumentException("-logfile needs a filename.");
  126. }
  127. break;
  128. case "-process":
  129. if (i + 1 < args.Length)
  130. {
  131. string exeName = Path.GetFileNameWithoutExtension(args[i + 1]);
  132. Process[] rgProcesses = Process.GetProcessesByName(exeName);
  133. if (rgProcesses.Length == 0)
  134. {
  135. throw new ArgumentException(string.Format("No such process to attach to named \"{0}\". Make sure the application is started before you run AccCheckConsole.exe", exeName));
  136. }
  137. else if (rgProcesses.Length > 1)
  138. {
  139. throw new ArgumentException(string.Format("Multiple processes called \"{0}\". Please close all but one of them.", exeName));
  140. }
  141. else
  142. {
  143. s_hwnd = rgProcesses[0].MainWindowHandle;
  144. i++;
  145. }
  146. }
  147. else
  148. {
  149. throw new ArgumentException("Command line argument \"-process\" needs a process name.");
  150. }
  151. break;
  152. case "-window":
  153. if (i + 1 < args.Length)
  154. {
  155. s_hwnd = Win32API.FindWindowEx(IntPtr.Zero, IntPtr.Zero, null, args[i + 1]);
  156. bool isValid = Win32API.IsWindow(s_hwnd);
  157. if (!isValid)
  158. {
  159. throw new ArgumentException(string.Format("There is no window with the caption \"{0}\" that was specified by the command line argument \"-window\"", args[i + 1]));
  160. // don't have to set hwnd = IntPtr.Zero, since the exception causes it not to be used
  161. }
  162. i++;
  163. }
  164. else
  165. {
  166. throw new ArgumentException("Command line argument \"-window\" needs a window title.");
  167. }
  168. break;
  169. case "-hwnd":
  170. if (i + 1 < args.Length)
  171. {
  172. int h;
  173. // remove "0x" from string, since int.TryParse doesn't allow it
  174. bool res;
  175. if (args[i + 1].StartsWith("0x"))
  176. {
  177. res = int.TryParse(args[i + 1].Replace("0x", ""), NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture.NumberFormat, out h);
  178. }
  179. else
  180. {
  181. res = int.TryParse(args[i + 1], out h);
  182. }
  183. if (res)
  184. {
  185. s_hwnd = new IntPtr(h);
  186. // validate hwnd
  187. bool isValid = Win32API.IsWindow(s_hwnd);
  188. if (!isValid)
  189. {
  190. throw new ArgumentException(string.Format("Command line argument \"-hwnd {0}\" did not point to an actual window", args[i + 1]));
  191. }
  192. // don't have to set hwnd = IntPtr.Zero, since the exception causes it not to be used
  193. i++;
  194. }
  195. else
  196. {
  197. throw new ArgumentException(string.Format("Command line argument \"-hwnd {0}\" was not a decimal or hexadecimal (0x00..).", args[i + 1]));
  198. }
  199. }
  200. else
  201. {
  202. throw new ArgumentException("Command line argument \"-hwnd\" needs an hwnd.");
  203. }
  204. break;
  205. case "-enable":
  206. if (i + 1 < args.Length)
  207. {
  208. s_enabledRoutines.Add(args[i + 1]);
  209. i++;
  210. }
  211. break;
  212. case "-disable":
  213. if (i + 1 < args.Length)
  214. {
  215. s_disabledRoutines.Add(args[i + 1]);
  216. i++;
  217. }
  218. break;
  219. case "-suppress":
  220. if (i + 1 < args.Length)
  221. {
  222. String xmlFile = args[i + 1];
  223. xmlFile = Path.GetFullPath(xmlFile);
  224. if (File.Exists(xmlFile))
  225. {
  226. s_vm.AddSuppressionFiles(xmlFile);
  227. }
  228. i++;
  229. }
  230. else
  231. {
  232. throw new ArgumentException("-suppress needs an XML file.");
  233. }
  234. break;
  235. case "-quiet":
  236. s_quiet = true;
  237. break;
  238. default:
  239. // interpret as filename
  240. if (File.Exists(args[i]))
  241. {
  242. s_routineLibs.Add(args[i]);
  243. }
  244. else
  245. {
  246. // or it might just be an error we didn't catch
  247. throw new ArgumentException("Unknown parameter '" + args[i] + "'! It's not a library.");
  248. }
  249. break;
  250. }
  251. i++;
  252. }
  253. LogToConsole(EventLevel.Information, "Command line argument", string.Join(" ", args));
  254. }
  255. catch (ArgumentException exception)
  256. {
  257. s_failure = FailureCode.CommandLineArgument;
  258. throw exception;
  259. }
  260. }
  261. static int Main(string[] args)
  262. {
  263. try
  264. {
  265. if (args.Length == 0)
  266. {
  267. LogHelpText(EventLevel.Information);
  268. }
  269. else
  270. {
  271. // Handle loading the libraries to add to the verification mananager
  272. s_vm = new VerificationManager();
  273. ParseArguments(args);
  274. //If there was a dll specified, clear current routines
  275. if(s_routineLibs.Count > 0)
  276. s_vm.ClearAllRoutines();
  277. //Adds specified dlls
  278. foreach (string filename in s_routineLibs)
  279. s_vm.AddVerificationDLL(filename);
  280. // Display the list of all routines in the loaded libraries
  281. if (s_list)
  282. {
  283. LogListOfRoutines(s_vm, EventLevel.Information);
  284. }
  285. // Display the help contents. If there is no hwnd, then display as an error
  286. if ((s_showHelp || s_hwnd.Equals(IntPtr.Zero)) && !s_list)
  287. {
  288. LogHelpText(s_hwnd == IntPtr.Zero ? EventLevel.Error : EventLevel.Information);
  289. }
  290. if (s_hwnd != IntPtr.Zero)
  291. {
  292. s_vm.Logger.LogLevel = s_loglevel;
  293. if (s_enabledRoutines.Count > 0)
  294. {
  295. foreach (string routine in s_enabledRoutines)
  296. {
  297. // If the routine is not found, then print list and exit out.
  298. if (s_vm.EnableRoutine(routine) != 1)
  299. {
  300. throw new ArgumentException(string.Format("Invalid routine \"{0}\", run AccCheckerConsole with command line \"-list\" to display a list of valid routines", routine));
  301. }
  302. }
  303. }
  304. else
  305. {
  306. s_vm.EnableVerifications(VerificationFilter.WithoutUI);
  307. if (s_disabledRoutines.Count > 0)
  308. {
  309. foreach (string routine in s_disabledRoutines)
  310. {
  311. // If the routine is not found, then print list and exit out.
  312. if (s_vm.DisableRoutine(routine) != 1)
  313. {
  314. throw new ArgumentException(string.Format("Invalid routine \"{0}\", run AccCheckerConsole with command line \"-list\" to display a list of valid routines", routine));
  315. }
  316. }
  317. }
  318. }
  319. // If you want to produce a log file for uploading to database, uncomment the following line.
  320. //s_vm.SetLabRun(true);
  321. // Execute the tests
  322. s_vm.ExecuteEnabled(s_hwnd);
  323. // output XML serialized data
  324. foreach (String filename in s_xmlLoggerFileNames)
  325. {
  326. LogFile logFile = new LogFile();
  327. logFile.ScreenShot = GraphicsHelper.GrabScreenShot(s_hwnd);
  328. s_xmlLogger.SerializeToFile(filename, logFile);
  329. }
  330. }
  331. }
  332. }
  333. catch (Exception exception)
  334. {
  335. LogToConsole(EventLevel.Error, "ERROR", exception.Message);
  336. }
  337. LogResults();
  338. return (int)GetResultsErrorCode();
  339. }
  340. /// <summary>Displays the results of the run to the console logger</summary>
  341. private static void LogResults()
  342. {
  343. if (!s_helpShown && !s_list)
  344. {
  345. s_vm.Logger.LogLevel = EventLevel.Information;
  346. StringBuilder sb = new StringBuilder();
  347. const int WIDTH = 40;
  348. sb.Append("\n".PadLeft(WIDTH - "Text: ".Length, '-'));
  349. sb.Append(string.Format("\tError count : {0}\n", s_consoleLogger.ErrorCount));
  350. sb.Append(string.Format("\tWarning count : {0}\n", s_consoleLogger.WarningCount));
  351. sb.Append(string.Format("\tInformational count : {0}\n", s_consoleLogger.InformationalCount));
  352. sb.Append(string.Format("\tTrace count : {0}\n", s_consoleLogger.TraceCount));
  353. sb.Append("\t".PadRight(WIDTH, '-'));
  354. LogToConsole(EventLevel.Information, "TEST RESULTS", sb.ToString());
  355. }
  356. }
  357. /// <summary>Logs the information to the console logger if -quiet is not specific on command line</summary>
  358. private static void LogToConsole(EventLevel eventLevel, string title, string message)
  359. {
  360. // May have dropped out of the parsing command and never started up the logger
  361. if (s_consoleLogger == null)
  362. {
  363. if (s_quiet)
  364. {
  365. // Just keep all the log entries in the logger
  366. s_consoleLogger = new AccumulatingLogger();
  367. }
  368. else
  369. {
  370. s_consoleLogger = new ConsoleLogger();
  371. }
  372. s_vm.AddLogger(s_consoleLogger);
  373. }
  374. LogEvent le = new LogEvent(eventLevel, title, message, "");
  375. s_consoleLogger.Log(le);
  376. }
  377. /// <summary>Determine the error code to return based on the run</summary>
  378. static FailureCode GetResultsErrorCode()
  379. {
  380. if (s_failure == FailureCode.NoErrorsNoWarnings)
  381. {
  382. if (s_consoleLogger.ErrorCount == 0)
  383. {
  384. if (s_consoleLogger.WarningCount == 0)
  385. {
  386. s_failure = FailureCode.NoErrorsNoWarnings;
  387. }
  388. else
  389. {
  390. s_failure = FailureCode.NoErrorsButWarnings;
  391. }
  392. }
  393. else if (s_consoleLogger.ErrorCount > 0)
  394. {
  395. if (s_consoleLogger.WarningCount == 0)
  396. {
  397. s_failure = FailureCode.ErrorsButNoWarnings;
  398. }
  399. else
  400. {
  401. s_failure = FailureCode.ErrorsAndWarnings;
  402. }
  403. }
  404. }
  405. return s_failure;
  406. }
  407. /// <summary>Log the help text to logger</summary>
  408. static void LogHelpText(EventLevel eventLevel)
  409. {
  410. StringBuilder sm = new StringBuilder();
  411. sm.Append("The syntax of this command is:\n");
  412. sm.Append("\n\tAccCheckConsole [options] (-hwnd <hwnd> | -process <name>) [<dlls>]\n");
  413. sm.Append("\n\tOptions:\n");
  414. sm.Append("\t\t-hwnd <hwnd> Validates the given hwnd. Can be hex or dec.\n");
  415. sm.Append("\t\t-window <title> Validates the window with the title given.\n");
  416. sm.Append("\t\t-process <name> Validates the main window of the process with that name.\n");
  417. sm.Append("\t\t-list Lists all the verification routines available.\n");
  418. sm.Append("\t\t-enable <name> Runs the given routine. Can be specified more than once\n");
  419. sm.Append("\t\t-disable <name> Runs all but the given routine. Can be specified more than once\n");
  420. sm.Append("\t\t-log (info|warn|err) The lowest event rating that will be logged.\n");
  421. sm.Append("\t\t-logfile <file> Outputs the log to file. Can be used multiple times.\n");
  422. sm.Append("\t\t-suppress <file> Uses the XML file <file> to suppress errors.\n");
  423. sm.Append("\t\t-quiet No logging to stdout.\n");
  424. sm.Append("\t\t-help Quick Help.\n");
  425. sm.Append("\n\tError codes returned from AccCheckConsole when using \"echo %errorlevel%\"\n");
  426. sm.Append("\t\t0 - No errors and no warnings.\n");
  427. sm.Append("\t\t1 - Usages statement was requested.\n");
  428. sm.Append("\t\t2 - Errors and no warnings.\n");
  429. sm.Append("\t\t3 - Errors and warnings.\n");
  430. sm.Append("\t\t4 - No errors but Warnings.\n");
  431. sm.Append("\t\t5 - Invalid command line.\n");
  432. sm.Append("\n");
  433. sm.Append("Examples:\n");
  434. sm.Append("\n");
  435. sm.Append("1) Run all verifications on a window with a specified name.\n");
  436. sm.Append("\tAccCheckConsole -window \"Untitled - Notepad\"\n");
  437. sm.Append("\n");
  438. sm.Append("2) Run a subset of the verifications against an HWND, specifying a suppression file.\n");
  439. sm.Append("\tAccCheckConsole -hwnd 0x00382f00 -enable CheckTabbing -enable CheckName -suppress suppress.xml\n");
  440. sm.Append("\n");
  441. sm.Append("3) Run all verifications from a new verification DLL.\n");
  442. sm.Append("\tAccCheckConsole -window \"Untitled - Notepad\" VerificationRoutine1.dll\n");
  443. Console.WriteLine(sm.ToString());
  444. s_helpShown = true;
  445. s_failure = FailureCode.ShowUsage;
  446. }
  447. /// <summary>Print the list of routines to the logger</summary>
  448. static void LogListOfRoutines(VerificationManager vm, EventLevel eventLevel)
  449. {
  450. StringBuilder sb = new StringBuilder("Available verification routines\n");
  451. foreach (VerificationRoutineWrapper rvr in vm.Routines)
  452. {
  453. sb.Append(string.Format("\t\t\"{0}\"\n", rvr.Title));
  454. }
  455. LogToConsole(eventLevel, "Verification Routines", sb.ToString());
  456. }
  457. }
  458. }