Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ public ConventionReportFailure Format(string failingData)
public class FailingConvention : IConvention<FakeData>
{
public string ConventionTitle { get { return "Header"; } }
public IEnumerable<object> GetFailingData(FakeData data)
public void Execute(FakeData data, IConventionResult result)
{
return new[] { "Different" };
result.Is(new[] {"Different"});
}
}
}
Expand Down
14 changes: 9 additions & 5 deletions TestStack.ConventionTests.Tests/TypeBasedConventions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ public TypeBasedConventions()
[Test]
public void all_classes_have_default_constructor()
{
var ex = Assert.Throws<ConventionFailedException>(()=>Convention.Is(new AllClassesHaveDefaultConstructor(), nhibernateEntities));
var ex =
Assert.Throws<ConventionFailedException>(
() => Convention.Is(new AllClassesHaveDefaultConstructor(), nhibernateEntities));

Approvals.Verify(ex.Message);
}
Expand All @@ -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<ConventionFailedException>(()=>Convention.Is(new AllMethodsAreVirtual(), nhibernateEntities));
var ex =
Assert.Throws<ConventionFailedException>(
() => Convention.Is(new AllMethodsAreVirtual(), nhibernateEntities));

Approvals.Verify(ex.Message);
}
Expand All @@ -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<ConventionFailedException>(() =>Convention.Is(convention, types));
var ex = Assert.Throws<ConventionFailedException>(() => Convention.Is(convention, types));
Approvals.Verify(ex.Message);
}

Expand All @@ -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");

Expand Down
20 changes: 20 additions & 0 deletions TestStack.ConventionTests/Convention.Generic.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace TestStack.ConventionTests
{
using System;
using System.Collections.Generic;

public abstract class Convention<TData> : IConvention<TData> where TData : IConventionData
{
public abstract string ConventionTitle { get; }

public virtual void Execute(TData data, IConventionResult result)
{
result.Is(Execute(data));
}

protected virtual IEnumerable<object> Execute(TData data)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recon get rid of this, I would prefer that we just use result.Is for all conventions and not try to put this in for compatibility

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair enough

{
throw new NotImplementedException("You need to overwrite the Execute method");
}
}
}
130 changes: 59 additions & 71 deletions TestStack.ConventionTests/Convention.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,99 +28,99 @@ static Convention()
};
}

public static IEnumerable<ResultInfo> ConventionReports { get { return Reports; } }
public static IList<IReportDataFormatter> Formatters { get; set; }

public static void Is<TDataSource>(IConvention<TDataSource> convention, TDataSource data)
where TDataSource : IConventionData
public static IEnumerable<ResultInfo> ConventionReports
{
Is(convention, data, new ConventionResultExceptionReporter());
get { return Reports; }
}

public static void Is<TDataSource>(IConvention<TDataSource> 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<IReportDataFormatter> 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<TDataSource>(IConvention<TDataSource> convention, TDataSource data)
public static void Is<TDataSource>(IConvention<TDataSource> 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<TDataSource>(IConvention<TDataSource> 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
{
HtmlRenderer.Render(Reports.ToArray());
}
}

public static void Is<TDataSource>(ISymmetricConvention<TDataSource> convention, TDataSource data)
where TDataSource : IConventionData
{
Is(convention, data, new ConventionResultExceptionReporter());
}

public static void Is<TDataSource>(ISymmetricConvention<TDataSource> convention, TDataSource data, IConventionReportRenderer reporter)
where TDataSource : IConventionData
//ResultInfo GetConventionReport(string conventionTitle, IConventionData data, IEnumerable<object> items, bool failed)
static void Render<TDataSource>(IConvention<TDataSource> convention, TDataSource data, ConventionContext result,
Func<string, IConventionContext, IEnumerable<object>, 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<TDataSource>(ISymmetricConvention<TDataSource> convention, TDataSource data)
public static void IsWithApprovedExeptions<TDataSource>(IConvention<TDataSource> 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)
{
Expand All @@ -131,17 +131,5 @@ public static void IsWithApprovedExeptions<TDataSource>(ISymmetricConvention<TDa
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);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
using TestStack.ConventionTests.ConventionData;
using TestStack.ConventionTests.Internal;

public class AllClassesHaveDefaultConstructor : IConvention<Types>
public class AllClassesHaveDefaultConstructor : Convention<Types>
{
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<object> GetFailingData(Types data)
protected override IEnumerable<object> Execute(Types data)
{
return data.TypesToVerify.Where(t => t.HasDefaultConstructor() == false);
}
Expand Down
10 changes: 6 additions & 4 deletions TestStack.ConventionTests/Conventions/AllMethodsAreVirtual.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Types>
public class AllMethodsAreVirtual : Convention<Types>
{
public string ConventionTitle { get { return "Methods must be virtual"; } }
public override string ConventionTitle
{
get { return "Methods must be virtual"; }
}

public IEnumerable<object> GetFailingData(Types data)
protected override IEnumerable<object> Execute(Types data)
{
return data.TypesToVerify.SelectMany(t => t.NonVirtualMethods());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,66 +1,53 @@
namespace TestStack.ConventionTests.Conventions
{
using System;
using System.Collections.Generic;
using System.Linq;
using TestStack.ConventionTests.ConventionData;

/// <summary>
/// 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
/// </summary>
public class ClassTypeHasSpecificNamespace : ISymmetricConvention<Types>
public class ClassTypeHasSpecificNamespace : IConvention<Types>
{
readonly Func<Type, bool> classIsApplicable;
readonly string namespaceToCheck;
readonly string classType;
readonly string namespaceToCheck;

/// <summary>
/// Ctor
/// Ctor
/// </summary>
/// <param name="classIsApplicable">Predicate to verify if the class is the right type</param>
/// <param name="namespaceToCheck"></param>
/// <param name="classType">The class type. ie, Dto, Domain Object, Event Handler</param>
public ClassTypeHasSpecificNamespace(Func<Type, bool> classIsApplicable, string namespaceToCheck, string classType)
public ClassTypeHasSpecificNamespace(Func<Type, bool> 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<object> 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<object> 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);
}
}
}
Loading