diff --git a/DistIL.sln b/DistIL.sln index 86cb745..5244d34 100644 --- a/DistIL.sln +++ b/DistIL.sln @@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PracticeTests", "tests\Prac EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "tests\Benchmarks\Benchmarks.csproj", "{3CE5B206-AEC2-4A18-9ECF-8DAF17CB893F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DistIL.Testing", "src\DistIL.Testing\DistIL.Testing.csproj", "{D3E25BCB-E6EC-436A-8BA1-269BFB248764}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -47,6 +49,10 @@ Global {3CE5B206-AEC2-4A18-9ECF-8DAF17CB893F}.Debug|Any CPU.Build.0 = Debug|Any CPU {3CE5B206-AEC2-4A18-9ECF-8DAF17CB893F}.Release|Any CPU.ActiveCfg = Release|Any CPU {3CE5B206-AEC2-4A18-9ECF-8DAF17CB893F}.Release|Any CPU.Build.0 = Release|Any CPU + {D3E25BCB-E6EC-436A-8BA1-269BFB248764}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D3E25BCB-E6EC-436A-8BA1-269BFB248764}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D3E25BCB-E6EC-436A-8BA1-269BFB248764}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D3E25BCB-E6EC-436A-8BA1-269BFB248764}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/DistIL.Testing/DistIL.Testing.csproj b/src/DistIL.Testing/DistIL.Testing.csproj new file mode 100644 index 0000000..6a94274 --- /dev/null +++ b/src/DistIL.Testing/DistIL.Testing.csproj @@ -0,0 +1,28 @@ + + + + true + DistIL.Testing + Testing suite for DistIL + + true + + CS1591;CS1573 + + true + snupkg + + true + true + + + + + + + + + + + + diff --git a/src/DistIL.Testing/ModuleAssertions.cs b/src/DistIL.Testing/ModuleAssertions.cs new file mode 100644 index 0000000..1fe4391 --- /dev/null +++ b/src/DistIL.Testing/ModuleAssertions.cs @@ -0,0 +1,46 @@ +using DistIL.AsmIO; + +using Shouldly; + +namespace DistIL.Testing; + +[ShouldlyMethods] +public static class ModuleAssertions +{ + public static void ShouldHaveName(this ModuleDef module, string name) + { + module.AsmName.Name.ShouldBe(name); + } + + public static void ShouldHaveVersion(this ModuleDef module, Version version) + { + module.AsmName.Version.ShouldBe(version); + } + + public static void ShouldHaveCustomAttribute(this ModuleDef module, string fullname, bool forAssembly = true) + { + module.GetCustomAttribs(forAssembly).Any(ca => $"{ca.Type.Namespace}.{ca.Type.Name}" == fullname).ShouldBeTrue(); + } + + public static void ShouldHaveCustomAttribute(this ModuleDef module, bool forAssembly = true) + where TAttribute : Attribute + { + module.ShouldHaveCustomAttribute(typeof(TAttribute)!.FullName!, forAssembly); + } + + public static void ShouldContainType(this ModuleDef module, string ns, string name, bool includeExports = true) + { + module.FindType(ns, name, includeExports, throwIfNotFound: true).ShouldNotBeNull(); + } + + public static void ShouldNotContainType(this ModuleDef module, string ns, string name, bool includeExports = true) + { + module.FindType(ns, name, includeExports, throwIfNotFound: false).ShouldBeNull(); + } + + public static void ShouldTargetRuntimeVersion(this ModuleDef module, string version) + { + module.GetCustomAttribs(true).FirstOrDefault(a => a.Type.Namespace == "System.Runtime.Versioning" && a.Type.Name == "TargetFrameworkAttribute")? + .Args[0].ShouldBe(".NETCoreApp,Version=v" + version); + } +} diff --git a/src/DistIL.Testing/ModuleEntityAssertions.cs b/src/DistIL.Testing/ModuleEntityAssertions.cs new file mode 100644 index 0000000..6c80a40 --- /dev/null +++ b/src/DistIL.Testing/ModuleEntityAssertions.cs @@ -0,0 +1,20 @@ +using DistIL.AsmIO; + +using Shouldly; + +namespace DistIL.Testing; + +[ShouldlyMethods] +public static class ModuleEntityAssertions +{ + public static void ShouldHaveCustomAttribute(this ModuleEntity entity, string fullname) + { + entity.GetCustomAttribs(true).Any(ca => $"{ca.Type.Namespace}.{ca.Type.Name}" == fullname).ShouldBeTrue(); + } + + public static void ShouldHaveCustomAttribute(this ModuleEntity entity) + where TAttribute : Attribute + { + entity.ShouldHaveCustomAttribute(typeof(TAttribute)!.FullName!); + } +} \ No newline at end of file diff --git a/src/DistIL.Testing/TypeAssertions.cs b/src/DistIL.Testing/TypeAssertions.cs new file mode 100644 index 0000000..f3d5e0b --- /dev/null +++ b/src/DistIL.Testing/TypeAssertions.cs @@ -0,0 +1,120 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +using DistIL.AsmIO; + +using Shouldly; + +namespace DistIL.Testing; + +[ShouldlyMethods] +public static class TypeAssertions +{ + public static void ShouldHaveName(this TypeDef type, string name) + { + type.Name.ShouldBe(name); + } + + public static void ShouldHaveNamespace(this TypeDef type, string ns) + { + type.Namespace.ShouldBe(ns); + } + + public static void ShouldHaveBaseType(this TypeDef type, string ns, string name) + { + type.BaseType.ShouldNotBeNull(); + type.BaseType!.Namespace.ShouldBe(ns); + type.BaseType!.Name.ShouldBe(name); + } + + public static void ShouldBeEnum(this TypeDef type) + { + type.IsEnum.ShouldBeTrue(); + } + + public static void ShouldBeEnumWithFlags(this TypeDef type) + { + type.IsEnum.ShouldBeTrue(); + type.HasCustomAttrib(typeof(FlagsAttribute)); + } + + public static void ShouldBeClass(this TypeDef type) + { + type.IsClass.ShouldBeTrue(); + } + + public static void ShouldBeInterface(this TypeDef type) + { + type.IsInterface.ShouldBeTrue(); + } + + public static void ShouldBeValueType(this TypeDef type) + { + type.IsValueType.ShouldBeTrue(); + } + + public static void ShouldBeAbstract(this TypeDef type) + { + type.Attribs.HasFlag(TypeAttributes.Abstract).ShouldBeTrue(); + } + + public static void ShouldBeSealed(this TypeDef type) + { + type.Attribs.HasFlag(TypeAttributes.Sealed).ShouldBeTrue(); + } + + public static void ShouldBePublic(this TypeDef type) + { + type.Attribs.HasFlag(TypeAttributes.Public).ShouldBeTrue(); + } + + public static void ShouldBePrivate(this TypeDef type) + { + type.Attribs.HasFlag(TypeAttributes.NotPublic).ShouldBeTrue(); + } + + public static void ShouldBeInternal(this TypeDef type) + { + type.Attribs.HasFlag(TypeAttributes.NestedAssembly).ShouldBeTrue(); + } + + public static void ShouldBeProtected(this TypeDef type) + { + type.Attribs.HasFlag(TypeAttributes.NestedFamily).ShouldBeTrue(); + } + + public static void ShouldImplement(this TypeDef type, string ns, string name) + { + type.Interfaces.ShouldContain(i => i.Namespace == ns && i.Name == name); + } + + public static void ShouldImplement(this TypeDef type) + { + type.Interfaces.ShouldContain(i => i.Namespace == typeof(T).Namespace && i.Name == typeof(T).Name); + } + + public static void ShouldHaveField(this TypeDef type, string name, string fieldType) + { + type.Fields.ShouldContain(f => f.Name == name && f.Type.Namespace == fieldType); + } + + public static void ShouldHaveProperty(this TypeDef type, string name) + { + type.Properties.ShouldContain(p => p.Name == name); + } + + public static void ShouldHaveMethod(this TypeDef type, string name) + { + type.Methods.ShouldContain(m => m.Name == name); + } + + public static void ShouldHaveMethod(this TypeDef type, string name, string returnType) + { + type.Methods.ShouldContain(m => m.Name == name && m.ReturnType.Namespace == returnType); + } + + public static void ShouldHaveEvent(this TypeDef type, string name) + { + type.Events.ShouldContain(e => e.Name == name); + } +}