diff --git a/TestStack.ConventionTests.Tests/ConventionAssertionClassTests.cs b/TestStack.ConventionTests.Tests/ConventionAssertionClassTests.cs index 6fd3730..a68836c 100644 --- a/TestStack.ConventionTests.Tests/ConventionAssertionClassTests.cs +++ b/TestStack.ConventionTests.Tests/ConventionAssertionClassTests.cs @@ -35,9 +35,9 @@ public ConventionReportFailure Format(string failingData) public class FailingConvention : IConvention { public string ConventionTitle { get { return "Header"; } } - public IEnumerable GetFailingData(FakeData data) + public void Execute(FakeData data, IConventionResult result) { - return new[] { "Different" }; + result.Is(new[] {"Different"}); } } } diff --git a/TestStack.ConventionTests.Tests/TypeBasedConventions.cs b/TestStack.ConventionTests.Tests/TypeBasedConventions.cs index 5f3c2d4..a2af0c3 100644 --- a/TestStack.ConventionTests.Tests/TypeBasedConventions.cs +++ b/TestStack.ConventionTests.Tests/TypeBasedConventions.cs @@ -26,7 +26,9 @@ public TypeBasedConventions() [Test] public void all_classes_have_default_constructor() { - var ex = Assert.Throws(()=>Convention.Is(new AllClassesHaveDefaultConstructor(), nhibernateEntities)); + var ex = + Assert.Throws( + () => Convention.Is(new AllClassesHaveDefaultConstructor(), nhibernateEntities)); Approvals.Verify(ex.Message); } @@ -40,7 +42,9 @@ public void all_classes_have_default_constructor_wth_approved_exceptions() [Test] public void all_methods_are_virtual() { - var ex = Assert.Throws(()=>Convention.Is(new AllMethodsAreVirtual(), nhibernateEntities)); + var ex = + Assert.Throws( + () => Convention.Is(new AllMethodsAreVirtual(), nhibernateEntities)); Approvals.Verify(ex.Message); } @@ -56,11 +60,11 @@ public void dtos_exists_in_dto_namespace() { var types = new Types("TestAssembly types") { - TypesToVerify = new[] { typeof(SomeDto), typeof(BlahDto), typeof(AnotherClass)} + TypesToVerify = new[] {typeof (SomeDto), typeof (BlahDto), typeof (AnotherClass)} }; var convention = new ClassTypeHasSpecificNamespace(t => t.Name.EndsWith("Dto"), "TestAssembly.Dtos", "Dto"); - var ex = Assert.Throws(() =>Convention.Is(convention, types)); + var ex = Assert.Throws(() => Convention.Is(convention, types)); Approvals.Verify(ex.Message); } @@ -69,7 +73,7 @@ public void dtos_exists_in_dto_namespace_wth_approved_exceptions() { var types = new Types("TestAssembly types") { - TypesToVerify = new[] { typeof(SomeDto), typeof(BlahDto), typeof(AnotherClass) } + TypesToVerify = new[] {typeof (SomeDto), typeof (BlahDto), typeof (AnotherClass)} }; var convention = new ClassTypeHasSpecificNamespace(t => t.Name.EndsWith("Dto"), "TestAssembly.Dtos", "Dto"); diff --git a/TestStack.ConventionTests/Convention.Generic.cs b/TestStack.ConventionTests/Convention.Generic.cs new file mode 100644 index 0000000..5dc597d --- /dev/null +++ b/TestStack.ConventionTests/Convention.Generic.cs @@ -0,0 +1,20 @@ +namespace TestStack.ConventionTests +{ + using System; + using System.Collections.Generic; + + public abstract class Convention : IConvention where TData : IConventionData + { + public abstract string ConventionTitle { get; } + + public virtual void Execute(TData data, IConventionResult result) + { + result.Is(Execute(data)); + } + + protected virtual IEnumerable Execute(TData data) + { + throw new NotImplementedException("You need to overwrite the Execute method"); + } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Convention.cs b/TestStack.ConventionTests/Convention.cs index 606d671..1c1a91c 100644 --- a/TestStack.ConventionTests/Convention.cs +++ b/TestStack.ConventionTests/Convention.cs @@ -28,49 +28,46 @@ static Convention() }; } - public static IEnumerable ConventionReports { get { return Reports; } } - public static IList Formatters { get; set; } - - public static void Is(IConvention convention, TDataSource data) - where TDataSource : IConventionData + public static IEnumerable ConventionReports { - Is(convention, data, new ConventionResultExceptionReporter()); + get { return Reports; } } - 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); + public static IList Formatters { get; set; } - new ConventionReportTraceRenderer().Render(conventionResult); - reporter.Render(conventionResult); - } - finally + static string AssemblyDirectory + { + get { - 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 + var codeBase = Assembly.GetExecutingAssembly().CodeBase; + var uri = new UriBuilder(codeBase); + var path = Uri.UnescapeDataString(uri.Path); + return Path.GetDirectoryName(path); } } - public static void IsWithApprovedExeptions(IConvention convention, TDataSource data) + public static void Is(IConvention convention, TDataSource data) where TDataSource : IConventionData { - var conventionResult = Executor.GetConventionReportWithApprovedExeptions(convention.ConventionTitle, convention.GetFailingData(data).ToArray(), data); - Reports.Add(conventionResult); + Is(convention, data, new ConventionResultExceptionReporter()); + } + public static void Is(IConvention convention, TDataSource data, + IConventionReportRenderer reporter) + where TDataSource : IConventionData + { try { - var conventionReportTextRenderer = new ConventionReportTextRenderer(); - conventionReportTextRenderer.Render(conventionResult); - Approvals.Verify(conventionReportTextRenderer.Output); + // we want to run that first so that we don't even bother running the convention if theere's no data + // conveniton author can assume that data is available. That will simplify the conventions + if (!data.HasData) + throw new ConventionSourceInvalidException(string.Format("{0} has no data", data.Description)); - new ConventionReportTraceRenderer().Render(conventionResult); - } - catch (ApprovalException ex) - { - throw new ConventionFailedException("Approved exceptions for convention differs\r\n\r\n"+ex.Message, ex); + var result = new ConventionContext(data, Formatters, reporter, new ConventionReportTraceRenderer()); + + convention.Execute(data, result); + Render(convention, data, result, Executor.GetConventionReport); } finally { @@ -78,49 +75,52 @@ public static void IsWithApprovedExeptions(IConvention } } - 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 + //ResultInfo GetConventionReport(string conventionTitle, IConventionData data, IEnumerable items, bool failed) + static void Render(IConvention convention, TDataSource data, ConventionContext result, + Func, ResultInfo> getConventionReport) where TDataSource : IConventionData { - try + if (result.IsSymmetricResult) { - 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); + var result1 = getConventionReport(result.FirstDescription, + result, result.FirstOnly); + var result2 = getConventionReport(result.SecondDescription, + result, result.SecondOnly); + Reports.Add(result1); + Reports.Add(result2); + + foreach (var renderer in result.Renderers) + { + renderer.Render(result1, result2); + } } - finally + else { - HtmlRenderer.Render(Reports.ToArray()); + var conventionResult = getConventionReport(convention.ConventionTitle, result, result.Items); + Reports.Add(conventionResult); + + foreach (var renderer in result.Renderers) + { + renderer.Render(conventionResult); + } } } - public static void IsWithApprovedExeptions(ISymmetricConvention convention, TDataSource data) + public static void IsWithApprovedExeptions(IConvention 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); + // we want to run that first so that we don't even bother running the convention if theere's no data + // conveniton author can assume that data is available. That will simplify the conventions + if (!data.HasData) + throw new ConventionSourceInvalidException(string.Format("{0} has no data", data.Description)); + var conventionReportTextRenderer = new ConventionReportTextRenderer(); + var result = new ConventionContext(data, Formatters, conventionReportTextRenderer, new ConventionReportTraceRenderer()); + convention.Execute(data, result); + + Render(convention, data, result, Executor.GetConventionReportWithApprovedExeptions); 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) { @@ -131,17 +131,5 @@ public static void IsWithApprovedExeptions(ISymmetricConvention + public class AllClassesHaveDefaultConstructor : Convention { - public string ConventionTitle { get { return "Types must have a default constructor"; } } + public override string ConventionTitle + { + get { return "Types must have a default constructor"; } + } - public IEnumerable GetFailingData(Types data) + protected override IEnumerable Execute(Types data) { return data.TypesToVerify.Where(t => t.HasDefaultConstructor() == false); } diff --git a/TestStack.ConventionTests/Conventions/AllMethodsAreVirtual.cs b/TestStack.ConventionTests/Conventions/AllMethodsAreVirtual.cs index dcf659e..0dd9144 100644 --- a/TestStack.ConventionTests/Conventions/AllMethodsAreVirtual.cs +++ b/TestStack.ConventionTests/Conventions/AllMethodsAreVirtual.cs @@ -2,15 +2,17 @@ { using System.Collections.Generic; using System.Linq; - using System.Reflection; using TestStack.ConventionTests.ConventionData; using TestStack.ConventionTests.Internal; - public class AllMethodsAreVirtual : IConvention + public class AllMethodsAreVirtual : Convention { - public string ConventionTitle { get { return "Methods must be virtual"; } } + public override string ConventionTitle + { + get { return "Methods must be virtual"; } + } - public IEnumerable GetFailingData(Types data) + protected override IEnumerable Execute(Types data) { return data.TypesToVerify.SelectMany(t => t.NonVirtualMethods()); } diff --git a/TestStack.ConventionTests/Conventions/ClassTypeHasSpecificNamespace.cs b/TestStack.ConventionTests/Conventions/ClassTypeHasSpecificNamespace.cs index d109d37..7643849 100644 --- a/TestStack.ConventionTests/Conventions/ClassTypeHasSpecificNamespace.cs +++ b/TestStack.ConventionTests/Conventions/ClassTypeHasSpecificNamespace.cs @@ -1,66 +1,53 @@ namespace TestStack.ConventionTests.Conventions { using System; - using System.Collections.Generic; - using System.Linq; using TestStack.ConventionTests.ConventionData; /// - /// This convention allows you to enforce a particular type of class is under a namespace, for instance. - /// - /// Dto must be under App.Contracts.Dtos - /// Domain Objects must be under App.Domain - /// Event Handlers must be under App.Handlers - /// - /// 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 + /// This convention allows you to enforce a particular type of class is under a namespace, for instance. + /// Dto must be under App.Contracts.Dtos + /// Domain Objects must be under App.Domain + /// Event Handlers must be under App.Handlers + /// 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 : ISymmetricConvention + public class ClassTypeHasSpecificNamespace : IConvention { readonly Func classIsApplicable; - readonly string namespaceToCheck; readonly string classType; + readonly string namespaceToCheck; /// - /// Ctor + /// Ctor /// /// Predicate to verify if the class is the right type /// /// The class type. ie, Dto, Domain Object, Event Handler - public ClassTypeHasSpecificNamespace(Func classIsApplicable, string namespaceToCheck, string classType) + public ClassTypeHasSpecificNamespace(Func classIsApplicable, string namespaceToCheck, + string classType) { this.classIsApplicable = classIsApplicable; this.namespaceToCheck = namespaceToCheck; this.classType = classType; } - public string ConventionTitle - { - get - { - return string.Format("{0}s must be under the '{1}' namespace", classType, namespaceToCheck); - } - } - public string InverseTitle { - get - { - return string.Format("Non-{0}s must not be under the '{1}' namespace", classType, namespaceToCheck); - } + get { return string.Format("Non-{0}s must not be under the '{1}' namespace", classType, namespaceToCheck); } } - public IEnumerable GetFailingData(Types data) + public string ConventionTitle { - return data.TypesToVerify - .Where(classIsApplicable) - .Where(t => t.Namespace == null || !t.Namespace.StartsWith(namespaceToCheck)); + get { return string.Format("{0}s must be under the '{1}' namespace", classType, namespaceToCheck); } } - public IEnumerable GetFailingInverseData(Types data) + public void Execute(Types data, IConventionResult result) { - return data.TypesToVerify - .Where(t => !classIsApplicable(t)) - .Where(t => t.Namespace != null && t.Namespace.StartsWith(namespaceToCheck)); + result.IsSymmetric(data.TypesToVerify, + classIsApplicable, + t => t.Namespace != null && t.Namespace.StartsWith(namespaceToCheck), + ConventionTitle, + InverseTitle); } } } \ No newline at end of file diff --git a/TestStack.ConventionTests/Conventions/FilesAreEmbeddedResources.cs b/TestStack.ConventionTests/Conventions/FilesAreEmbeddedResources.cs index 88fc915..05f7757 100644 --- a/TestStack.ConventionTests/Conventions/FilesAreEmbeddedResources.cs +++ b/TestStack.ConventionTests/Conventions/FilesAreEmbeddedResources.cs @@ -4,24 +4,21 @@ using System.Linq; using TestStack.ConventionTests.ConventionData; - public class FilesAreEmbeddedResources : IConvention + public class FilesAreEmbeddedResources : Convention { public FilesAreEmbeddedResources(string fileExtension) { FileExtension = fileExtension; } - public string ConventionTitle + public string FileExtension { get; set; } + + public override string ConventionTitle { - get - { - return string.Format("{0} Files must be embedded resources", FileExtension); - } + get { return string.Format("{0} Files must be embedded resources", FileExtension); } } - public string FileExtension { get; set; } - - public IEnumerable GetFailingData(ProjectFiles data) + protected override IEnumerable Execute(ProjectFiles data) { return data.Files.Where(s => s.FilePath.EndsWith(FileExtension) && s.ReferenceType != "EmbeddedResource"); } diff --git a/TestStack.ConventionTests/Conventions/ISymmetricConvention.cs b/TestStack.ConventionTests/Conventions/ISymmetricConvention.cs deleted file mode 100644 index e7454bf..0000000 --- a/TestStack.ConventionTests/Conventions/ISymmetricConvention.cs +++ /dev/null @@ -1,12 +0,0 @@ -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 04d9fdb..c2eaf13 100644 --- a/TestStack.ConventionTests/Conventions/ProjectDoesNotReferenceDllsFromBinOrObjDirectories.cs +++ b/TestStack.ConventionTests/Conventions/ProjectDoesNotReferenceDllsFromBinOrObjDirectories.cs @@ -5,20 +5,23 @@ using System.Text.RegularExpressions; using TestStack.ConventionTests.ConventionData; - public class ProjectDoesNotReferenceDllsFromBinOrObjDirectories : IConvention + public class ProjectDoesNotReferenceDllsFromBinOrObjDirectories : Convention { const string AssemblyReferencingObjRegex = @"^(?.*?(obj|bin).*?)$"; - static bool IsBinOrObjReference(ProjectReference reference) + public override string ConventionTitle { - return Regex.IsMatch(reference.ReferencedPath, AssemblyReferencingObjRegex, RegexOptions.IgnoreCase); + get { return "Project must not reference dlls from bin or obj directories"; } } - public string ConventionTitle { get { return "Project must not reference dlls from bin or obj directories"; } } - - public IEnumerable GetFailingData(ProjectReferences data) + protected override IEnumerable Execute(ProjectReferences data) { return data.References.Where(IsBinOrObjReference); } + + static bool IsBinOrObjReference(ProjectReference reference) + { + return Regex.IsMatch(reference.ReferencedPath, AssemblyReferencingObjRegex, RegexOptions.IgnoreCase); + } } } \ No newline at end of file diff --git a/TestStack.ConventionTests/IConvention.cs b/TestStack.ConventionTests/IConvention.cs index 468fdf6..9f76f3f 100644 --- a/TestStack.ConventionTests/IConvention.cs +++ b/TestStack.ConventionTests/IConvention.cs @@ -1,10 +1,8 @@ namespace TestStack.ConventionTests { - using System.Collections.Generic; - - public interface IConvention where T : IConventionData + public interface IConvention where TData : IConventionData { string ConventionTitle { get; } - IEnumerable GetFailingData(T data); + void Execute(TData data, IConventionResult result); } } \ No newline at end of file diff --git a/TestStack.ConventionTests/IConventionResult.cs b/TestStack.ConventionTests/IConventionResult.cs new file mode 100644 index 0000000..06223a8 --- /dev/null +++ b/TestStack.ConventionTests/IConventionResult.cs @@ -0,0 +1,26 @@ +namespace TestStack.ConventionTests +{ + using System; + using System.Collections.Generic; + using System.Text; + + public interface IConventionResult + { + void Is(IEnumerable items); + + void Is( + IEnumerable items, + Action itemDescriptor); + + void Is( + IEnumerable items, + Func itemDescriptor); + + void IsSymmetric( + IEnumerable items, + Func firstPredicate, + Func secondPredicate, + string firstDescription = null, + string secondDescription = null); + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Internal/ConventionContext.cs b/TestStack.ConventionTests/Internal/ConventionContext.cs new file mode 100644 index 0000000..7065945 --- /dev/null +++ b/TestStack.ConventionTests/Internal/ConventionContext.cs @@ -0,0 +1,188 @@ +namespace TestStack.ConventionTests.Internal +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using System.Text; + using TestStack.ConventionTests.Reporting; + + public class ConventionContext : IConventionResult, IConventionContext + { + readonly ICollection formatters; + readonly ICollection renderers; + + public ConventionContext(IConventionData data, ICollection formatters, + params IConventionReportRenderer[] renderers) + { + Data = data; + this.formatters = formatters; + this.renderers = renderers; + } + + public string Message { get; private set; } + + public IEnumerable Items { get; set; } + + public object[] SecondOnly { get; set; } + + public object[] FirstOnly { get; set; } + + public string SecondDescription { get; set; } + + public string FirstDescription { get; set; } + public bool IsSymmetricResult { get; set; } + + public IEnumerable Renderers + { + get { return renderers; } + } + + public IConventionData Data { get; private set; } + + public IEnumerable Formatters + { + get { return formatters; } + } + + void IConventionResult.Is(IEnumerable items) + { + Items = items.Select(item => (object) item).ToList(); + } + + public void Is( + IEnumerable items, + Action itemDescriptor) + { + var array = items.ToArray(); + if (array.None()) + { + return; + } + + // 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(); + message.AppendLine(); + message.AppendLine(); + Array.ForEach(array, r => itemDescriptor(r, message)); + Message = message.ToString(); + } + + public void Is( + IEnumerable items, + Func itemDescriptor) + { + Is(items, (item, message) => message.AppendLine(itemDescriptor(item))); + } + + public void IsSymmetric( + IEnumerable items, + Func firstPredicate, + Func secondPredicate, + string firstDescription = null, + string secondDescription = null) + { + IsSymmetricResult = true; + FirstDescription = firstDescription; + SecondDescription = secondDescription; + var array = items.ToArray(); + if (array.None()) + { + return; + } + FirstOnly = array.Where(firstPredicate).Where(i => secondPredicate(i) == false) + .Select(i => (object) i).ToArray(); + SecondOnly = array.Where(secondPredicate).Where(i => firstPredicate(i) == false) + .Select(i => (object) i).ToArray(); + } + + public void IsSymmetric( + string firstHeader, TResult[] firstResults, + string secondHeader, TResult[] secondResults, + Action itemDescriptor) + { + var firstArray = firstResults.ToArray(); + var secondArray = secondResults.ToArray(); + if (firstArray.None() && secondArray.None()) + { + return; + } + + 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)); + } + Message = message.ToString(); + } + + public void IsSymmetric( + string firstHeader, TResult[] firstResults, + string secondHeader, TResult[] secondResults, + Func itemDescriptor) + { + IsSymmetric( + firstHeader, firstResults, secondHeader, + secondResults, + (item, message) => message.AppendLine(itemDescriptor(item))); + } + } + + public class CsvReporter + { + public string Build(IEnumerable results, string header, DefaultFormatter formatter) + { + var message = new StringBuilder(); + message.AppendLine(string.Join(",", formatter.DesribeType())); + foreach (var result in results) + { + message.AppendLine(string.Join(",", formatter.DesribeItem(result))); + } + return message.ToString(); + } + } + + public class DefaultFormatter + { + readonly PropertyInfo[] properties; + readonly Type type; + + public DefaultFormatter(Type type) + { + this.type = type; + properties = type.GetProperties(); + } + + // TODO: this is a very crappy name for a method + public string[] DesribeType() + { + return properties.Select(Describe).ToArray(); + } + + string Describe(PropertyInfo property) + { + return property.Name.Replace('_', ' '); + } + + public string[] DesribeItem(object result) + { + return properties.Select(p => p.GetValue(result, null).ToString()).ToArray(); + } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/Internal/Executor.cs b/TestStack.ConventionTests/Internal/Executor.cs index c054e29..e883de6 100644 --- a/TestStack.ConventionTests/Internal/Executor.cs +++ b/TestStack.ConventionTests/Internal/Executor.cs @@ -1,29 +1,26 @@ namespace TestStack.ConventionTests.Internal { + using System.Collections.Generic; 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) + public static ResultInfo GetConventionReport(string conventionTitle, IConventionContext context, + IEnumerable items) { - 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, + items.None() ? TestResult.Passed : TestResult.Failed, conventionTitle, - data.Description, - failingData.Select(FormatData).ToArray()); + context.Data.Description, + items.Select(o => FormatData(o, context)).ToArray()); return conventionResult; } - public static ResultInfo GetConventionReportWithApprovedExeptions(string conventionTitle, object[] failingData, IConventionData data) + public static ResultInfo GetConventionReportWithApprovedExeptions(string conventionTitle, + IConventionContext context, IEnumerable items) { - var conventionResult = Executor.GetConventionReport(conventionTitle, failingData, data); + var conventionResult = GetConventionReport(conventionTitle, context, items); var conventionReportTextRenderer = new ConventionReportTextRenderer(); // Add approved exceptions to report conventionReportTextRenderer.RenderItems(conventionResult); @@ -32,12 +29,13 @@ public static ResultInfo GetConventionReportWithApprovedExeptions(string convent return conventionResult; } - static ConventionReportFailure FormatData(T failingData) + static ConventionReportFailure FormatData(T failingData, IConventionContext context) { - var formatter = Convention.Formatters.FirstOrDefault(f => f.CanFormat(failingData)); + var formatter = context.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());`"); + throw new NoDataFormatterFoundException(typeof (T).Name + + " has no formatter, add one with `Convention.Formatters.Add(new MyDataFormatter());`"); return formatter.Format(failingData); } diff --git a/TestStack.ConventionTests/Internal/IConventionContext.cs b/TestStack.ConventionTests/Internal/IConventionContext.cs new file mode 100644 index 0000000..7353de2 --- /dev/null +++ b/TestStack.ConventionTests/Internal/IConventionContext.cs @@ -0,0 +1,12 @@ +namespace TestStack.ConventionTests.Internal +{ + using System.Collections.Generic; + using TestStack.ConventionTests.Reporting; + + public interface IConventionContext + { + IEnumerable Formatters { get; } + IEnumerable Renderers { get; } + IConventionData Data { get; } + } +} \ No newline at end of file diff --git a/TestStack.ConventionTests/TestStack.ConventionTests.csproj b/TestStack.ConventionTests/TestStack.ConventionTests.csproj index 8235b14..2773b3e 100644 --- a/TestStack.ConventionTests/TestStack.ConventionTests.csproj +++ b/TestStack.ConventionTests/TestStack.ConventionTests.csproj @@ -61,8 +61,12 @@ + + + + @@ -75,7 +79,6 @@ -