diff --git a/ConventionTests.sln.DotSettings b/ConventionTests.sln.DotSettings index b5ca155..2941484 100644 --- a/ConventionTests.sln.DotSettings +++ b/ConventionTests.sln.DotSettings @@ -1,4 +1,5 @@  + DO_NOT_SHOW <?xml version="1.0" encoding="utf-16"?><Profile name="all"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSRemoveCodeRedundancies>True</CSRemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSUseVar><BehavourStyle>CAN_CHANGE_TO_IMPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_IMPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_IMPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments><CSReorderTypeMembers>True</CSReorderTypeMembers></Profile> all False diff --git a/TestStack.ConventionTests.Tests/ConventionAssertionClassTests.approval_mismatch.approved.txt b/TestStack.ConventionTests.Tests/ConventionAssertionClassTests.approval_mismatch.approved.txt index 1f1503b..11906c4 100644 --- a/TestStack.ConventionTests.Tests/ConventionAssertionClassTests.approval_mismatch.approved.txt +++ b/TestStack.ConventionTests.Tests/ConventionAssertionClassTests.approval_mismatch.approved.txt @@ -1,4 +1,5 @@ -Header ------- +Passed: 'Header' for 'Fake data' +-------------------------------- -Approved Exception +With approved exceptions: + Approved Exception \ No newline at end of file diff --git a/TestStack.ConventionTests.Tests/ConventionAssertionClassTests.cs b/TestStack.ConventionTests.Tests/ConventionAssertionClassTests.cs index d6b66ff..6fd3730 100644 --- a/TestStack.ConventionTests.Tests/ConventionAssertionClassTests.cs +++ b/TestStack.ConventionTests.Tests/ConventionAssertionClassTests.cs @@ -1,5 +1,6 @@ namespace TestStack.ConventionTests.Tests { + using System.Collections.Generic; using ApprovalTests.Reporters; using NUnit.Framework; using TestStack.ConventionTests.Internal; @@ -21,16 +22,22 @@ public void approval_mismatch() public class FakeData : IConventionData { - public void EnsureHasNonEmptySource() + public string Description { get { return "Fake data"; } } + + public bool HasData { get { return true; } } + + public ConventionReportFailure Format(string failingData) { + return new ConventionReportFailure(failingData); } } public class FailingConvention : IConvention { - public ConventionResult Execute(FakeData data) + public string ConventionTitle { get { return "Header"; } } + public IEnumerable GetFailingData(FakeData data) { - return ConventionResult.For(new[] { "" }, "Header", (s, builder) => builder.AppendLine("Different")); + return new[] { "Different" }; } } } diff --git a/TestStack.ConventionTests.Tests/ProjectBasedConventions.assemblies_referencing_bin_obj.approved.txt b/TestStack.ConventionTests.Tests/ProjectBasedConventions.assemblies_referencing_bin_obj.approved.txt index 5c77681..c1f4909 100644 --- a/TestStack.ConventionTests.Tests/ProjectBasedConventions.assemblies_referencing_bin_obj.approved.txt +++ b/TestStack.ConventionTests.Tests/ProjectBasedConventions.assemblies_referencing_bin_obj.approved.txt @@ -1,4 +1,4 @@ -Some invalid assembly references found in TestStack.ConventionTests.Tests -------------------------------------------------------------------------- +Failed: 'Project must not reference dlls from bin or obj directories' for 'TestStack.ConventionTests.Tests' +----------------------------------------------------------------------------------------------------------- - bin\Debug\ApprovalTests.dll + bin\Debug\ApprovalTests.dll \ No newline at end of file diff --git a/TestStack.ConventionTests.Tests/ProjectBasedConventions.assemblies_referencing_bin_obj_with_approved_exceptions.approved.txt b/TestStack.ConventionTests.Tests/ProjectBasedConventions.assemblies_referencing_bin_obj_with_approved_exceptions.approved.txt index 5c77681..1b176e9 100644 --- a/TestStack.ConventionTests.Tests/ProjectBasedConventions.assemblies_referencing_bin_obj_with_approved_exceptions.approved.txt +++ b/TestStack.ConventionTests.Tests/ProjectBasedConventions.assemblies_referencing_bin_obj_with_approved_exceptions.approved.txt @@ -1,4 +1,5 @@ -Some invalid assembly references found in TestStack.ConventionTests.Tests -------------------------------------------------------------------------- +Passed: 'Project must not reference dlls from bin or obj directories' for 'TestStack.ConventionTests.Tests' +----------------------------------------------------------------------------------------------------------- - bin\Debug\ApprovalTests.dll +With approved exceptions: + bin\Debug\ApprovalTests.dll \ No newline at end of file diff --git a/TestStack.ConventionTests.Tests/ProjectBasedConventions.cs b/TestStack.ConventionTests.Tests/ProjectBasedConventions.cs index c003a56..45ee86e 100644 --- a/TestStack.ConventionTests.Tests/ProjectBasedConventions.cs +++ b/TestStack.ConventionTests.Tests/ProjectBasedConventions.cs @@ -57,11 +57,8 @@ public void scripts_not_embedded_resources() .Returns(XDocument.Parse(Resources.ProjectFileWithInvalidSqlScriptFile)); var projectLocator = Substitute.For(); - var project = new ProjectFiles(typeof (ProjectBasedConventions).Assembly, projectProvider, projectLocator) - { - Items = i => i.FilePath.EndsWith(".sql") - }; - var ex = Assert.Throws(() => Convention.Is(new FilesAreEmbeddedResources(), project)); + var project = new ProjectFiles(typeof (ProjectBasedConventions).Assembly, projectProvider, projectLocator); + var ex = Assert.Throws(() => Convention.Is(new FilesAreEmbeddedResources(".sql"), project)); Approvals.Verify(ex.Message); } @@ -70,15 +67,12 @@ public void scripts_not_embedded_resources() public void scripts_not_embedded_resources_with_approved_exceptions() { var projectLocator = Substitute.For(); - var project = new ProjectFiles(typeof(ProjectBasedConventions).Assembly, projectProvider, projectLocator) - { - Items = i => i.FilePath.EndsWith(".sql") - }; + var project = new ProjectFiles(typeof (ProjectBasedConventions).Assembly, projectProvider, projectLocator); projectProvider .LoadProjectDocument(Arg.Any()) .Returns(XDocument.Parse(Resources.ProjectFileWithInvalidSqlScriptFile)); - Convention.IsWithApprovedExeptions(new FilesAreEmbeddedResources(), project); + Convention.IsWithApprovedExeptions(new FilesAreEmbeddedResources(".sql"), project); } } } \ No newline at end of file diff --git a/TestStack.ConventionTests.Tests/ProjectBasedConventions.scripts_not_embedded_resources.approved.txt b/TestStack.ConventionTests.Tests/ProjectBasedConventions.scripts_not_embedded_resources.approved.txt index a12b82e..4ce889e 100644 --- a/TestStack.ConventionTests.Tests/ProjectBasedConventions.scripts_not_embedded_resources.approved.txt +++ b/TestStack.ConventionTests.Tests/ProjectBasedConventions.scripts_not_embedded_resources.approved.txt @@ -1,4 +1,4 @@ -The following files which should be embedded resources: -------------------------------------------------------- +Failed: '.sql Files must be embedded resources' for 'TestStack.ConventionTests.Tests' +------------------------------------------------------------------------------------- - Scripts\Script2.sql + Scripts\Script2.sql \ No newline at end of file diff --git a/TestStack.ConventionTests.Tests/ProjectBasedConventions.scripts_not_embedded_resources_with_approved_exceptions.approved.txt b/TestStack.ConventionTests.Tests/ProjectBasedConventions.scripts_not_embedded_resources_with_approved_exceptions.approved.txt index a12b82e..0217788 100644 --- a/TestStack.ConventionTests.Tests/ProjectBasedConventions.scripts_not_embedded_resources_with_approved_exceptions.approved.txt +++ b/TestStack.ConventionTests.Tests/ProjectBasedConventions.scripts_not_embedded_resources_with_approved_exceptions.approved.txt @@ -1,4 +1,5 @@ -The following files which should be embedded resources: -------------------------------------------------------- +Passed: '.sql Files must be embedded resources' for 'TestStack.ConventionTests.Tests' +------------------------------------------------------------------------------------- - Scripts\Script2.sql +With approved exceptions: + Scripts\Script2.sql \ No newline at end of file diff --git a/TestStack.ConventionTests.Tests/TypeBasedConventions.all_classes_have_default_constructor.approved.txt b/TestStack.ConventionTests.Tests/TypeBasedConventions.all_classes_have_default_constructor.approved.txt index 02f6b64..173a5a4 100644 --- a/TestStack.ConventionTests.Tests/TypeBasedConventions.all_classes_have_default_constructor.approved.txt +++ b/TestStack.ConventionTests.Tests/TypeBasedConventions.all_classes_have_default_constructor.approved.txt @@ -1,5 +1,5 @@ -The following types do not have default constructor ---------------------------------------------------- +Failed: 'Types must have a default constructor' for 'nHibernate Entitites' +-------------------------------------------------------------------------- TestAssembly.ClassWithNoDefaultCtor - TestAssembly.ClassWithPrivateDefaultCtor + TestAssembly.ClassWithPrivateDefaultCtor \ No newline at end of file diff --git a/TestStack.ConventionTests.Tests/TypeBasedConventions.all_classes_have_default_constructor_wth_approved_exceptions.approved.txt b/TestStack.ConventionTests.Tests/TypeBasedConventions.all_classes_have_default_constructor_wth_approved_exceptions.approved.txt index 02f6b64..3cad8d9 100644 --- a/TestStack.ConventionTests.Tests/TypeBasedConventions.all_classes_have_default_constructor_wth_approved_exceptions.approved.txt +++ b/TestStack.ConventionTests.Tests/TypeBasedConventions.all_classes_have_default_constructor_wth_approved_exceptions.approved.txt @@ -1,5 +1,6 @@ -The following types do not have default constructor ---------------------------------------------------- +Passed: 'Types must have a default constructor' for 'nHibernate Entitites' +-------------------------------------------------------------------------- +With approved exceptions: TestAssembly.ClassWithNoDefaultCtor - TestAssembly.ClassWithPrivateDefaultCtor + TestAssembly.ClassWithPrivateDefaultCtor \ No newline at end of file diff --git a/TestStack.ConventionTests.Tests/TypeBasedConventions.all_methods_are_virtual.approved.txt b/TestStack.ConventionTests.Tests/TypeBasedConventions.all_methods_are_virtual.approved.txt index b951c4b..ec20755 100644 --- a/TestStack.ConventionTests.Tests/TypeBasedConventions.all_methods_are_virtual.approved.txt +++ b/TestStack.ConventionTests.Tests/TypeBasedConventions.all_methods_are_virtual.approved.txt @@ -1,4 +1,4 @@ -The following methods are not virtual. --------------------------------------- +Failed: 'Methods must be virtual' for 'nHibernate Entitites' +------------------------------------------------------------ - TestAssembly.SampleDomainClass.TestNonVirtual + TestAssembly.SampleDomainClass.TestNonVirtual \ No newline at end of file diff --git a/TestStack.ConventionTests.Tests/TypeBasedConventions.all_methods_are_virtual_wth_approved_exceptions.approved.txt b/TestStack.ConventionTests.Tests/TypeBasedConventions.all_methods_are_virtual_wth_approved_exceptions.approved.txt index b951c4b..c1902ad 100644 --- a/TestStack.ConventionTests.Tests/TypeBasedConventions.all_methods_are_virtual_wth_approved_exceptions.approved.txt +++ b/TestStack.ConventionTests.Tests/TypeBasedConventions.all_methods_are_virtual_wth_approved_exceptions.approved.txt @@ -1,4 +1,5 @@ -The following methods are not virtual. --------------------------------------- +Passed: 'Methods must be virtual' for 'nHibernate Entitites' +------------------------------------------------------------ - TestAssembly.SampleDomainClass.TestNonVirtual +With approved exceptions: + TestAssembly.SampleDomainClass.TestNonVirtual \ No newline at end of file diff --git a/TestStack.ConventionTests.Tests/TypeBasedConventions.cs b/TestStack.ConventionTests.Tests/TypeBasedConventions.cs index 51f7df6..5f3c2d4 100644 --- a/TestStack.ConventionTests.Tests/TypeBasedConventions.cs +++ b/TestStack.ConventionTests.Tests/TypeBasedConventions.cs @@ -17,7 +17,7 @@ public class TypeBasedConventions public TypeBasedConventions() { var itemsToVerify = typeof (SampleDomainClass).Assembly.GetTypes(); - nhibernateEntities = new Types + nhibernateEntities = new Types("nHibernate Entitites") { TypesToVerify = itemsToVerify }; @@ -54,7 +54,7 @@ public void all_methods_are_virtual_wth_approved_exceptions() [Test] public void dtos_exists_in_dto_namespace() { - var types = new Types + var types = new Types("TestAssembly types") { TypesToVerify = new[] { typeof(SomeDto), typeof(BlahDto), typeof(AnotherClass)} }; @@ -67,7 +67,7 @@ public void dtos_exists_in_dto_namespace() [Test] public void dtos_exists_in_dto_namespace_wth_approved_exceptions() { - var types = new Types + var types = new Types("TestAssembly types") { TypesToVerify = new[] { typeof(SomeDto), typeof(BlahDto), typeof(AnotherClass) } }; diff --git a/TestStack.ConventionTests.Tests/TypeBasedConventions.dtos_exists_in_dto_namespace.approved.txt b/TestStack.ConventionTests.Tests/TypeBasedConventions.dtos_exists_in_dto_namespace.approved.txt index 2a5fb4f..b864151 100644 --- a/TestStack.ConventionTests.Tests/TypeBasedConventions.dtos_exists_in_dto_namespace.approved.txt +++ b/TestStack.ConventionTests.Tests/TypeBasedConventions.dtos_exists_in_dto_namespace.approved.txt @@ -1,10 +1,10 @@ -Dtos must be under the 'TestAssembly.Dtos' namespace ----------------------------------------------------- +Failed: 'Dtos must be under the 'TestAssembly.Dtos' namespace' for 'TestAssembly types' +--------------------------------------------------------------------------------------- TestAssembly.SomeDto -Non-Dtos must not be under the 'TestAssembly.Dtos' namespace ------------------------------------------------------------- +Failed: 'Non-Dtos must not be under the 'TestAssembly.Dtos' namespace' for 'TestAssembly types' +----------------------------------------------------------------------------------------------- - TestAssembly.Dtos.AnotherClass + TestAssembly.Dtos.AnotherClass \ No newline at end of file diff --git a/TestStack.ConventionTests.Tests/TypeBasedConventions.dtos_exists_in_dto_namespace_wth_approved_exceptions.approved.txt b/TestStack.ConventionTests.Tests/TypeBasedConventions.dtos_exists_in_dto_namespace_wth_approved_exceptions.approved.txt index 2a5fb4f..6d0ccc9 100644 --- a/TestStack.ConventionTests.Tests/TypeBasedConventions.dtos_exists_in_dto_namespace_wth_approved_exceptions.approved.txt +++ b/TestStack.ConventionTests.Tests/TypeBasedConventions.dtos_exists_in_dto_namespace_wth_approved_exceptions.approved.txt @@ -1,10 +1,14 @@ -Dtos must be under the 'TestAssembly.Dtos' namespace ----------------------------------------------------- +Passed: 'Dtos must be under the 'TestAssembly.Dtos' namespace' for 'TestAssembly types' +--------------------------------------------------------------------------------------- +With approved exceptions: TestAssembly.SomeDto -Non-Dtos must not be under the 'TestAssembly.Dtos' namespace ------------------------------------------------------------- - TestAssembly.Dtos.AnotherClass + +Passed: 'Non-Dtos must not be under the 'TestAssembly.Dtos' namespace' for 'TestAssembly types' +----------------------------------------------------------------------------------------------- + +With approved exceptions: + TestAssembly.Dtos.AnotherClass \ No newline at end of file diff --git a/TestStack.ConventionTests/Convention.cs b/TestStack.ConventionTests/Convention.cs index 0d60479..606d671 100644 --- a/TestStack.ConventionTests/Convention.cs +++ b/TestStack.ConventionTests/Convention.cs @@ -1,36 +1,147 @@ namespace TestStack.ConventionTests { - using System.Diagnostics; + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Reflection; using ApprovalTests; using ApprovalTests.Core.Exceptions; + using TestStack.ConventionTests.Conventions; + using TestStack.ConventionTests.Internal; + using TestStack.ConventionTests.Reporting; public static class Convention { - public static void Is(IConvention convention, TData data) where TData : IConventionData + static readonly HtmlReportRenderer HtmlRenderer = new HtmlReportRenderer(AssemblyDirectory); + static readonly List Reports = new List(); + + static Convention() + { + Formatters = new List + { + new TypeDataFormatter(), + new ProjectReferenceFormatter(), + new ProjectFileFormatter(), + new MethodInfoDataFormatter(), + new StringDataFormatter() + }; + } + + public static IEnumerable ConventionReports { get { return Reports; } } + public static IList Formatters { get; set; } + + public static void Is(IConvention convention, TDataSource data) + where TDataSource : IConventionData { - data.EnsureHasNonEmptySource(); - var result = convention.Execute(data); + Is(convention, data, new ConventionResultExceptionReporter()); + } + + public static void Is(IConvention convention, TDataSource data, IConventionReportRenderer reporter) + where TDataSource : IConventionData + { + try + { + var conventionResult = Executor.GetConventionReport(convention.ConventionTitle, convention.GetFailingData(data).ToArray(), data); + Reports.Add(conventionResult); - if (result.Failed) - throw new ConventionFailedException(result.Message); + new ConventionReportTraceRenderer().Render(conventionResult); + reporter.Render(conventionResult); + } + finally + { + HtmlRenderer.Render(Reports.ToArray()); + } } - public static void IsWithApprovedExeptions(IConvention convention, TData data) where TData : IConventionData + public static void IsWithApprovedExeptions(IConvention convention, TDataSource data) + where TDataSource : IConventionData { - data.EnsureHasNonEmptySource(); - var result = convention.Execute(data); + var conventionResult = Executor.GetConventionReportWithApprovedExeptions(convention.ConventionTitle, convention.GetFailingData(data).ToArray(), data); + Reports.Add(conventionResult); - // should we encapsulate Approvals behind Settings? try { - Approvals.Verify(result.Message); + var conventionReportTextRenderer = new ConventionReportTextRenderer(); + conventionReportTextRenderer.Render(conventionResult); + Approvals.Verify(conventionReportTextRenderer.Output); - Trace.WriteLine(string.Format("{0} has approved exceptions:\r\n\r\n{1}", convention.GetType().Name, result.Message)); + new ConventionReportTraceRenderer().Render(conventionResult); } catch (ApprovalException ex) { throw new ConventionFailedException("Approved exceptions for convention differs\r\n\r\n"+ex.Message, ex); } + finally + { + HtmlRenderer.Render(Reports.ToArray()); + } + } + + public static void Is(ISymmetricConvention convention, TDataSource data) + where TDataSource : IConventionData + { + Is(convention, data, new ConventionResultExceptionReporter()); + } + + public static void Is(ISymmetricConvention convention, TDataSource data, IConventionReportRenderer reporter) + where TDataSource : IConventionData + { + try + { + var conventionResult = Executor.GetConventionReport(convention.ConventionTitle, convention.GetFailingData(data).ToArray(), data); + var inverseConventionResult = Executor.GetConventionReport(convention.InverseTitle, convention.GetFailingInverseData(data).ToArray(), data); + + Reports.Add(conventionResult); + Reports.Add(inverseConventionResult); + + new ConventionReportTraceRenderer().Render(conventionResult, inverseConventionResult); + reporter.Render(conventionResult, inverseConventionResult); + } + finally + { + HtmlRenderer.Render(Reports.ToArray()); + } + } + + public static void IsWithApprovedExeptions(ISymmetricConvention convention, TDataSource data) + where TDataSource : IConventionData + { + var conventionResult = Executor.GetConventionReportWithApprovedExeptions(convention.ConventionTitle, convention.GetFailingData(data).ToArray(), data); + var inverseConventionResult = Executor.GetConventionReportWithApprovedExeptions(convention.InverseTitle, convention.GetFailingInverseData(data).ToArray(), data); + Reports.Add(conventionResult); + Reports.Add(inverseConventionResult); + + try + { + //Render both, with approved exceptions included + var conventionReportTextRenderer = new ConventionReportTextRenderer(); + conventionReportTextRenderer.Render(conventionResult, inverseConventionResult); + Approvals.Verify(conventionReportTextRenderer.Output); + + // Trace on success + new ConventionReportTraceRenderer().Render(conventionResult, inverseConventionResult); + } + catch (ApprovalException ex) + { + throw new ConventionFailedException("Approved exceptions for convention differs\r\n\r\n" + ex.Message, ex); + } + finally + { + HtmlRenderer.Render(Reports.ToArray()); + } + } + + // http://stackoverflow.com/questions/52797/c-how-do-i-get-the-path-of-the-assembly-the-code-is-in#answer-283917 + static string AssemblyDirectory + { + get + { + string codeBase = Assembly.GetExecutingAssembly().CodeBase; + var uri = new UriBuilder(codeBase); + string path = Uri.UnescapeDataString(uri.Path); + return Path.GetDirectoryName(path); + } } } } \ No newline at end of file diff --git a/TestStack.ConventionTests/ConventionData/AbstractProjectData.cs b/TestStack.ConventionTests/ConventionData/AbstractProjectData.cs index d4f5d29..971779c 100644 --- a/TestStack.ConventionTests/ConventionData/AbstractProjectData.cs +++ b/TestStack.ConventionTests/ConventionData/AbstractProjectData.cs @@ -20,11 +20,9 @@ protected AbstractProjectData(Assembly assembly, IProjectProvider projectProvide public IProjectProvider ProjectProvider { get; private set; } - public void EnsureHasNonEmptySource() - { - if (ProjectLocator.ResolveProjectFilePath(Assembly) == null) - throw new ConventionSourceInvalidException("Cannot resolve project file for assembly {0}"); - } + public string Description { get { return Assembly.GetName().Name; } } + + public bool HasData { get { return ProjectLocator.ResolveProjectFilePath(Assembly) != null; } } protected XDocument GetProject() { diff --git a/TestStack.ConventionTests/ConventionData/ProjectFiles.cs b/TestStack.ConventionTests/ConventionData/ProjectFiles.cs index 2138ad6..71e45d8 100644 --- a/TestStack.ConventionTests/ConventionData/ProjectFiles.cs +++ b/TestStack.ConventionTests/ConventionData/ProjectFiles.cs @@ -1,6 +1,5 @@ namespace TestStack.ConventionTests.ConventionData { - using System; using System.Linq; using System.Reflection; using System.Xml.Linq; @@ -11,10 +10,8 @@ public class ProjectFiles : AbstractProjectData public ProjectFiles(Assembly assembly, IProjectProvider projectProvider, IProjectLocator projectLocator) : base(assembly, projectProvider, projectLocator) { - Items = PredicateHelpers.All(); } - public ProjectFile[] Files { get @@ -31,11 +28,8 @@ public ProjectFile[] Files ReferenceType = refElem.Name.LocalName, FilePath = refElem.Attribute("Include").Value }) - .Where(Items) .ToArray(); } } - - public Func Items { get; set; } } } \ No newline at end of file diff --git a/TestStack.ConventionTests/ConventionData/ProjectReferences.cs b/TestStack.ConventionTests/ConventionData/ProjectReferences.cs index b22aa2a..eb83825 100644 --- a/TestStack.ConventionTests/ConventionData/ProjectReferences.cs +++ b/TestStack.ConventionTests/ConventionData/ProjectReferences.cs @@ -12,7 +12,6 @@ public class ProjectReferences : AbstractProjectData public ProjectReferences(Assembly assembly, IProjectProvider projectProvider, IProjectLocator projectLocator) : base(assembly, projectProvider, projectLocator) { - Items = PredicateHelpers.All(); } public ProjectReference[] References @@ -25,7 +24,6 @@ public ProjectReference[] References { ReferencedPath = r }) - .Where(Items) .ToArray(); } } @@ -41,7 +39,5 @@ static IEnumerable AllProjectReferences(XDocument projDefinition) .Select(refElem => refElem.Value); return references; } - - public Func Items { get; set; } } } \ No newline at end of file diff --git a/TestStack.ConventionTests/ConventionData/Types.cs b/TestStack.ConventionTests/ConventionData/Types.cs index 74d1cc0..e491811 100644 --- a/TestStack.ConventionTests/ConventionData/Types.cs +++ b/TestStack.ConventionTests/ConventionData/Types.cs @@ -1,20 +1,22 @@ namespace TestStack.ConventionTests.ConventionData { using System; - using TestStack.ConventionTests.Conventions; - using TestStack.ConventionTests.Internal; + using System.Linq; /// /// This is where we set what our convention is all about. /// public class Types : IConventionData { - public Type[] TypesToVerify { get; set; } - - public void EnsureHasNonEmptySource() + public Types(string descriptionOfTypes) { - if (TypesToVerify.None()) - throw new ConventionSourceInvalidException("You must supply types to verify"); + Description = descriptionOfTypes; } + + public Type[] TypesToVerify { get; set; } + + public string Description { get; private set; } + + public bool HasData {get { return TypesToVerify.Any(); }} } } \ No newline at end of file diff --git a/TestStack.ConventionTests/Conventions/AllClassesHaveDefaultConstructor.cs b/TestStack.ConventionTests/Conventions/AllClassesHaveDefaultConstructor.cs index 3881284..63ee4f8 100644 --- a/TestStack.ConventionTests/Conventions/AllClassesHaveDefaultConstructor.cs +++ b/TestStack.ConventionTests/Conventions/AllClassesHaveDefaultConstructor.cs @@ -1,22 +1,17 @@ namespace TestStack.ConventionTests.Conventions { + using System.Collections.Generic; using System.Linq; using TestStack.ConventionTests.ConventionData; using TestStack.ConventionTests.Internal; public class AllClassesHaveDefaultConstructor : IConvention { - public AllClassesHaveDefaultConstructor() - { - HeaderMessage = "The following types do not have default constructor"; - } - - public string HeaderMessage { get; set; } + public string ConventionTitle { get { return "Types must have a default constructor"; } } - public ConventionResult Execute(Types data) + public IEnumerable GetFailingData(Types data) { - var invalid = data.TypesToVerify.Where(t => t.HasDefaultConstructor() == false); - return ConventionResult.For(invalid, HeaderMessage, t => "\t" + t); + return data.TypesToVerify.Where(t => t.HasDefaultConstructor() == false); } } } \ No newline at end of file diff --git a/TestStack.ConventionTests/Conventions/AllMethodsAreVirtual.cs b/TestStack.ConventionTests/Conventions/AllMethodsAreVirtual.cs index 7d43b01..dcf659e 100644 --- a/TestStack.ConventionTests/Conventions/AllMethodsAreVirtual.cs +++ b/TestStack.ConventionTests/Conventions/AllMethodsAreVirtual.cs @@ -1,44 +1,18 @@ namespace TestStack.ConventionTests.Conventions { - using System; using System.Collections.Generic; using System.Linq; using System.Reflection; - using System.Text; using TestStack.ConventionTests.ConventionData; using TestStack.ConventionTests.Internal; public class AllMethodsAreVirtual : IConvention { - public AllMethodsAreVirtual() - { - HeaderMessage = "The following methods are not virtual."; - } - - public string HeaderMessage { get; set; } - - public ConventionResult Execute(Types data) - { - // do we want to encapsulate that in some way? - // also notice how data gives us types, yet the convention acts upon methods. - var items = from applicableType in data.TypesToVerify - let nonVirtuals = applicableType.NonVirtualMethods() - where nonVirtuals.Any() - select Tuple.Create(applicableType, nonVirtuals); - return ConventionResult.For(items, HeaderMessage, DescribeTypeAndMethods); - } + public string ConventionTitle { get { return "Methods must be virtual"; } } - // I like how that's encapsulated in the reusable convention type, whereas previously it was part of the convention/test code - void DescribeTypeAndMethods(Tuple> item, StringBuilder message) + public IEnumerable GetFailingData(Types data) { - foreach (var method in item.Item2) - { - message.Append("\t"); - message.Append(item.Item1); - message.Append("."); - message.Append(method.Name); - message.AppendLine(); - } + return data.TypesToVerify.SelectMany(t => t.NonVirtualMethods()); } } } \ No newline at end of file diff --git a/TestStack.ConventionTests/Conventions/ClassTypeHasSpecificNamespace.cs b/TestStack.ConventionTests/Conventions/ClassTypeHasSpecificNamespace.cs index 700b47d..d109d37 100644 --- a/TestStack.ConventionTests/Conventions/ClassTypeHasSpecificNamespace.cs +++ b/TestStack.ConventionTests/Conventions/ClassTypeHasSpecificNamespace.cs @@ -1,9 +1,9 @@ namespace TestStack.ConventionTests.Conventions { using System; + using System.Collections.Generic; using System.Linq; using TestStack.ConventionTests.ConventionData; - using TestStack.ConventionTests.Internal; /// /// This convention allows you to enforce a particular type of class is under a namespace, for instance. @@ -14,7 +14,7 @@ /// /// This is a Symmetric convention, and will verify all of a Class Type lives in the namespace, but also that only that class type is in that namespace /// - public class ClassTypeHasSpecificNamespace : IConvention + public class ClassTypeHasSpecificNamespace : ISymmetricConvention { readonly Func classIsApplicable; readonly string namespaceToCheck; @@ -33,24 +33,34 @@ public ClassTypeHasSpecificNamespace(Func classIsApplicable, string this.classType = classType; } - public ConventionResult Execute(Types data) + public string ConventionTitle { - var applicableTypes = data.TypesToVerify.Where(classIsApplicable); - var nonApplicableTypes = data.TypesToVerify.Where(t => !classIsApplicable(t)); + get + { + return string.Format("{0}s must be under the '{1}' namespace", classType, namespaceToCheck); + } + } - var applicableTypesWhichDoNotConform = applicableTypes - .Where(t => t.Namespace == null || !t.Namespace.StartsWith(namespaceToCheck)) - .ToArray(); - var nonApplicableTypesInNamespace = nonApplicableTypes - .Where(t => t.Namespace != null && t.Namespace.StartsWith(namespaceToCheck)) - .ToArray(); + public string InverseTitle + { + get + { + return string.Format("Non-{0}s must not be under the '{1}' namespace", classType, namespaceToCheck); + } + } - return ConventionResult.ForSymmetric( - string.Format("{0}s must be under the '{1}' namespace", classType, namespaceToCheck), - applicableTypesWhichDoNotConform, - string.Format("Non-{0}s must not be under the '{1}' namespace", classType, namespaceToCheck), - nonApplicableTypesInNamespace, - t => "\t" + t.FullName); + public IEnumerable GetFailingData(Types data) + { + return data.TypesToVerify + .Where(classIsApplicable) + .Where(t => t.Namespace == null || !t.Namespace.StartsWith(namespaceToCheck)); + } + + public IEnumerable GetFailingInverseData(Types data) + { + return data.TypesToVerify + .Where(t => !classIsApplicable(t)) + .Where(t => t.Namespace != null && t.Namespace.StartsWith(namespaceToCheck)); } } } \ No newline at end of file diff --git a/TestStack.ConventionTests/Conventions/FilesAreEmbeddedResources.cs b/TestStack.ConventionTests/Conventions/FilesAreEmbeddedResources.cs index 73b8e60..88fc915 100644 --- a/TestStack.ConventionTests/Conventions/FilesAreEmbeddedResources.cs +++ b/TestStack.ConventionTests/Conventions/FilesAreEmbeddedResources.cs @@ -1,16 +1,29 @@ namespace TestStack.ConventionTests.Conventions { + using System.Collections.Generic; using System.Linq; using TestStack.ConventionTests.ConventionData; - using TestStack.ConventionTests.Internal; public class FilesAreEmbeddedResources : IConvention { - public ConventionResult Execute(ProjectFiles data) + public FilesAreEmbeddedResources(string fileExtension) { - return ConventionResult.For(data.Files.Where(s => s.ReferenceType != "EmbeddedResource"), - "The following files which should be embedded resources:", - (t, m) => m.AppendLine("\t" + t.FilePath)); + FileExtension = fileExtension; + } + + public string ConventionTitle + { + get + { + return string.Format("{0} Files must be embedded resources", FileExtension); + } + } + + public string FileExtension { get; set; } + + public IEnumerable GetFailingData(ProjectFiles data) + { + return data.Files.Where(s => s.FilePath.EndsWith(FileExtension) && s.ReferenceType != "EmbeddedResource"); } } } \ No newline at end of file diff --git a/TestStack.ConventionTests/Conventions/ISymmetricConvention.cs b/TestStack.ConventionTests/Conventions/ISymmetricConvention.cs new file mode 100644 index 0000000..e7454bf --- /dev/null +++ b/TestStack.ConventionTests/Conventions/ISymmetricConvention.cs @@ -0,0 +1,12 @@ +namespace TestStack.ConventionTests.Conventions +{ + using System.Collections.Generic; + + public interface ISymmetricConvention where T : IConventionData + { + string ConventionTitle { get; } + string InverseTitle { get; } + IEnumerable GetFailingData(T data); + IEnumerable GetFailingInverseData(T data); + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Conventions/ProjectDoesNotReferenceDllsFromBinOrObjDirectories.cs b/TestStack.ConventionTests/Conventions/ProjectDoesNotReferenceDllsFromBinOrObjDirectories.cs index 716c654..04d9fdb 100644 --- a/TestStack.ConventionTests/Conventions/ProjectDoesNotReferenceDllsFromBinOrObjDirectories.cs +++ b/TestStack.ConventionTests/Conventions/ProjectDoesNotReferenceDllsFromBinOrObjDirectories.cs @@ -1,30 +1,24 @@ namespace TestStack.ConventionTests.Conventions { + using System.Collections.Generic; using System.Linq; - using System.Text; using System.Text.RegularExpressions; using TestStack.ConventionTests.ConventionData; - using TestStack.ConventionTests.Internal; public class ProjectDoesNotReferenceDllsFromBinOrObjDirectories : IConvention { const string AssemblyReferencingObjRegex = @"^(?.*?(obj|bin).*?)$"; - public ConventionResult Execute(ProjectReferences data) + static bool IsBinOrObjReference(ProjectReference reference) { - var invalid = data.References.Where(IsBinOrObjReference); - var header = string.Format("Some invalid assembly references found in {0}", data.Assembly.GetName().Name); - return ConventionResult.For(invalid, header, FormatLine); + return Regex.IsMatch(reference.ReferencedPath, AssemblyReferencingObjRegex, RegexOptions.IgnoreCase); } - void FormatLine(ProjectReference assemblyReference, StringBuilder m) - { - m.AppendLine("\t" + assemblyReference.ReferencedPath); - } + public string ConventionTitle { get { return "Project must not reference dlls from bin or obj directories"; } } - static bool IsBinOrObjReference(ProjectReference reference) + public IEnumerable GetFailingData(ProjectReferences data) { - return Regex.IsMatch(reference.ReferencedPath, AssemblyReferencingObjRegex, RegexOptions.IgnoreCase); + return data.References.Where(IsBinOrObjReference); } } } \ No newline at end of file diff --git a/TestStack.ConventionTests/IConvention.cs b/TestStack.ConventionTests/IConvention.cs index 82a0908..468fdf6 100644 --- a/TestStack.ConventionTests/IConvention.cs +++ b/TestStack.ConventionTests/IConvention.cs @@ -1,9 +1,10 @@ namespace TestStack.ConventionTests { - using TestStack.ConventionTests.Internal; + using System.Collections.Generic; public interface IConvention where T : IConventionData { - ConventionResult Execute(T data); + string ConventionTitle { get; } + IEnumerable GetFailingData(T data); } } \ No newline at end of file diff --git a/TestStack.ConventionTests/IConventionData.cs b/TestStack.ConventionTests/IConventionData.cs index 26b0215..03679d0 100644 --- a/TestStack.ConventionTests/IConventionData.cs +++ b/TestStack.ConventionTests/IConventionData.cs @@ -2,6 +2,7 @@ { public interface IConventionData { - void EnsureHasNonEmptySource(); + string Description { get; } + bool HasData { get; } } } \ No newline at end of file diff --git a/TestStack.ConventionTests/Internal/ConventionReportFailure.cs b/TestStack.ConventionTests/Internal/ConventionReportFailure.cs new file mode 100644 index 0000000..199b94c --- /dev/null +++ b/TestStack.ConventionTests/Internal/ConventionReportFailure.cs @@ -0,0 +1,17 @@ +namespace TestStack.ConventionTests.Internal +{ + public class ConventionReportFailure + { + public string Failure { get; set; } + + public ConventionReportFailure(string failure) + { + Failure = failure; + } + + public override string ToString() + { + return Failure; + } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Internal/ConventionResult.cs b/TestStack.ConventionTests/Internal/ConventionResult.cs deleted file mode 100644 index 59e655c..0000000 --- a/TestStack.ConventionTests/Internal/ConventionResult.cs +++ /dev/null @@ -1,99 +0,0 @@ -namespace TestStack.ConventionTests.Internal -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - - public class ConventionResult - { - ConventionResult() - { - } - - public string Message { get; private set; } - - public bool Failed - { - get { return !string.IsNullOrEmpty(Message); } - } - - public static ConventionResult For( - IEnumerable items, - string header, - Action itemDescriptor) - { - var array = items.ToArray(); - var result = new ConventionResult(); - if (array.None()) - { - return result; - } - - // NOTE: we might possibly want to abstract the StringBuilder to have more high level construct that would allow us to plug rich reports here... - var message = new StringBuilder(header); - message.AppendLine(); - message.AppendLine(string.Empty.PadRight(header.Length, '-')); - message.AppendLine(); - Array.ForEach(array, r => itemDescriptor(r, message)); - result.Message = message.ToString(); - return result; - } - - public static ConventionResult For( - IEnumerable items, - string header, - Func itemDescriptor) - { - return For(items, header, (item, message) => message.AppendLine(itemDescriptor(item))); - } - - public static ConventionResult ForSymmetric( - string firstHeader, TResult[] firstResults, - string secondHeader, TResult[] secondResults, - Action itemDescriptor) - { - var firstArray = firstResults.ToArray(); - var secondArray = secondResults.ToArray(); - var result = new ConventionResult(); - if (firstArray.None() && secondArray.None()) - { - return result; - } - - var message = new StringBuilder(); - if (firstArray.Any()) - { - message.AppendLine(firstHeader); - message.AppendLine(string.Empty.PadRight(firstHeader.Length, '-')); - message.AppendLine(); - Array.ForEach(firstArray, r => itemDescriptor(r, message)); - } - if (secondArray.Any()) - { - if (firstArray.Any()) - { - message.AppendLine(); - message.AppendLine(); - } - message.AppendLine(secondHeader); - message.AppendLine(string.Empty.PadRight(secondHeader.Length, '-')); - message.AppendLine(); - Array.ForEach(secondArray, r => itemDescriptor(r, message)); - } - result.Message = message.ToString(); - return result; - } - - public static ConventionResult ForSymmetric( - string firstHeader, TResult[] firstResults, - string secondHeader, TResult[] secondResults, - Func itemDescriptor) - { - return ForSymmetric( - firstHeader, firstResults, secondHeader, - secondResults, - (item, message) => message.AppendLine(itemDescriptor(item))); - } - } -} \ No newline at end of file diff --git a/TestStack.ConventionTests/Internal/Executor.cs b/TestStack.ConventionTests/Internal/Executor.cs new file mode 100644 index 0000000..c054e29 --- /dev/null +++ b/TestStack.ConventionTests/Internal/Executor.cs @@ -0,0 +1,45 @@ +namespace TestStack.ConventionTests.Internal +{ + using System.Linq; + using TestStack.ConventionTests.Conventions; + using TestStack.ConventionTests.Reporting; + + public static class Executor + { + public static ResultInfo GetConventionReport(string conventionTitle, object[] failingData, IConventionData data) + { + if (!data.HasData) + throw new ConventionSourceInvalidException(string.Format("{0} has no data", data.Description)); + + var passed = failingData.None(); + + var conventionResult = new ResultInfo( + passed ? TestResult.Passed : TestResult.Failed, + conventionTitle, + data.Description, + failingData.Select(FormatData).ToArray()); + return conventionResult; + } + + public static ResultInfo GetConventionReportWithApprovedExeptions(string conventionTitle, object[] failingData, IConventionData data) + { + var conventionResult = Executor.GetConventionReport(conventionTitle, failingData, data); + var conventionReportTextRenderer = new ConventionReportTextRenderer(); + // Add approved exceptions to report + conventionReportTextRenderer.RenderItems(conventionResult); + conventionResult.WithApprovedException(conventionReportTextRenderer.Output); + + return conventionResult; + } + + static ConventionReportFailure FormatData(T failingData) + { + var formatter = Convention.Formatters.FirstOrDefault(f => f.CanFormat(failingData)); + + if (formatter == null) + throw new NoDataFormatterFoundException(typeof(T).Name + " has no formatter, add one with `Convention.Formatters.Add(new MyDataFormatter());`"); + + return formatter.Format(failingData); + } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Internal/NoDataFormatterFoundException.cs b/TestStack.ConventionTests/Internal/NoDataFormatterFoundException.cs new file mode 100644 index 0000000..6347ed1 --- /dev/null +++ b/TestStack.ConventionTests/Internal/NoDataFormatterFoundException.cs @@ -0,0 +1,27 @@ +namespace TestStack.ConventionTests.Internal +{ + using System; + using System.Runtime.Serialization; + + [Serializable] + public class NoDataFormatterFoundException : Exception + { + public NoDataFormatterFoundException() + { + } + + public NoDataFormatterFoundException(string message) : base(message) + { + } + + public NoDataFormatterFoundException(string message, Exception inner) : base(message, inner) + { + } + + protected NoDataFormatterFoundException( + SerializationInfo info, + StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Internal/ResultInfo.cs b/TestStack.ConventionTests/Internal/ResultInfo.cs new file mode 100644 index 0000000..3b2828b --- /dev/null +++ b/TestStack.ConventionTests/Internal/ResultInfo.cs @@ -0,0 +1,28 @@ +namespace TestStack.ConventionTests.Internal +{ + using TestStack.ConventionTests.Reporting; + + public class ResultInfo + { + public TestResult Result { get; private set; } + public string ConventionTitle { get; private set; } + public string DataDescription { get; private set; } + public ConventionReportFailure[] ConventionFailures { get; private set; } + public string ApprovedException { get; private set; } + + public ResultInfo(TestResult result, string conventionTitle, string dataDescription, ConventionReportFailure[] conventionFailures) + { + Result = result; + ConventionTitle = conventionTitle; + DataDescription = dataDescription; + ConventionFailures = conventionFailures; + } + + public void WithApprovedException(string output) + { + ApprovedException = output; + Result = TestResult.Passed; + ConventionFailures = new ConventionReportFailure[0]; + } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Reporting/ConventionReportTextRenderer.cs b/TestStack.ConventionTests/Reporting/ConventionReportTextRenderer.cs new file mode 100644 index 0000000..3fda3d0 --- /dev/null +++ b/TestStack.ConventionTests/Reporting/ConventionReportTextRenderer.cs @@ -0,0 +1,53 @@ +namespace TestStack.ConventionTests.Reporting +{ + using System.Text; + using TestStack.ConventionTests.Internal; + + public class ConventionReportTextRenderer : IConventionReportRenderer + { + public void Render(params ResultInfo[] conventionResult) + { + var stringBuilder = new StringBuilder(); + + foreach (var conventionReport in conventionResult) + { + var title = string.Format("{0}: '{1}' for '{2}'", conventionReport.Result, conventionReport.ConventionTitle, + conventionReport.DataDescription); + stringBuilder.AppendLine(title); + stringBuilder.AppendLine(string.Empty.PadRight(title.Length, '-')); + stringBuilder.AppendLine(); + + if (!string.IsNullOrEmpty(conventionReport.ApprovedException)) + { + stringBuilder.AppendLine("With approved exceptions:"); + stringBuilder.AppendLine(conventionReport.ApprovedException); + stringBuilder.AppendLine(); + } + + RenderItems(conventionReport, stringBuilder); + stringBuilder.AppendLine(); + stringBuilder.AppendLine(); + } + + Output = stringBuilder.ToString().TrimEnd(); + } + + public string Output { get; private set; } + + public void RenderItems(ResultInfo conventionResult) + { + var stringBuilder = new StringBuilder(); + RenderItems(conventionResult, stringBuilder); + Output = stringBuilder.ToString(); + } + + static void RenderItems(ResultInfo resultInfo, StringBuilder stringBuilder) + { + foreach (var conventionFailure in resultInfo.ConventionFailures) + { + stringBuilder.Append("\t"); + stringBuilder.AppendLine(conventionFailure.ToString()); + } + } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Reporting/ConventionReportTraceRenderer.cs b/TestStack.ConventionTests/Reporting/ConventionReportTraceRenderer.cs new file mode 100644 index 0000000..7c4d68d --- /dev/null +++ b/TestStack.ConventionTests/Reporting/ConventionReportTraceRenderer.cs @@ -0,0 +1,15 @@ +namespace TestStack.ConventionTests.Reporting +{ + using System.Diagnostics; + using TestStack.ConventionTests.Internal; + + public class ConventionReportTraceRenderer : IConventionReportRenderer + { + public void Render(params ResultInfo[] conventionResult) + { + var conventionReportTextRenderer = new ConventionReportTextRenderer(); + conventionReportTextRenderer.Render(conventionResult); + Trace.WriteLine(conventionReportTextRenderer.Output); + } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Reporting/ConventionResultExceptionReporter.cs b/TestStack.ConventionTests/Reporting/ConventionResultExceptionReporter.cs new file mode 100644 index 0000000..c4cc903 --- /dev/null +++ b/TestStack.ConventionTests/Reporting/ConventionResultExceptionReporter.cs @@ -0,0 +1,18 @@ +namespace TestStack.ConventionTests.Reporting +{ + using System.Linq; + using TestStack.ConventionTests.Internal; + + public class ConventionResultExceptionReporter : IConventionReportRenderer + { + public void Render(params ResultInfo[] conventionResult) + { + var conventionReportTextRenderer = new ConventionReportTextRenderer(); + conventionReportTextRenderer.Render(conventionResult); + if (conventionResult.Any(r => r.Result == TestResult.Failed)) + { + throw new ConventionFailedException(conventionReportTextRenderer.Output); + } + } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Reporting/HtmlReportRenderer.cs b/TestStack.ConventionTests/Reporting/HtmlReportRenderer.cs new file mode 100644 index 0000000..2f646b3 --- /dev/null +++ b/TestStack.ConventionTests/Reporting/HtmlReportRenderer.cs @@ -0,0 +1,78 @@ +namespace TestStack.ConventionTests.Reporting +{ + using System.IO; + using System.Text; + using System.Web.UI; + using TestStack.ConventionTests.Internal; + + public class HtmlReportRenderer : IConventionReportRenderer + { + readonly string file; + + public HtmlReportRenderer(string assemblyDirectory) + { + file = Path.Combine(assemblyDirectory, "Conventions.htm"); + } + + public void Render(params ResultInfo[] conventionResult) + { + var sb = new StringBuilder(); + var html = new HtmlTextWriter(new StringWriter(sb)); + html.WriteLine(""); + html.RenderBeginTag(HtmlTextWriterTag.Html); // + html.RenderBeginTag(HtmlTextWriterTag.Head); // + html.RenderEndTag(); // + html.WriteLine(); + html.RenderBeginTag(HtmlTextWriterTag.Body); // + + html.RenderBeginTag(HtmlTextWriterTag.H1); + html.Write("Project Conventions"); + html.RenderEndTag(); + + foreach (var conventionReport in conventionResult) + { + html.RenderBeginTag(HtmlTextWriterTag.P); + html.RenderBeginTag(HtmlTextWriterTag.Div); + html.RenderBeginTag(HtmlTextWriterTag.Strong); + html.Write(conventionReport.Result+": "); + html.RenderEndTag(); + var title = string.Format("{0} for {1}", conventionReport.ConventionTitle, conventionReport.DataDescription); + html.Write(title); + if (!string.IsNullOrEmpty(conventionReport.ApprovedException)) + { + html.RenderBeginTag(HtmlTextWriterTag.Div); + html.RenderBeginTag(HtmlTextWriterTag.Strong); + html.WriteLine("With approved exceptions:"); + html.RenderEndTag(); + html.RenderEndTag(); + } + + html.RenderBeginTag(HtmlTextWriterTag.Ul); + + if (!string.IsNullOrEmpty(conventionReport.ApprovedException)) + { + html.RenderBeginTag(HtmlTextWriterTag.Li); + html.WriteLine(conventionReport.ApprovedException); + html.RenderEndTag(); + } + + foreach (var conventionFailure in conventionReport.ConventionFailures) + { + html.RenderBeginTag(HtmlTextWriterTag.Li); + html.Write(conventionFailure.ToString()); + html.RenderEndTag(); + } + + html.RenderEndTag(); + html.RenderEndTag(); + html.RenderEndTag(); + } + + html.RenderEndTag(); // + html.RenderEndTag(); // + html.Flush(); + + File.WriteAllText(file, sb.ToString()); + } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Reporting/IConventionReportRenderer.cs b/TestStack.ConventionTests/Reporting/IConventionReportRenderer.cs new file mode 100644 index 0000000..15ee9fc --- /dev/null +++ b/TestStack.ConventionTests/Reporting/IConventionReportRenderer.cs @@ -0,0 +1,9 @@ +namespace TestStack.ConventionTests.Reporting +{ + using TestStack.ConventionTests.Internal; + + public interface IConventionReportRenderer + { + void Render(params ResultInfo[] conventionResult); + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Reporting/IReportDataFormatter.cs b/TestStack.ConventionTests/Reporting/IReportDataFormatter.cs new file mode 100644 index 0000000..1cea26c --- /dev/null +++ b/TestStack.ConventionTests/Reporting/IReportDataFormatter.cs @@ -0,0 +1,10 @@ +namespace TestStack.ConventionTests.Reporting +{ + using TestStack.ConventionTests.Internal; + + public interface IReportDataFormatter + { + bool CanFormat(object failingData); + ConventionReportFailure Format(object failingData); + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Reporting/MethodInfoDataFormatter.cs b/TestStack.ConventionTests/Reporting/MethodInfoDataFormatter.cs new file mode 100644 index 0000000..7bdd5a1 --- /dev/null +++ b/TestStack.ConventionTests/Reporting/MethodInfoDataFormatter.cs @@ -0,0 +1,20 @@ +namespace TestStack.ConventionTests.Reporting +{ + using System.Reflection; + using TestStack.ConventionTests.Internal; + + public class MethodInfoDataFormatter : IReportDataFormatter + { + public bool CanFormat(object failingData) + { + return failingData is MethodInfo; + } + + public ConventionReportFailure Format(object failingData) + { + var methodInfo = (MethodInfo)failingData; + + return new ConventionReportFailure(methodInfo.DeclaringType + "." + methodInfo.Name); + } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Reporting/ProjectFileFormatter.cs b/TestStack.ConventionTests/Reporting/ProjectFileFormatter.cs new file mode 100644 index 0000000..61667b2 --- /dev/null +++ b/TestStack.ConventionTests/Reporting/ProjectFileFormatter.cs @@ -0,0 +1,18 @@ +namespace TestStack.ConventionTests.Reporting +{ + using TestStack.ConventionTests.ConventionData; + using TestStack.ConventionTests.Internal; + + public class ProjectFileFormatter : IReportDataFormatter + { + public bool CanFormat(object failingData) + { + return failingData is ProjectFile; + } + + public ConventionReportFailure Format(object failingData) + { + return new ConventionReportFailure(((ProjectFile)failingData).FilePath); + } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Reporting/ProjectReferenceFormatter.cs b/TestStack.ConventionTests/Reporting/ProjectReferenceFormatter.cs new file mode 100644 index 0000000..1481849 --- /dev/null +++ b/TestStack.ConventionTests/Reporting/ProjectReferenceFormatter.cs @@ -0,0 +1,18 @@ +namespace TestStack.ConventionTests.Reporting +{ + using TestStack.ConventionTests.ConventionData; + using TestStack.ConventionTests.Internal; + + public class ProjectReferenceFormatter : IReportDataFormatter + { + public bool CanFormat(object failingData) + { + return failingData is ProjectReference; + } + + public ConventionReportFailure Format(object failingData) + { + return new ConventionReportFailure(((ProjectReference)failingData).ReferencedPath); + } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Reporting/StringDataFormatter.cs b/TestStack.ConventionTests/Reporting/StringDataFormatter.cs new file mode 100644 index 0000000..ab70c55 --- /dev/null +++ b/TestStack.ConventionTests/Reporting/StringDataFormatter.cs @@ -0,0 +1,17 @@ +namespace TestStack.ConventionTests.Reporting +{ + using TestStack.ConventionTests.Internal; + + public class StringDataFormatter : IReportDataFormatter + { + public bool CanFormat(object failingData) + { + return failingData is string; + } + + public ConventionReportFailure Format(object failingData) + { + return new ConventionReportFailure((string)failingData); + } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Reporting/TestResult.cs b/TestStack.ConventionTests/Reporting/TestResult.cs new file mode 100644 index 0000000..4090591 --- /dev/null +++ b/TestStack.ConventionTests/Reporting/TestResult.cs @@ -0,0 +1,8 @@ +namespace TestStack.ConventionTests.Reporting +{ + public enum TestResult + { + Passed, + Failed + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Reporting/TypeDataFormatter.cs b/TestStack.ConventionTests/Reporting/TypeDataFormatter.cs new file mode 100644 index 0000000..d1b2cd2 --- /dev/null +++ b/TestStack.ConventionTests/Reporting/TypeDataFormatter.cs @@ -0,0 +1,18 @@ +namespace TestStack.ConventionTests.Reporting +{ + using System; + using TestStack.ConventionTests.Internal; + + public class TypeDataFormatter : IReportDataFormatter + { + public bool CanFormat(object failingData) + { + return failingData is Type; + } + + public ConventionReportFailure Format(object failingData) + { + return new ConventionReportFailure(((Type)failingData).FullName); + } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/TestStack.ConventionTests.csproj b/TestStack.ConventionTests/TestStack.ConventionTests.csproj index 5f2b876..8235b14 100644 --- a/TestStack.ConventionTests/TestStack.ConventionTests.csproj +++ b/TestStack.ConventionTests/TestStack.ConventionTests.csproj @@ -52,6 +52,7 @@ + @@ -60,17 +61,27 @@ + + + + + + + + + - + + @@ -85,6 +96,12 @@ + + + + + +