PageRenderTime 59ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/base/Windows/mkjig/mkjig.cs

https://bitbucket.org/MpDzik/singularity-rdk
C# | 395 lines | 327 code | 28 blank | 40 comment | 34 complexity | 35454a715c422d89c46543e864666dea MD5 | raw file
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Microsoft Research Singularity
  4. //
  5. // Copyright (c) Microsoft Corporation. All rights reserved.
  6. //
  7. // Examine assembly metadata to generate test jig code.
  8. //
  9. using System;
  10. using System.Collections;
  11. using System.Collections.Generic;
  12. using System.Diagnostics;
  13. using System.IO;
  14. using System.Runtime.InteropServices;
  15. using System.Text;
  16. using System.Xml;
  17. using Bartok.MSIL;
  18. internal class SuiteDesc
  19. {
  20. public readonly List<string> tests = new List<string>();
  21. public string init = null;
  22. public string cleanup = null;
  23. public string testInit = null;
  24. public string testCleanup = null;
  25. }
  26. internal class ModuleDesc
  27. {
  28. public readonly IDictionary<string, SuiteDesc> suites = new SortedDictionary<string, SuiteDesc>();
  29. public string init = null;
  30. public string cleanup = null;
  31. public SuiteDesc ProvideSuite(string className)
  32. {
  33. SuiteDesc res;
  34. if (!suites.TryGetValue(className, out res)) {
  35. res = new SuiteDesc();
  36. suites.Add(className, res);
  37. }
  38. return res;
  39. }
  40. public SuiteDesc ProvideSuite(MetaDataObject m, out string child)
  41. {
  42. string className;
  43. child = mktests.Tail(m.FullName, '.', out className);
  44. return ProvideSuite(className);
  45. }
  46. }
  47. public class mktests
  48. {
  49. const string ATTRIBUTE_PREFIX = "Microsoft.Singularity.UnitTest.";
  50. private string m_outpath;
  51. private ModuleDesc m_module;
  52. private IDictionary<string, MetaDataObject> m_suites = new SortedDictionary<string, MetaDataObject>();
  53. // TEMPLATE STRINGS DEFINE AT BOTTOM OF FILE
  54. private mktests(string path)
  55. {
  56. m_outpath = path;
  57. m_module = new ModuleDesc();
  58. }
  59. public static int Main(string[] args)
  60. {
  61. if (args.Length != 2) {
  62. Usage();
  63. return -1;
  64. }
  65. DateTime timeBegin = DateTime.Now;
  66. string outfile = args[0];
  67. string infile = args[1];
  68. ArrayList infiles = new ArrayList();
  69. infiles.Add(infile);
  70. mktests it = new mktests(outfile);
  71. it.ProcessAssembly(infiles);
  72. //FileStream oo = new FileStream(outfile, FileMode.Create, FileAccess.Write);
  73. // try {
  74. // }
  75. // finally {
  76. // oo.Close();
  77. // }
  78. TimeSpan elapsed = DateTime.Now - timeBegin;
  79. Console.WriteLine("mkjig: {0} seconds elapsed.", elapsed.TotalSeconds);
  80. return 0;
  81. }
  82. private static void Usage()
  83. {
  84. Console.WriteLine("Usage:\n mkjig <test_jig_source> <assembly> \n");
  85. }
  86. private void ProcessAssembly(ArrayList infiles)
  87. {
  88. MetaDataResolver resolver =
  89. new MetaDataResolver(infiles, new ArrayList(), new DateTime(), false, false);
  90. MetaDataResolver.ResolveCustomAttributes(new MetaDataResolver[] { resolver });
  91. foreach (MetaData md in resolver.MetaDataList) {
  92. // Assume that if we are processing an assembly, it will contain tests
  93. string name = md.Name;
  94. string prefix = name.Substring(0, name.LastIndexOf('.')); // trim extension
  95. string module = prefix.Substring(prefix.LastIndexOf('\\')+1); // extract filename
  96. ProcessAssembly(md);
  97. }
  98. }
  99. private void ProcessAssembly(MetaData md)
  100. {
  101. // Look for the annotation that tells us that this assembly is a stand-alone
  102. // test app.
  103. MetaDataAssembly mda = (MetaDataAssembly) md.Assemblies[0];
  104. foreach (MetaDataCustomAttribute attrib in md.CustomAttributes) {
  105. MetaDataObject parent = attrib.Parent;
  106. //Console.WriteLine("Found: {0} in {1} {2}", attrib.Name, parent.FullName, parent.FullNameWithContext);
  107. if (!attrib.Name.StartsWith(ATTRIBUTE_PREFIX)) {
  108. continue;
  109. }
  110. Console.WriteLine("Found: {0} in {1} {2}", attrib.Name, parent.FullName, parent.FullNameWithContext);
  111. string attribName = attrib.Name.Substring(ATTRIBUTE_PREFIX.Length);
  112. string className;
  113. string item = Tail(attrib.Parent, out className);
  114. if (attribName == "TestClassAttribute") {
  115. m_suites.Add(attrib.Parent.FullName, attrib);
  116. }
  117. else if (attribName == "TestMethodAttribute") {
  118. m_module.ProvideSuite(className).tests.Add(item);
  119. }
  120. else if (attribName == "ClassInitializeAttribute") {
  121. m_module.ProvideSuite(className).init = item;
  122. }
  123. else if (attribName == "ClassCleanupAttribute") {
  124. m_module.ProvideSuite(className).cleanup = item;
  125. }
  126. else if (attribName == "TestInitializeAttribute") {
  127. m_module.ProvideSuite(className).testInit = item;
  128. }
  129. else if (attribName == "TestCleanupAttribute") {
  130. m_module.ProvideSuite(className).testCleanup = item;
  131. }
  132. else if (attribName == "AssemblyInitializeAttribute") {
  133. m_module.init = item;
  134. }
  135. else if (attribName == "TestCleanupAttribute") {
  136. m_module.cleanup = item;
  137. }
  138. else {
  139. // IGNORE
  140. }
  141. }
  142. StringBuilder suiteStr = new StringBuilder();
  143. StringBuilder jigsStr = new StringBuilder();
  144. foreach (KeyValuePair<string, SuiteDesc> kvp in m_module.suites) {
  145. string fullname = kvp.Key;
  146. if (!m_suites.ContainsKey(fullname)) {
  147. Console.WriteLine("TestMethod declared outside of a TestClass: {0}", fullname);
  148. continue;
  149. }
  150. string pkg;
  151. string className = Tail(fullname, '.', out pkg);
  152. SuiteDesc desc = kvp.Value;
  153. GenTests(className, pkg, desc, suiteStr);
  154. jigsStr.AppendFormat(SUITE_CASE_TEMPLATE, className, pkg);
  155. }
  156. string modulename = "Foo";
  157. StringBuilder otherStr = new StringBuilder();
  158. //AppendOpt(MODULE_INIT_TEMPLATE, m_module.init, otherStr);
  159. //AppendOpt(MODULE_INIT_TEMPLATE, m_module.cleanup, otherStr);
  160. string content = string.Format(FILE_TEMPLATE, modulename, jigsStr, otherStr, suiteStr);
  161. File.WriteAllText(m_outpath, content);
  162. }
  163. private void GenTests(string className, string pkg, SuiteDesc desc, StringBuilder suiteStr)
  164. {
  165. StringBuilder caseStr = new StringBuilder();
  166. desc.tests.Sort();
  167. foreach (string t in desc.tests) {
  168. caseStr.AppendFormat(TEST_CASE_TEMPLATE, t);
  169. }
  170. StringBuilder otherStr = new StringBuilder();
  171. AppendOpt(INIT_TEMPLATE, desc.init, otherStr);
  172. AppendOpt(CLEANUP_TEMPLATE, desc.cleanup, otherStr);
  173. // TODO test init
  174. suiteStr.AppendFormat(SUITE_TEMPLATE, className, pkg, caseStr, otherStr);
  175. }
  176. private static void AppendOpt(string format, string optS, StringBuilder otherStr)
  177. {
  178. if (optS != null) {
  179. otherStr.AppendFormat(format, optS);
  180. }
  181. }
  182. // Split a string at the last occcurence of a character, returning both
  183. // the before and after. If the character is not present, then the tail
  184. // is empty and before is the entire string.
  185. public static string Tail(string it, char pattern, out string before)
  186. {
  187. int i = it.LastIndexOf(pattern);
  188. if (i < 0) {
  189. before = it;
  190. return "";
  191. }
  192. else {
  193. before = it.Substring(0, i);
  194. return it.Substring(i + 1);
  195. }
  196. }
  197. public static string Tail(MetaDataObject m, out string before)
  198. {
  199. return Tail(m.FullName, '.', out before);
  200. }
  201. //Assert(m != null, "TestMethod attribute is on a method");
  202. // oo.WriteStartElement("Suite");
  203. // oo.WriteAttributeString("Name", );
  204. // inSuite = true;
  205. // object timeout = psItem.Fields["Test Timeout"].Value;
  206. // if (timeout != null) {
  207. // oo.WriteAttributeString("Timeout", timeout.ToString());
  208. // }
  209. // object knownFailure = psItem.Fields["Test Known Failure"].Value;
  210. // if (knownFailure != null) {
  211. // oo.WriteAttributeString("KnownFailure", knownFailure.ToString());
  212. // }
  213. private static void Assert(bool cond, string trueText)
  214. {
  215. if (!cond) {
  216. throw new Exception("Expected: " + trueText);
  217. }
  218. }
  219. private const string TEST_INIT_TEMPLATE = @"
  220. override public void TestInitialize()
  221. {{
  222. m_test.{0}();
  223. }}
  224. ";
  225. private const string TEST_CLEANUP_TEMPLATE = @"
  226. override public void TestCleanup()
  227. {{
  228. m_test.{0}();
  229. }}
  230. ";
  231. private const string INIT_TEMPLATE = @"
  232. override public void Initialize()
  233. {{
  234. m_test.{0}();
  235. }}
  236. ";
  237. private const string CLEANUP_TEMPLATE = @"
  238. override public void Cleanup()
  239. {{
  240. m_test.{0}();
  241. }}
  242. ";
  243. private const string TEST_CASE_TEMPLATE = @"
  244. case ""{0}"":
  245. m_test.{0}();
  246. break;
  247. ";
  248. private const string SUITE_CASE_TEMPLATE = @"
  249. case ""{0}"":
  250. return new {1}.{0}_Jig(log);
  251. ";
  252. // suite name, suite namespace, suite cases, suite other
  253. private const string SUITE_TEMPLATE = @"
  254. namespace {1} {{
  255. internal class {0}_Jig : SuiteJig
  256. {{
  257. private {0}! m_test;
  258. public {0}_Jig(TestLog! log)
  259. {{
  260. {0} t = new {0}();
  261. t.SetLog(log);
  262. m_test = t;
  263. }}
  264. override public void DoTest(string! test)
  265. {{
  266. switch (test) {{
  267. {2}
  268. default:
  269. base.DoTest(test);
  270. break;
  271. }}
  272. }}
  273. {3}
  274. }}
  275. }}
  276. ";
  277. // module name, module cases, module other, suite jigs
  278. private const string FILE_TEMPLATE = @"
  279. ///////////////////////////////////////////////////////////////////////////////
  280. //
  281. // Copyright (c) Microsoft Corporation. All rights reserved.
  282. //
  283. // Generated test jig code
  284. using System;
  285. using System.Threading;
  286. using Microsoft.Singularity.UnitTest;
  287. using Microsoft.Singularity.Channels;
  288. using Microsoft.Contracts;
  289. using Microsoft.SingSharp.Reflection;
  290. using Microsoft.Singularity.Applications;
  291. using Microsoft.Singularity.Io;
  292. using Microsoft.Singularity.Configuration;
  293. using Microsoft.Singularity.Test.Contracts;
  294. using Microsoft.Singularity.Configuration;
  295. [assembly: Transform(typeof(ApplicationResourceTransform))]
  296. // GENERATED SUITE JIGS
  297. {3}
  298. namespace Microsoft.Singularity.Applications {{
  299. // GENERATED MODULE JIG
  300. public class {0}_ModuleJig : ModuleJig
  301. {{
  302. override public SuiteJig GetSuite(string! name, TestLog! log)
  303. {{
  304. switch (name) {{
  305. {1}
  306. default:
  307. return base.GetSuite(name, log);
  308. }}
  309. }}
  310. {2}
  311. }}
  312. [ConsoleCategory(HelpMessage=""ModuleTester"", Action=""test"")]
  313. internal class ModuleTest_Category {{
  314. [InputEndpoint(""data"")]
  315. public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
  316. [OutputEndpoint(""data"")]
  317. public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
  318. [CustomEndpoint]
  319. public readonly TRef<ModuleTesterContract.Exp:START> testerRef;
  320. reflective internal ModuleTest_Category();
  321. internal int AppMain() {{
  322. if (testerRef == null) {{
  323. DebugStub.WriteLine(""TEST endpoint not setup"");
  324. throw new Exception(""TEST endpoint not setup "");
  325. }}
  326. ModuleTesterContract.Exp tester = testerRef.Acquire();
  327. if (tester == null) {{
  328. DebugStub.WriteLine(""TEST unable to acquite handle to test driver"");
  329. throw new Exception(""Unable to acquire handle to the test driver"");
  330. }}
  331. ModuleJig jig = new {0}_ModuleJig();
  332. ModuleTester.RunTests(tester, jig);
  333. return 0;
  334. }}
  335. }}
  336. // Currently required to get process launch code generated.
  337. [ConsoleCategory(HelpMessage=""Run using the test framework"", DefaultAction=true)]
  338. internal class ModuleConsole_Category {{
  339. [InputEndpoint(""data"")]
  340. public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
  341. [OutputEndpoint(""data"")]
  342. public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
  343. reflective internal ModuleConsole_Category();
  344. internal int AppMain() {{
  345. Console.WriteLine(""This is a test application and can only be run from the tester."");
  346. return -1;
  347. }}
  348. }}
  349. }}
  350. ";
  351. }