|
using System; |
|
using System.IO; |
|
using System.Linq; |
|
using System.Reflection; |
|
using System.Reflection.Emit; |
|
|
|
namespace XunitAutoTest |
|
{ |
|
public class Program |
|
{ |
|
static string GetTestName(string filePath) |
|
{ |
|
var fileName = Path.GetFileNameWithoutExtension(filePath); |
|
|
|
if (fileName.Length == 0) |
|
fileName = "_"; |
|
else |
|
fileName = string.Concat(fileName.Select((char c) => { |
|
if (char.IsLetterOrDigit(c)) |
|
return c; |
|
else |
|
return '_'; |
|
})); |
|
|
|
if (char.IsDigit(fileName.First())) |
|
fileName = "_" + fileName; |
|
|
|
return fileName; |
|
} |
|
|
|
static CustomAttributeBuilder BuildFactAttribute(string displayName) |
|
{ |
|
var factType = typeof(Xunit.FactAttribute); |
|
var factCtor = typeof(Xunit.FactAttribute).GetConstructor(Type.EmptyTypes); |
|
|
|
var attrBuilder = new CustomAttributeBuilder( |
|
factCtor, |
|
new object[] { }, |
|
new[] { factType.GetProperty("DisplayName") }, |
|
new[] { displayName }); |
|
|
|
return attrBuilder; |
|
} |
|
|
|
static void Main(string[] args) |
|
{ |
|
Console.WriteLine("Preparing generation of the dynamic assembly..."); |
|
|
|
// Create a dynamic assembly to save the generated types to: |
|
AssemblyName aName = new AssemblyName("XunitAutoTest.Generated.Dynamic"); |
|
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave); |
|
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(aName.Name, aName.Name + ".dll"); |
|
|
|
// The base class of the generated class. |
|
var baseType = typeof(Tests.AutoPopulatedTest); |
|
var baseMeth = baseType.GetMethod("RunTest", new[] { typeof(string) }); |
|
|
|
// The TypeBilder for our generated test class |
|
var typeBuilder = moduleBuilder.DefineType( |
|
"Json.GeneratedTests.TestDataTests", // * The name of the generated class, including the namespace * |
|
TypeAttributes.Public | TypeAttributes.Class, |
|
baseType |
|
); |
|
|
|
// For each [EnumerateFiles(path, searchPattern)] attribute: |
|
foreach (var attr in baseType.GetCustomAttributes<EnumerateFilesFixtureAttribute>()) |
|
{ |
|
// Enumerate the corresponding file(-names): |
|
foreach (var fileName in Directory.EnumerateFiles(attr.Path, attr.SearchPattern)) |
|
{ |
|
// And generate a test method for each of them: |
|
var testBuilder = typeBuilder.DefineMethod(GetTestName(fileName), MethodAttributes.Public); |
|
var ilGenerator = testBuilder.GetILGenerator(); |
|
|
|
// Our "test method" just calls AutoPopulatedTest.RunTest("<value of fileName>"). |
|
// ex.: |
|
// AutoPopulatedTest.RunTest("C:\(...)\test-data\test_one.json"); |
|
// The value of fileName is stored as a literal value in the generated assembly file. |
|
ilGenerator.Emit(OpCodes.Ldarg_0); // Load the "this" reference |
|
ilGenerator.Emit(OpCodes.Ldstr, fileName); // and the filename string on the stack, |
|
ilGenerator.Emit(OpCodes.Callvirt, baseMeth); // and pass them to AutoPopulatedTest.RunTest(string fileName). |
|
|
|
ilGenerator.Emit(OpCodes.Ret); |
|
// End of test method implementation. |
|
|
|
// Add a [Fact] attribute to the method. |
|
// 'displayName' is the string which will be shown in the Xunit GUI Runner. |
|
var displayName = "JSON data test: " + fileName; |
|
var factAttr = BuildFactAttribute(displayName); |
|
testBuilder.SetCustomAttribute(factAttr); |
|
} |
|
} |
|
|
|
// Finally, create our dynamically generated type: |
|
typeBuilder.CreateType(); |
|
|
|
Console.WriteLine("Generation finished."); |
|
Console.WriteLine("Preparing writing the assembly to the disk."); |
|
|
|
assemblyBuilder.Save(aName.Name + ".dll"); |
|
|
|
Console.WriteLine("The generated module has been written to disk successfully."); |
|
Console.WriteLine("Press any key to exit..."); |
|
Console.ReadKey(); |
|
} |
|
} |
|
} |