/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
- //#define DEB
- //#define FULDEB
- //#define PROGDEB
-
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Reflection;
- using System.Diagnostics;
- using System.IO;
- using System.Threading;
-
- namespace CBucks.Compiler.Tests {
-
- using MC = CBucks.MemberCache;
- using CBucks.Efi;
-
- /// <summary>
- /// Contains utility functions for testing the compiler
- /// </summary>
- public static class CompilerTestUtils {
-
- /// <summary>
- /// Writes the application configuration file
- /// </summary>
- /// <param name="filename"> The name of the configuration file to write </param>
- /// <param name="configName"> The name config file to write as a value </param>
- public static void writeAppConfigFile(string filename, string configName) {
- using(StreamWriter sw = new StreamWriter(filename)) {
- sw.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
- sw.WriteLine("<configuration>");
- sw.WriteLine("<appSettings>");
- sw.Write("<add key=\"runtimeConfigFile\" value=\"");
- sw.Write(configName);
- sw.WriteLine("\"/>");
- sw.WriteLine("</appSettings>");
- sw.WriteLine("</configuration>");
- } // end of using()
- } // end of writeAppConfigFile()
-
- /// <summary>
- /// Compiles the list of input files with the specified target
- /// </summary>
- /// <param name="useCbRuntime"> Whether or not to use C$ runtime
- /// (if used, it is first initialized) </param>
- /// <param name="outfile"> The name of the output file </param>
- /// <param name="target"> The compilation target - either "exe"
- /// or "dll" </param>
- /// <param name="filenames"> The list of files to compile </param>
- /// <returns> The compiler return code </returns>
- public static int compile(bool useCbRuntime, string outfile, string target,
- string[] filenames) {
- // build the command line
- StringBuilder cl = new StringBuilder();
- if(useCbRuntime)
- cl.Append("-cb ");
- #if DEB
- cl.Append("-wr ");
- #endif
- #if FULDEB
- cl.Append("-wd ");
- cl.Append("-ce ");
- #endif
- cl.Append("-o ").Append(outfile).Append(" ");
- cl.Append("-m gen ");
- cl.Append("-t " + target + " ");
- cl.Append("-s ");
- foreach(string name in filenames)
- cl.Append(name + " ");
- #if PROGDEB
- #else
- cl.AppendFormat("-spe ");
- #endif
- // start compilation and wait for it to finish
- ProcessStartInfo psi = new ProcessStartInfo("cbc.exe", cl.ToString());
- // for debug purposes
- #if DEB
- #else
- psi.WindowStyle = ProcessWindowStyle.Hidden;
- #endif
- Process p = Process.Start(psi);
- p.WaitForExit();
-
- // return exit code
- return p.ExitCode;
- } // end of compile()
-
- /// <summary>
- /// Compiles the list of input files with the specified target
- /// </summary>
- /// <param name="outfile"> The name of the output file </param>
- /// <param name="target"> The compilation target - either "exe"
- /// or "dll" </param>
- /// <param name="filenames"> The list of files to compile </param>
- /// <returns> The compiler return code </returns>
- public static int compile(string outfile, string target,
- string[] filenames) {
- return compile(false, outfile, target, filenames);
- } // end of compile()
-
- /// <summary>
- /// Compiles the list of input files and returns the error code
- /// </summary>
- /// <param name="outfile"> The output file </param>
- /// <param name="filenames"> The names of input files </param>
- /// <returns> The tokenizer return code </returns>
- public static int compile(string outfile, params string[] filenames) {
- return compile(outfile, "dll", filenames);
- } // end of parse()
-
-
- /// <summary>
- /// Compiles the program, runs it and checks whether its output
- /// matches desired output.
- /// </summary>
- /// <param name="inFiles"> The input files </param>
- /// <param name="output"> The desired output </param>
- /// <returns> True if the test succeeds and false otherwise </returns>
- public static bool testOutput(string[] inFiles, string[] output) {
- return testOutput(false, inFiles, output, "");
- } // end of testOutput()
-
- /// <summary>
- /// Compiles the program, runs it and checks whether its output
- /// matches desired output.
- /// </summary>
- /// <param name="useCbRuntime"> Whether to use C$ runtime
- /// during compilation and evaluation </param>
- /// <param name="inFiles"> The input files </param>
- /// <param name="output"> The desired output </param>
- /// <returns> True if the test succeeds and false otherwise </returns>
- public static bool testOutput(bool useCbRuntime, string[] inFiles,
- string[] output, string configName) {
- // generate a random file name
- string outname = Guid.NewGuid().ToString() + ".exe";
- // compile it
- int r = compile(useCbRuntime, outname, "exe", inFiles);
- if(r != 0) {
- return false;
- }
- if(useCbRuntime) {
- string appConfigFile = outname + ".config";
- writeAppConfigFile(appConfigFile, configName);
- } // end of if()
- return checkOutput(outname, output);
- } // end of testOutput()
-
- /// <summary>
- /// Tests the output. Accepts a single input file.
- /// </summary>
- /// /// <param name="useCbRuntime"> Whether to use C$ runtime
- /// during compilation and evaluation </param>
- /// <param name="inFile"> The input file to process </param>
- /// <param name="output"> The desired output </param>
- /// <returns> True if test succeeds and false if not </returns>
- public static bool testOutput(string inFile, params string[] output) {
- return testOutput(new string[] { inFile }, output);
- } // end of testOutput()
-
- /// <summary>
- /// Tests the output. Accepts a single input file.
- /// </summary>
- /// <param name="inFile"> The input file to process </param>
- /// <param name="output"> The desired output </param>
- /// <returns> True if test succeeds and false if not </returns>
- public static bool testOutput(bool useCbRuntime, string configName,
- string inFile, params string[] output) {
- return testOutput(useCbRuntime, new string[] { inFile }, output,
- configName);
- } // end of testOutput()
-
- /// <summary>
- /// Checks the output
- /// </summary>
- /// <param name="outname"> The name of the output file </param>
- /// <param name="output"> The output strings </param>
- /// <returns> True if the real output matches the desired one
- /// and false otherwise </returns>
- public static bool checkOutput(string outname, string[] output) {
- ProcessStartInfo psi = new ProcessStartInfo(outname);
- #if PROGDEB
- Process p = Process.Start(psi);
- Thread.Sleep(Timeout.Infinite);
- return false;
- #else
- psi.UseShellExecute = false;
- psi.WindowStyle = ProcessWindowStyle.Hidden;
- psi.RedirectStandardOutput = true;
- // WORKAROUND: mono requires standard output encoding to be set correctly
- // windows may require a different encoding
- psi.StandardOutputEncoding = Encoding.UTF8;
- Process p = Process.Start(psi);
- // get the standard output
- StreamReader sr = p.StandardOutput;
- // wait for the process to end
- p.WaitForExit();
- // read up to the end of file and check with the output
- int i = 0, n = output.Length;
- while(!sr.EndOfStream) {
- if(i == n) {
- return false;
- }
- string s = sr.ReadLine();
- if(s != output[i]) {
- //throw new ArgumentNullException("xxx", s + ":" + output[i]);
- return false;
- }
- i++;
- } // end of while()
- return i == n;
- #endif
- } // end of checkOutput()
-
- /// <summary>
- /// Tests the compilation for success
- /// </summary>
- /// <param name="inFiles"> The list of input files </param>
- /// <param name="typeNames"> The list of desired type names </param>
- /// <returns> Whether the test succeeded or failed </returns>
- /// <remarks> Does not uses C$ runtime and compiles into a ".dll"</remarks>
- public static bool testSuccess(string[] inFiles, params string[] typeNames) {
- return testSuccess(false, "dll", inFiles, typeNames);
- } // end of testSuccess()
-
- /// <summary>
- /// Tests the compilation for success
- /// </summary>
- /// <param name="useCbRuntime"> True if to compile to use with C$ runtime
- /// (-cb switch) and false if not. </param>
- /// <param name="outputName"> The type of the output file. Must be either
- /// "exe" or "dll". </param>
- /// <param name="inFiles"> The list of input files </param>
- /// <param name="typeNames"> The list of desired type names </param>
- /// <returns> Whether the test succeeded or failed </returns>
- public static bool testSuccess(bool useCbRuntime, string outputType,
- string[] inFiles, params string[] typeNames) {
- string name = compileSuccess(useCbRuntime, outputType, inFiles);
- if(null == name)
- return false;
- // check the resulting assembly
- return testTypes(name, typeNames);
- } // end of testSuccess()
-
- /// <summary>
- /// Tests the compilation for success. Returns the name of the compiled file in case of success
- /// and null if failed.
- /// </summary>
- /// <param name="useCbRuntime"> True if to compile to use with C$ runtime
- /// (-cb switch) and false if not. </param>
- /// <param name="outputName"> The type of the output file. Must be either
- /// "exe" or "dll". </param>
- /// <param name="inFiles"> The list of input files. </param>
- /// <returns> The name of the assembly in case of success and null in case of failure. </returns>
- private static string compileSuccess(bool useCbRuntime, string outputType,
- string[] inFiles) {
- // generate a random file name
- string name = Guid.NewGuid().ToString() + "." + outputType;
- // compile it
- int r = compile(useCbRuntime, name, outputType, inFiles);
- if(r != 0)
- return null;
- return name;
- } // end of compileSuccess()
-
- /// <summary>
- /// Loads the assembly.
- /// </summary>
- /// <param name="useCbRuntime"> Whether C$ runtime is used. </param>
- /// <param name="outputType"> The output type. Must be either "exe" or "dll". </param>
- /// <param name="inFiles"> The input file names. </param>
- /// <returns> The assembly loaded in case of success and null or exception
- /// if failed. </returns>
- public static Assembly testLoadAss(bool useCbRuntime, string outputType, params string[] inFiles) {
- string name = compileSuccess(useCbRuntime, outputType, inFiles);
- Assembly ass = Assembly.LoadFrom(name);
- return ass;
- } // end of testLoadAss()
-
- /// <summary>
- /// Tests compilation for success
- /// </summary>
- /// <param name="inFile"> The name of the input file </param>
- /// <param name="typeNames"> The names of the types </param>
- /// <returns> Whether the test succeeds or fails </returns>
- public static bool testSuccess(string inFile, params string[] typeNames) {
- return testSuccess(new string[] { inFile }, typeNames);
- } // end of testSuccess()
-
- /// <summary>
- /// Starts the type member testing
- /// </summary>
- /// <param name="inFile"> The input file for the testing </param>
- /// <param name="typeNames"> The names of the types being tested </param>
- /// <returns> The loaded assembly if the initial testing succeeds
- /// and null if it fails </returns>
- public static Assembly startTypeMemberTesting(string inFile,
- params string[] typeNames) {
- // generate a random file name
- string name = Guid.NewGuid().ToString() + ".dll";
- // compile it
- int r = compile(name, inFile);
- if(r != 0)
- return null;
- Assembly a = Assembly.LoadFrom(name);
- foreach(string typeName in typeNames)
- if(null == loadCbType(a, typeName))
- return null;
- return a;
- } // end of startTypeMemberTesting()
-
- /// <summary>
- /// Starts the type member testing
- /// </summary>
- /// <param name="inFiles"> The input files for the testing </param>
- /// <param name="typeNames"> The names of the types being tested </param>
- /// <returns> The loaded assembly if the initial testing succeeds
- /// and null if it fails </returns>
- public static Assembly startTypeMemberTesting(string[] inFiles,
- params string[] typeNames) {
- // generate a random file name
- string name = Guid.NewGuid().ToString() + ".dll";
- // compile it
- int r = compile(name, inFiles);
- if(r != 0)
- return null;
- Assembly a = Assembly.LoadFrom(name);
- foreach(string typeName in typeNames)
- if(null == loadCbType(a, typeName))
- return null;
- return a;
- } // end of startTypeMemberTesting()
-
- /// <summary>
- /// Tests whether the assembly has all of the specified list of types
- /// </summary>
- /// <param name="assName"> The name of the assembly (more precisely,
- /// the path to the output file) </param>
- /// <param name="typeNames"> The list of fully qualified
- /// type names </param>
- /// <returns> True if all the types loaded successfully and
- /// false otherwise </returns>
- public static bool testTypes(string assName,
- params string[] typeNames) {
- Assembly a = Assembly.ReflectionOnlyLoadFrom(assName);
- if(null == a)
- return false;
- foreach(string typeName in typeNames)
- if(null == loadCbType(a, typeName))
- return false;
- return true;
- } // end of testTypes()
-
- /// <summary>
- /// A simple check of type definitions - checks whether the members with
- /// the specified names are defined. Except for names, nothing is checked.
- /// </summary>
- /// <param name="type"> The type in which to check member names </param>
- /// <param name="memberNames"> The member names to check </param>
- /// <returns> True if the check succeeds and false otherwise </returns>
- public static bool testTypeMembers(Type type,
- params string[] memberNames) {
- foreach(string memberName in memberNames)
- if(!testTypeMember(type, memberName))
- return false;
- return true;
- } // end of testTypeMembers()
-
- /// <summary>
- /// Performs advanced testing of type members
- /// </summary>
- /// <param name="type"> The type to test </param>
- /// <param name="memberData"> The member data </param>
- /// <returns> True if the test succeeds and false if it fails </returns>
- public static bool testAdvancedTypeMembers(Type type,
- params object[][] memberData) {
- foreach(object[] member in memberData)
- if(!testAdvancedTypeMember(type, member))
- return false;
- return true;
- } // end of testAdvancedTypeMembers()
-
- /// <summary>
- /// Performs advanced testing of type members
- /// </summary>
- /// <param name="a"> The assembly from which to load the type </param>
- /// <param name="typeName"> The name of the type to load </param>
- /// <param name="memberData"> The member data to be tested </param>
- /// <returns> True if the test succeeds and false if it fails </returns>
- public static bool testAdvancedTypeMembers(Assembly a,
- string typeName, params object[][] memberData) {
- Type t = a.GetType(typeName);
- if(null == t)
- return false;
- return testAdvancedTypeMembers(t, memberData);
- } // end of testAdvancedTypeMembers()
-
- /// <summary>
- /// Checks whether the type has a member with the specified name
- /// </summary>
- /// <param name="type"> The type to check </param>
- /// <param name="memberName"> The name of the member to check </param>
- /// <returns> True if the member if found and false if not </returns>
- public static bool testTypeMember(Type type, string memberName) {
- BindingFlags bf = BindingFlags.NonPublic | BindingFlags.Static
- | BindingFlags.Public | BindingFlags.Instance |
- BindingFlags.DeclaredOnly;
- return type.GetMember(memberName, bf).Length != 0;
- } // end of testTypeMember()
-
- /// <summary>
- /// Performs and advanced type member check. Does not checks
- /// static/non-static, access level and purity.
- /// </summary>
- /// <param name="type"> The type whose member to check </param>
- /// <param name="memberData"> The array of objects which together
- /// describe member data. For a field, this is field name and type.
- /// For the method, it is the return type,
- /// the method name and a list of argument types (or null if there is
- /// no arguments). For the property, it is the type, name and types
- /// of accessors (get or set) </param>
- /// <returns> True if the member test succeeded and false otherwise </returns>
- public static bool testAdvancedTypeMember(Type type, object[] memberData) {
- BindingFlags bf = BindingFlags.NonPublic | BindingFlags.Static
- | BindingFlags.Public | BindingFlags.Instance |
- BindingFlags.DeclaredOnly;
- if(memberData[0] is Type) {
- Type rt = (Type)memberData[0];
- string name = (string)memberData[1];
- if(memberData.Length == 2) {
- // check the field
- return null != type.GetField(name, bf);
- } else if(memberData[2] is string) {
- // a property
- string gs = (string)memberData[2];
- PropertyInfo pi = type.GetProperty(name, bf);
- if(null == pi)
- return false;
- if(gs.IndexOf("get") != -1 && null == pi.GetGetMethod())
- return false;
- if(gs.IndexOf("set") != -1 && null == pi.GetSetMethod())
- return false;
- return true;
- } else {
- // define the type list
- Type[] types = Type.EmptyTypes;
- if(memberData[2] != null) {
- List<Type> l = new List<Type>();
- for(int i = 2; i < memberData.Length; i++)
- l.Add((Type)memberData[i]);
- types = l.ToArray();
- } // end of if()
- // a method or constructor
- if(name != ".ctor" && name != "new") {
- MethodInfo mi = type.GetMethod(name, bf, null, types, null);
- if(null == mi)
- return false;
- if(mi.ReturnType != rt)
- return false;
- return true;
- } else {
- // constructor's return type is ignored
- return null != type.GetConstructor(bf, null, types, null);
- } // end of if()-else
- } // end of if()
- } // end of if()
- // control shall not reach here
- throw new NotSupportedException();
- } // end of testAdvancedTypeMember()
-
- /// <summary>
- /// Loads a single C$ type
- /// </summary>
- /// <param name="a"> The assembly from which to load a type </param>
- /// <param name="typeName"> The name of the type to load </param>
- /// <returns> The type loaded or null if it is impossible </returns>
- public static Type loadCbType(Assembly a, string typeName) {
- Type t = a.GetType(typeName, false);
- if(null != t)
- return t;
- // try to load a type alias attribute
- foreach(CustomAttributeData cad in CustomAttributeData.GetCustomAttributes(a)) {
- if(null == cad)
- continue;
- //if(cad == null || cad.Constructor == null || cad.Constructor.DeclaringType == null)
- // throw new ArgumentNullException();
- if(cad.Constructor.DeclaringType.FullName != "CBucks.TypeAliasAttribute")
- continue;
- string name = (string)cad.ConstructorArguments[0].Value;
- string namespaceName = (string)cad.ConstructorArguments[1].Value;
- string fullTypeName = (string)cad.ConstructorArguments[2].Value;
- if(namespaceName + "." + name == typeName)
- return Type.GetType(fullTypeName, false);
- } // end of foreach()
- return null;
- } // end of loadCbType()
-
- /// <summary>
- /// Loads a member from an assembly as a C$ member function.
- /// </summary>
- /// <param name="t"> The type from which to load a member. </param>
- /// <param name="name"> The name of the member to load. </param>
- /// <param name="paramTypes"> The type parameters of the member. Empty array
- /// for methods without parameters. Null for properties and fields. </param>
- /// <returns> The method if loaded and null if none. </returns>
- /// <remarks> Does not load the EFI FDAG representation of the member. It is loaded
- /// later separately. </remarks>
- public static IPureFunction loadMember(Type t, string name, Type[] paramTypes) {
- BindingFlags fullFlags = BindingFlags.Instance | BindingFlags.Static |
- BindingFlags.NonPublic | BindingFlags.Public;
- if(null == paramTypes) {
- // a field or property
- MemberInfo[] mis = null;
- mis = t.GetMember(name, fullFlags);
- return reportOrCreateFunc(mis);
- } else if(name == constructorName) {
- ConstructorInfo ci = t.GetConstructor(paramTypes);
- if(null == ci)
- return null;
- return MC.instance.create(ci);
- } else {
- MethodInfo mi = t.GetMethod(name, paramTypes);
- if(null == mi)
- return null;
- return MC.instance.create(mi);
- } // end of if()
- } // end of loadMember()
-
- /// <summary>
- /// Tests whether the EFI is loaded correctly.
- /// </summary>
- /// <param name="member"> The member function on which to test the correct loading of EFI. </param>
- /// <param name="desiredEfi"> The desired EFI. </param>
- /// <returns> True in case of success and false or raises an exception in case of failure. </returns>
- private static bool testMemberEfi(IPureFunction member, string desiredEfi) {
- EfiFdag efi = MsilEfiLoader.load(member);
- string efistr = efi.ToString();
- return efistr == desiredEfi;
- } // end of testMemberEfi()
-
- /// <summary>
- /// Tests whether the EFI is loaded correctly.
- /// </summary>
- /// <param name="t"> The type from which to load a member. </param>
- /// <param name="member"> The member function on which to test the correct loading of EFI. </param>
- /// <param name="name"> The name of the member to load. </param>
- /// <param name="desiredEfi"> The desired EFI. </param>
- /// <param name="paramTypes"> The types of member parameters. Must be null for fields and
- /// properties. </param>
- /// <returns> True in case of success and false or raises an exception in case of failure. </returns>
- public static bool testMemberEfi(Type t, string name, string desiredEfi, Type[] paramTypes) {
- IPureFunction member = loadMember(t, name, paramTypes);
- if(null == member)
- return false;
- return testMemberEfi(member, desiredEfi);
- } // end of testMemberEfi()
-
- /// <summary>
- /// Creates a C$ member function if a single member array is passed and
- /// returns null if not.
- /// </summary>
- /// <param name="mis"> The member information. </param>
- /// <returns> The C$ function if created and null if non-existent or ambiguous. </returns>
- private static IPureFunction reportOrCreateFunc(MemberInfo[] mis) {
- if(mis.Length != 1)
- return null;
- return MC.instance.create(mis[0]);
- } // end of reportOrCreateFunc()
-
- /// <summary>
- /// The name of the constructor method
- /// </summary>
- private const string constructorName = "new";
-
- } // end of class CompilerTestUtils
-
- }