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

/branches/adinetz03/all/CBucks.Compiler.TestUtils/CompilerTestUtils.cs

#
C# | 576 lines | 296 code | 36 blank | 244 comment | 83 complexity | 55c454bd02823e9597908949e6421015 MD5 | raw file
Possible License(s): AGPL-3.0
  1. //#define DEB
  2. //#define FULDEB
  3. //#define PROGDEB
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Text;
  7. using System.Reflection;
  8. using System.Diagnostics;
  9. using System.IO;
  10. using System.Threading;
  11. namespace CBucks.Compiler.Tests {
  12. using MC = CBucks.MemberCache;
  13. using CBucks.Efi;
  14. /// <summary>
  15. /// Contains utility functions for testing the compiler
  16. /// </summary>
  17. public static class CompilerTestUtils {
  18. /// <summary>
  19. /// Writes the application configuration file
  20. /// </summary>
  21. /// <param name="filename"> The name of the configuration file to write </param>
  22. /// <param name="configName"> The name config file to write as a value </param>
  23. public static void writeAppConfigFile(string filename, string configName) {
  24. using(StreamWriter sw = new StreamWriter(filename)) {
  25. sw.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
  26. sw.WriteLine("<configuration>");
  27. sw.WriteLine("<appSettings>");
  28. sw.Write("<add key=\"runtimeConfigFile\" value=\"");
  29. sw.Write(configName);
  30. sw.WriteLine("\"/>");
  31. sw.WriteLine("</appSettings>");
  32. sw.WriteLine("</configuration>");
  33. } // end of using()
  34. } // end of writeAppConfigFile()
  35. /// <summary>
  36. /// Compiles the list of input files with the specified target
  37. /// </summary>
  38. /// <param name="useCbRuntime"> Whether or not to use C$ runtime
  39. /// (if used, it is first initialized) </param>
  40. /// <param name="outfile"> The name of the output file </param>
  41. /// <param name="target"> The compilation target - either "exe"
  42. /// or "dll" </param>
  43. /// <param name="filenames"> The list of files to compile </param>
  44. /// <returns> The compiler return code </returns>
  45. public static int compile(bool useCbRuntime, string outfile, string target,
  46. string[] filenames) {
  47. // build the command line
  48. StringBuilder cl = new StringBuilder();
  49. if(useCbRuntime)
  50. cl.Append("-cb ");
  51. #if DEB
  52. cl.Append("-wr ");
  53. #endif
  54. #if FULDEB
  55. cl.Append("-wd ");
  56. cl.Append("-ce ");
  57. #endif
  58. cl.Append("-o ").Append(outfile).Append(" ");
  59. cl.Append("-m gen ");
  60. cl.Append("-t " + target + " ");
  61. cl.Append("-s ");
  62. foreach(string name in filenames)
  63. cl.Append(name + " ");
  64. #if PROGDEB
  65. #else
  66. cl.AppendFormat("-spe ");
  67. #endif
  68. // start compilation and wait for it to finish
  69. ProcessStartInfo psi = new ProcessStartInfo("cbc.exe", cl.ToString());
  70. // for debug purposes
  71. #if DEB
  72. #else
  73. psi.WindowStyle = ProcessWindowStyle.Hidden;
  74. #endif
  75. Process p = Process.Start(psi);
  76. p.WaitForExit();
  77. // return exit code
  78. return p.ExitCode;
  79. } // end of compile()
  80. /// <summary>
  81. /// Compiles the list of input files with the specified target
  82. /// </summary>
  83. /// <param name="outfile"> The name of the output file </param>
  84. /// <param name="target"> The compilation target - either "exe"
  85. /// or "dll" </param>
  86. /// <param name="filenames"> The list of files to compile </param>
  87. /// <returns> The compiler return code </returns>
  88. public static int compile(string outfile, string target,
  89. string[] filenames) {
  90. return compile(false, outfile, target, filenames);
  91. } // end of compile()
  92. /// <summary>
  93. /// Compiles the list of input files and returns the error code
  94. /// </summary>
  95. /// <param name="outfile"> The output file </param>
  96. /// <param name="filenames"> The names of input files </param>
  97. /// <returns> The tokenizer return code </returns>
  98. public static int compile(string outfile, params string[] filenames) {
  99. return compile(outfile, "dll", filenames);
  100. } // end of parse()
  101. /// <summary>
  102. /// Compiles the program, runs it and checks whether its output
  103. /// matches desired output.
  104. /// </summary>
  105. /// <param name="inFiles"> The input files </param>
  106. /// <param name="output"> The desired output </param>
  107. /// <returns> True if the test succeeds and false otherwise </returns>
  108. public static bool testOutput(string[] inFiles, string[] output) {
  109. return testOutput(false, inFiles, output, "");
  110. } // end of testOutput()
  111. /// <summary>
  112. /// Compiles the program, runs it and checks whether its output
  113. /// matches desired output.
  114. /// </summary>
  115. /// <param name="useCbRuntime"> Whether to use C$ runtime
  116. /// during compilation and evaluation </param>
  117. /// <param name="inFiles"> The input files </param>
  118. /// <param name="output"> The desired output </param>
  119. /// <returns> True if the test succeeds and false otherwise </returns>
  120. public static bool testOutput(bool useCbRuntime, string[] inFiles,
  121. string[] output, string configName) {
  122. // generate a random file name
  123. string outname = Guid.NewGuid().ToString() + ".exe";
  124. // compile it
  125. int r = compile(useCbRuntime, outname, "exe", inFiles);
  126. if(r != 0) {
  127. return false;
  128. }
  129. if(useCbRuntime) {
  130. string appConfigFile = outname + ".config";
  131. writeAppConfigFile(appConfigFile, configName);
  132. } // end of if()
  133. return checkOutput(outname, output);
  134. } // end of testOutput()
  135. /// <summary>
  136. /// Tests the output. Accepts a single input file.
  137. /// </summary>
  138. /// /// <param name="useCbRuntime"> Whether to use C$ runtime
  139. /// during compilation and evaluation </param>
  140. /// <param name="inFile"> The input file to process </param>
  141. /// <param name="output"> The desired output </param>
  142. /// <returns> True if test succeeds and false if not </returns>
  143. public static bool testOutput(string inFile, params string[] output) {
  144. return testOutput(new string[] { inFile }, output);
  145. } // end of testOutput()
  146. /// <summary>
  147. /// Tests the output. Accepts a single input file.
  148. /// </summary>
  149. /// <param name="inFile"> The input file to process </param>
  150. /// <param name="output"> The desired output </param>
  151. /// <returns> True if test succeeds and false if not </returns>
  152. public static bool testOutput(bool useCbRuntime, string configName,
  153. string inFile, params string[] output) {
  154. return testOutput(useCbRuntime, new string[] { inFile }, output,
  155. configName);
  156. } // end of testOutput()
  157. /// <summary>
  158. /// Checks the output
  159. /// </summary>
  160. /// <param name="outname"> The name of the output file </param>
  161. /// <param name="output"> The output strings </param>
  162. /// <returns> True if the real output matches the desired one
  163. /// and false otherwise </returns>
  164. public static bool checkOutput(string outname, string[] output) {
  165. ProcessStartInfo psi = new ProcessStartInfo(outname);
  166. #if PROGDEB
  167. Process p = Process.Start(psi);
  168. Thread.Sleep(Timeout.Infinite);
  169. return false;
  170. #else
  171. psi.UseShellExecute = false;
  172. psi.WindowStyle = ProcessWindowStyle.Hidden;
  173. psi.RedirectStandardOutput = true;
  174. // WORKAROUND: mono requires standard output encoding to be set correctly
  175. // windows may require a different encoding
  176. psi.StandardOutputEncoding = Encoding.UTF8;
  177. Process p = Process.Start(psi);
  178. // get the standard output
  179. StreamReader sr = p.StandardOutput;
  180. // wait for the process to end
  181. p.WaitForExit();
  182. // read up to the end of file and check with the output
  183. int i = 0, n = output.Length;
  184. while(!sr.EndOfStream) {
  185. if(i == n) {
  186. return false;
  187. }
  188. string s = sr.ReadLine();
  189. if(s != output[i]) {
  190. //throw new ArgumentNullException("xxx", s + ":" + output[i]);
  191. return false;
  192. }
  193. i++;
  194. } // end of while()
  195. return i == n;
  196. #endif
  197. } // end of checkOutput()
  198. /// <summary>
  199. /// Tests the compilation for success
  200. /// </summary>
  201. /// <param name="inFiles"> The list of input files </param>
  202. /// <param name="typeNames"> The list of desired type names </param>
  203. /// <returns> Whether the test succeeded or failed </returns>
  204. /// <remarks> Does not uses C$ runtime and compiles into a ".dll"</remarks>
  205. public static bool testSuccess(string[] inFiles, params string[] typeNames) {
  206. return testSuccess(false, "dll", inFiles, typeNames);
  207. } // end of testSuccess()
  208. /// <summary>
  209. /// Tests the compilation for success
  210. /// </summary>
  211. /// <param name="useCbRuntime"> True if to compile to use with C$ runtime
  212. /// (-cb switch) and false if not. </param>
  213. /// <param name="outputName"> The type of the output file. Must be either
  214. /// "exe" or "dll". </param>
  215. /// <param name="inFiles"> The list of input files </param>
  216. /// <param name="typeNames"> The list of desired type names </param>
  217. /// <returns> Whether the test succeeded or failed </returns>
  218. public static bool testSuccess(bool useCbRuntime, string outputType,
  219. string[] inFiles, params string[] typeNames) {
  220. string name = compileSuccess(useCbRuntime, outputType, inFiles);
  221. if(null == name)
  222. return false;
  223. // check the resulting assembly
  224. return testTypes(name, typeNames);
  225. } // end of testSuccess()
  226. /// <summary>
  227. /// Tests the compilation for success. Returns the name of the compiled file in case of success
  228. /// and null if failed.
  229. /// </summary>
  230. /// <param name="useCbRuntime"> True if to compile to use with C$ runtime
  231. /// (-cb switch) and false if not. </param>
  232. /// <param name="outputName"> The type of the output file. Must be either
  233. /// "exe" or "dll". </param>
  234. /// <param name="inFiles"> The list of input files. </param>
  235. /// <returns> The name of the assembly in case of success and null in case of failure. </returns>
  236. private static string compileSuccess(bool useCbRuntime, string outputType,
  237. string[] inFiles) {
  238. // generate a random file name
  239. string name = Guid.NewGuid().ToString() + "." + outputType;
  240. // compile it
  241. int r = compile(useCbRuntime, name, outputType, inFiles);
  242. if(r != 0)
  243. return null;
  244. return name;
  245. } // end of compileSuccess()
  246. /// <summary>
  247. /// Loads the assembly.
  248. /// </summary>
  249. /// <param name="useCbRuntime"> Whether C$ runtime is used. </param>
  250. /// <param name="outputType"> The output type. Must be either "exe" or "dll". </param>
  251. /// <param name="inFiles"> The input file names. </param>
  252. /// <returns> The assembly loaded in case of success and null or exception
  253. /// if failed. </returns>
  254. public static Assembly testLoadAss(bool useCbRuntime, string outputType, params string[] inFiles) {
  255. string name = compileSuccess(useCbRuntime, outputType, inFiles);
  256. Assembly ass = Assembly.LoadFrom(name);
  257. return ass;
  258. } // end of testLoadAss()
  259. /// <summary>
  260. /// Tests compilation for success
  261. /// </summary>
  262. /// <param name="inFile"> The name of the input file </param>
  263. /// <param name="typeNames"> The names of the types </param>
  264. /// <returns> Whether the test succeeds or fails </returns>
  265. public static bool testSuccess(string inFile, params string[] typeNames) {
  266. return testSuccess(new string[] { inFile }, typeNames);
  267. } // end of testSuccess()
  268. /// <summary>
  269. /// Starts the type member testing
  270. /// </summary>
  271. /// <param name="inFile"> The input file for the testing </param>
  272. /// <param name="typeNames"> The names of the types being tested </param>
  273. /// <returns> The loaded assembly if the initial testing succeeds
  274. /// and null if it fails </returns>
  275. public static Assembly startTypeMemberTesting(string inFile,
  276. params string[] typeNames) {
  277. // generate a random file name
  278. string name = Guid.NewGuid().ToString() + ".dll";
  279. // compile it
  280. int r = compile(name, inFile);
  281. if(r != 0)
  282. return null;
  283. Assembly a = Assembly.LoadFrom(name);
  284. foreach(string typeName in typeNames)
  285. if(null == loadCbType(a, typeName))
  286. return null;
  287. return a;
  288. } // end of startTypeMemberTesting()
  289. /// <summary>
  290. /// Starts the type member testing
  291. /// </summary>
  292. /// <param name="inFiles"> The input files for the testing </param>
  293. /// <param name="typeNames"> The names of the types being tested </param>
  294. /// <returns> The loaded assembly if the initial testing succeeds
  295. /// and null if it fails </returns>
  296. public static Assembly startTypeMemberTesting(string[] inFiles,
  297. params string[] typeNames) {
  298. // generate a random file name
  299. string name = Guid.NewGuid().ToString() + ".dll";
  300. // compile it
  301. int r = compile(name, inFiles);
  302. if(r != 0)
  303. return null;
  304. Assembly a = Assembly.LoadFrom(name);
  305. foreach(string typeName in typeNames)
  306. if(null == loadCbType(a, typeName))
  307. return null;
  308. return a;
  309. } // end of startTypeMemberTesting()
  310. /// <summary>
  311. /// Tests whether the assembly has all of the specified list of types
  312. /// </summary>
  313. /// <param name="assName"> The name of the assembly (more precisely,
  314. /// the path to the output file) </param>
  315. /// <param name="typeNames"> The list of fully qualified
  316. /// type names </param>
  317. /// <returns> True if all the types loaded successfully and
  318. /// false otherwise </returns>
  319. public static bool testTypes(string assName,
  320. params string[] typeNames) {
  321. Assembly a = Assembly.ReflectionOnlyLoadFrom(assName);
  322. if(null == a)
  323. return false;
  324. foreach(string typeName in typeNames)
  325. if(null == loadCbType(a, typeName))
  326. return false;
  327. return true;
  328. } // end of testTypes()
  329. /// <summary>
  330. /// A simple check of type definitions - checks whether the members with
  331. /// the specified names are defined. Except for names, nothing is checked.
  332. /// </summary>
  333. /// <param name="type"> The type in which to check member names </param>
  334. /// <param name="memberNames"> The member names to check </param>
  335. /// <returns> True if the check succeeds and false otherwise </returns>
  336. public static bool testTypeMembers(Type type,
  337. params string[] memberNames) {
  338. foreach(string memberName in memberNames)
  339. if(!testTypeMember(type, memberName))
  340. return false;
  341. return true;
  342. } // end of testTypeMembers()
  343. /// <summary>
  344. /// Performs advanced testing of type members
  345. /// </summary>
  346. /// <param name="type"> The type to test </param>
  347. /// <param name="memberData"> The member data </param>
  348. /// <returns> True if the test succeeds and false if it fails </returns>
  349. public static bool testAdvancedTypeMembers(Type type,
  350. params object[][] memberData) {
  351. foreach(object[] member in memberData)
  352. if(!testAdvancedTypeMember(type, member))
  353. return false;
  354. return true;
  355. } // end of testAdvancedTypeMembers()
  356. /// <summary>
  357. /// Performs advanced testing of type members
  358. /// </summary>
  359. /// <param name="a"> The assembly from which to load the type </param>
  360. /// <param name="typeName"> The name of the type to load </param>
  361. /// <param name="memberData"> The member data to be tested </param>
  362. /// <returns> True if the test succeeds and false if it fails </returns>
  363. public static bool testAdvancedTypeMembers(Assembly a,
  364. string typeName, params object[][] memberData) {
  365. Type t = a.GetType(typeName);
  366. if(null == t)
  367. return false;
  368. return testAdvancedTypeMembers(t, memberData);
  369. } // end of testAdvancedTypeMembers()
  370. /// <summary>
  371. /// Checks whether the type has a member with the specified name
  372. /// </summary>
  373. /// <param name="type"> The type to check </param>
  374. /// <param name="memberName"> The name of the member to check </param>
  375. /// <returns> True if the member if found and false if not </returns>
  376. public static bool testTypeMember(Type type, string memberName) {
  377. BindingFlags bf = BindingFlags.NonPublic | BindingFlags.Static
  378. | BindingFlags.Public | BindingFlags.Instance |
  379. BindingFlags.DeclaredOnly;
  380. return type.GetMember(memberName, bf).Length != 0;
  381. } // end of testTypeMember()
  382. /// <summary>
  383. /// Performs and advanced type member check. Does not checks
  384. /// static/non-static, access level and purity.
  385. /// </summary>
  386. /// <param name="type"> The type whose member to check </param>
  387. /// <param name="memberData"> The array of objects which together
  388. /// describe member data. For a field, this is field name and type.
  389. /// For the method, it is the return type,
  390. /// the method name and a list of argument types (or null if there is
  391. /// no arguments). For the property, it is the type, name and types
  392. /// of accessors (get or set) </param>
  393. /// <returns> True if the member test succeeded and false otherwise </returns>
  394. public static bool testAdvancedTypeMember(Type type, object[] memberData) {
  395. BindingFlags bf = BindingFlags.NonPublic | BindingFlags.Static
  396. | BindingFlags.Public | BindingFlags.Instance |
  397. BindingFlags.DeclaredOnly;
  398. if(memberData[0] is Type) {
  399. Type rt = (Type)memberData[0];
  400. string name = (string)memberData[1];
  401. if(memberData.Length == 2) {
  402. // check the field
  403. return null != type.GetField(name, bf);
  404. } else if(memberData[2] is string) {
  405. // a property
  406. string gs = (string)memberData[2];
  407. PropertyInfo pi = type.GetProperty(name, bf);
  408. if(null == pi)
  409. return false;
  410. if(gs.IndexOf("get") != -1 && null == pi.GetGetMethod())
  411. return false;
  412. if(gs.IndexOf("set") != -1 && null == pi.GetSetMethod())
  413. return false;
  414. return true;
  415. } else {
  416. // define the type list
  417. Type[] types = Type.EmptyTypes;
  418. if(memberData[2] != null) {
  419. List<Type> l = new List<Type>();
  420. for(int i = 2; i < memberData.Length; i++)
  421. l.Add((Type)memberData[i]);
  422. types = l.ToArray();
  423. } // end of if()
  424. // a method or constructor
  425. if(name != ".ctor" && name != "new") {
  426. MethodInfo mi = type.GetMethod(name, bf, null, types, null);
  427. if(null == mi)
  428. return false;
  429. if(mi.ReturnType != rt)
  430. return false;
  431. return true;
  432. } else {
  433. // constructor's return type is ignored
  434. return null != type.GetConstructor(bf, null, types, null);
  435. } // end of if()-else
  436. } // end of if()
  437. } // end of if()
  438. // control shall not reach here
  439. throw new NotSupportedException();
  440. } // end of testAdvancedTypeMember()
  441. /// <summary>
  442. /// Loads a single C$ type
  443. /// </summary>
  444. /// <param name="a"> The assembly from which to load a type </param>
  445. /// <param name="typeName"> The name of the type to load </param>
  446. /// <returns> The type loaded or null if it is impossible </returns>
  447. public static Type loadCbType(Assembly a, string typeName) {
  448. Type t = a.GetType(typeName, false);
  449. if(null != t)
  450. return t;
  451. // try to load a type alias attribute
  452. foreach(CustomAttributeData cad in CustomAttributeData.GetCustomAttributes(a)) {
  453. if(null == cad)
  454. continue;
  455. //if(cad == null || cad.Constructor == null || cad.Constructor.DeclaringType == null)
  456. // throw new ArgumentNullException();
  457. if(cad.Constructor.DeclaringType.FullName != "CBucks.TypeAliasAttribute")
  458. continue;
  459. string name = (string)cad.ConstructorArguments[0].Value;
  460. string namespaceName = (string)cad.ConstructorArguments[1].Value;
  461. string fullTypeName = (string)cad.ConstructorArguments[2].Value;
  462. if(namespaceName + "." + name == typeName)
  463. return Type.GetType(fullTypeName, false);
  464. } // end of foreach()
  465. return null;
  466. } // end of loadCbType()
  467. /// <summary>
  468. /// Loads a member from an assembly as a C$ member function.
  469. /// </summary>
  470. /// <param name="t"> The type from which to load a member. </param>
  471. /// <param name="name"> The name of the member to load. </param>
  472. /// <param name="paramTypes"> The type parameters of the member. Empty array
  473. /// for methods without parameters. Null for properties and fields. </param>
  474. /// <returns> The method if loaded and null if none. </returns>
  475. /// <remarks> Does not load the EFI FDAG representation of the member. It is loaded
  476. /// later separately. </remarks>
  477. public static IPureFunction loadMember(Type t, string name, Type[] paramTypes) {
  478. BindingFlags fullFlags = BindingFlags.Instance | BindingFlags.Static |
  479. BindingFlags.NonPublic | BindingFlags.Public;
  480. if(null == paramTypes) {
  481. // a field or property
  482. MemberInfo[] mis = null;
  483. mis = t.GetMember(name, fullFlags);
  484. return reportOrCreateFunc(mis);
  485. } else if(name == constructorName) {
  486. ConstructorInfo ci = t.GetConstructor(paramTypes);
  487. if(null == ci)
  488. return null;
  489. return MC.instance.create(ci);
  490. } else {
  491. MethodInfo mi = t.GetMethod(name, paramTypes);
  492. if(null == mi)
  493. return null;
  494. return MC.instance.create(mi);
  495. } // end of if()
  496. } // end of loadMember()
  497. /// <summary>
  498. /// Tests whether the EFI is loaded correctly.
  499. /// </summary>
  500. /// <param name="member"> The member function on which to test the correct loading of EFI. </param>
  501. /// <param name="desiredEfi"> The desired EFI. </param>
  502. /// <returns> True in case of success and false or raises an exception in case of failure. </returns>
  503. private static bool testMemberEfi(IPureFunction member, string desiredEfi) {
  504. EfiFdag efi = MsilEfiLoader.load(member);
  505. string efistr = efi.ToString();
  506. return efistr == desiredEfi;
  507. } // end of testMemberEfi()
  508. /// <summary>
  509. /// Tests whether the EFI is loaded correctly.
  510. /// </summary>
  511. /// <param name="t"> The type from which to load a member. </param>
  512. /// <param name="member"> The member function on which to test the correct loading of EFI. </param>
  513. /// <param name="name"> The name of the member to load. </param>
  514. /// <param name="desiredEfi"> The desired EFI. </param>
  515. /// <param name="paramTypes"> The types of member parameters. Must be null for fields and
  516. /// properties. </param>
  517. /// <returns> True in case of success and false or raises an exception in case of failure. </returns>
  518. public static bool testMemberEfi(Type t, string name, string desiredEfi, Type[] paramTypes) {
  519. IPureFunction member = loadMember(t, name, paramTypes);
  520. if(null == member)
  521. return false;
  522. return testMemberEfi(member, desiredEfi);
  523. } // end of testMemberEfi()
  524. /// <summary>
  525. /// Creates a C$ member function if a single member array is passed and
  526. /// returns null if not.
  527. /// </summary>
  528. /// <param name="mis"> The member information. </param>
  529. /// <returns> The C$ function if created and null if non-existent or ambiguous. </returns>
  530. private static IPureFunction reportOrCreateFunc(MemberInfo[] mis) {
  531. if(mis.Length != 1)
  532. return null;
  533. return MC.instance.create(mis[0]);
  534. } // end of reportOrCreateFunc()
  535. /// <summary>
  536. /// The name of the constructor method
  537. /// </summary>
  538. private const string constructorName = "new";
  539. } // end of class CompilerTestUtils
  540. }