Skip to content
Open
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
3 changes: 3 additions & 0 deletions .csharpierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# XmlViolationStore rewrites this file without a trailing newline on every test run; CSharpier's
# XML formatter requires one. Excluded so `csharpier check` doesn't flag the store's own output.
ArchUnitNETTests/ArchUnitNET/Storage/FrozenRules.xml
15 changes: 15 additions & 0 deletions ArchUnit.sln
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PropertyMemberAssembly", "T
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassAssembly", "TestAssemblies\ClassAssembly\ClassAssembly.csproj", "{2C04EB93-5AE0-474C-B328-145A734A5E2B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlicesTestAssembly", "TestAssemblies\SlicesTestAssembly\SlicesTestAssembly.csproj", "{E421FA12-BBB9-4A8B-BD29-8757836079A5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A780B3D6-C525-4270-ABF1-63BA65EA1593}"
ProjectSection(SolutionItems) = preProject
README.md = README.md
Expand Down Expand Up @@ -427,6 +429,18 @@ Global
{2C04EB93-5AE0-474C-B328-145A734A5E2B}.Release|x64.Build.0 = Release|Any CPU
{2C04EB93-5AE0-474C-B328-145A734A5E2B}.Release|x86.ActiveCfg = Release|Any CPU
{2C04EB93-5AE0-474C-B328-145A734A5E2B}.Release|x86.Build.0 = Release|Any CPU
{E421FA12-BBB9-4A8B-BD29-8757836079A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E421FA12-BBB9-4A8B-BD29-8757836079A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E421FA12-BBB9-4A8B-BD29-8757836079A5}.Debug|x64.ActiveCfg = Debug|Any CPU
{E421FA12-BBB9-4A8B-BD29-8757836079A5}.Debug|x64.Build.0 = Debug|Any CPU
{E421FA12-BBB9-4A8B-BD29-8757836079A5}.Debug|x86.ActiveCfg = Debug|Any CPU
{E421FA12-BBB9-4A8B-BD29-8757836079A5}.Debug|x86.Build.0 = Debug|Any CPU
{E421FA12-BBB9-4A8B-BD29-8757836079A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E421FA12-BBB9-4A8B-BD29-8757836079A5}.Release|Any CPU.Build.0 = Release|Any CPU
{E421FA12-BBB9-4A8B-BD29-8757836079A5}.Release|x64.ActiveCfg = Release|Any CPU
{E421FA12-BBB9-4A8B-BD29-8757836079A5}.Release|x64.Build.0 = Release|Any CPU
{E421FA12-BBB9-4A8B-BD29-8757836079A5}.Release|x86.ActiveCfg = Release|Any CPU
{E421FA12-BBB9-4A8B-BD29-8757836079A5}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -444,6 +458,7 @@ Global
{05F40E4D-94EC-4DF7-B0FA-1BCBFDF56088} = {B1191F18-91CB-4387-B775-A5EB64D3AC30}
{1D0187EB-9D04-4CF3-AE63-0C0E97FCB49C} = {B1191F18-91CB-4387-B775-A5EB64D3AC30}
{2C04EB93-5AE0-474C-B328-145A734A5E2B} = {B1191F18-91CB-4387-B775-A5EB64D3AC30}
{E421FA12-BBB9-4A8B-BD29-8757836079A5} = {B1191F18-91CB-4387-B775-A5EB64D3AC30}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D1A8274D-32D6-44DB-9BB3-1A5B273709AF}
Expand Down
1 change: 1 addition & 0 deletions ArchUnitNET/Domain/PlantUml/Export/PlantUmlSlice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ private StringBuilder BuildStringC4Style()
if (Namespace == null)
{
result.Append("Container(" + _name + ", " + _name + ")");
return result;
}

var namespc = Namespace.Remove(Namespace.Length - 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
]
},
{
"ArchRuleDescription": "Slices matching \"TestAssembly.Slices.(**)\" should not depend on each other",
"ArchRuleDescription": "Slices matching \"SlicesTestAssembly.MultipleSubnamespaces.(**)\" should not depend on each other",
"Violations": [
"Slice3",
"Slice3.Group1",
Expand Down
4 changes: 2 additions & 2 deletions ArchUnitNETTests/ArchUnitNET/Storage/FrozenRules.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
]
},
{
"ArchRuleDescription": "Slices matching \"TestAssembly.Slices.(**)\" should not depend on each other",
"ArchRuleDescription": "Slices matching \"SlicesTestAssembly.MultipleSubnamespaces.(**)\" should not depend on each other",
"Violations": [
"Slice3",
"Slice3.Group1",
Expand All @@ -30,7 +30,7 @@
]
},
{
"ArchRuleDescription": "Slices matching \"TestAssembly.Slices.(*)..\" should not depend on each other",
"ArchRuleDescription": "Slices matching \"SlicesTestAssembly.MultipleSubnamespaces.(*)..\" should not depend on each other",
"Violations": []
}
]
6 changes: 3 additions & 3 deletions ArchUnitNETTests/ArchUnitNET/Storage/FrozenRules.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
<FrozenRule ArchRule="Types that are &quot;ArchUnitNETTests.Fluent.FreezeTests+Violation&quot; or &quot;ArchUnitNETTests.Fluent.FreezeTests+Violation2&quot; should be public">
<Violation>ArchUnitNETTests.Fluent.FreezeTests+Violation</Violation>
</FrozenRule>
<FrozenRule ArchRule="Slices matching &quot;TestAssembly.Slices.(*)..&quot; should not depend on each other" />
<FrozenRule ArchRule="Slices matching &quot;SlicesTestAssembly.MultipleSubnamespaces.(*)..&quot; should not depend on each other" />
<FrozenRule ArchRule="Types that are &quot;ArchUnitNETTests.Fluent.FreezeTests+Violation&quot; or &quot;ArchUnitNETTests.Fluent.FreezeTests+Violation2&quot; should not be private">
<Violation>ArchUnitNETTests.Fluent.FreezeTests+Violation</Violation>
<Violation>ArchUnitNETTests.Fluent.FreezeTests+Violation2</Violation>
</FrozenRule>
<FrozenRule ArchRule="Types that are &quot;ArchUnitNETTests.Fluent.FreezeTests+Violation&quot; should be protected">
<Violation>ArchUnitNETTests.Fluent.FreezeTests+Violation</Violation>
</FrozenRule>
<FrozenRule ArchRule="Slices matching &quot;TestAssembly.Slices.(**)&quot; should not depend on each other">
<FrozenRule ArchRule="Slices matching &quot;SlicesTestAssembly.MultipleSubnamespaces.(**)&quot; should not depend on each other">
<Violation>Slice3</Violation>
<Violation>Slice3.Group1</Violation>
<Violation>Slice2</Violation>
<Violation>Slice2.Service</Violation>
<Violation>Slice1</Violation>
<Violation>Slice1.Service</Violation>
</FrozenRule>
</FrozenRules>
</FrozenRules>
1 change: 1 addition & 0 deletions ArchUnitNETTests/ArchUnitNETTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<ProjectReference Include="..\TestAssemblies\MethodMemberAssembly\MethodMemberAssembly.csproj" />
<ProjectReference Include="..\TestAssemblies\PropertyMemberAssembly\PropertyMemberAssembly.csproj" />
<ProjectReference Include="..\TestAssemblies\ClassAssembly\ClassAssembly.csproj" />
<ProjectReference Include="..\TestAssemblies\SlicesTestAssembly\SlicesTestAssembly.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
Expand Down
62 changes: 58 additions & 4 deletions ArchUnitNETTests/AssemblyTestHelper/AssemblyTestHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,26 @@ namespace ArchUnitNETTests.AssemblyTestHelper;
public abstract class AssemblyTestHelper
{
private readonly StringBuilder _snapshot = new StringBuilder();
private bool _recordSnapshot = true;

public readonly string NonExistentObjectName = "NotTheNameOfAnyObject";

public abstract Architecture Architecture { get; }

public AssemblyTestHelper WithoutSnapshot()
{
_recordSnapshot = false;
return this;
}

private void Record(string output)
{
if (_recordSnapshot)
{
_snapshot.Append(output);
}
}

public void AddSnapshotHeader(string header)
{
_snapshot.AppendLine("===== " + header + " =====\n");
Expand Down Expand Up @@ -62,15 +77,45 @@ private static bool HasNoRealResults(IEnumerable<EvaluationResult> results)
return results.Any(result => result.EvaluatedObject is ICanBeEvaluated);
}

/// <summary>
/// Asserts the rule's verdict -- <see cref="IArchRule.HasNoViolations" /> -- rather than the
/// shape of its results. The distinction matters for
/// <see cref="CombinedArchRule" />: its verdict applies the conjunction, while its
/// <see cref="CombinedArchRule.Evaluate" /> merely concatenates both operands' results and
/// never applies it. A passing <c>Or</c> whose first operand fails therefore has failing
/// results, which is why this assertion cannot be expressed as
/// <c>results.All(r =&gt; r.Passed)</c>, and why <see cref="AssertAnyViolations" /> and
/// <see cref="AssertOnlyViolations" /> -- which do read the results -- say nothing about a
/// combinator.
/// </summary>
public void AssertNoViolations(IArchRule rule)
{
var results = rule.Evaluate(Architecture).ToList();
var output = FormatSnapshot(rule, results);
if (!results.All(result => result.Passed))
if (!rule.HasNoViolations(Architecture))
{
Assert.Fail(output);
}
_snapshot.Append(output);
Record(output);
}

/// <summary>
/// The exact negation of <see cref="AssertNoViolations" />, and like it a statement about the
/// verdict, not the results -- see there for why the two differ. Use it wherever the point of
/// the test is that a rule fails as a whole; prefer <see cref="AssertOnlyViolations" /> when
/// the point is that every evaluated object was reported, since this assertion is also
/// satisfied by the failing placeholder <see cref="ArchRule{TRuleType}.Evaluate" /> injects
/// for a rule whose input set is empty.
/// </summary>
public void AssertHasViolations(IArchRule rule)
{
var results = rule.Evaluate(Architecture).ToList();
var output = FormatSnapshot(rule, results);
if (rule.HasNoViolations(Architecture))
{
Assert.Fail(output);
}
Record(output);
}

public void AssertAnyViolations(IArchRule rule)
Expand All @@ -85,7 +130,7 @@ public void AssertAnyViolations(IArchRule rule)
{
Assert.Fail(output);
}
_snapshot.Append(output);
Record(output);
}

public void AssertOnlyViolations(IArchRule rule)
Expand All @@ -109,7 +154,7 @@ public void AssertOnlyViolations(IArchRule rule, bool allowNoResults)
+ output
);
}
_snapshot.Append(output);
Record(output);
}

public void AssertException<T>(IArchRule rule)
Expand All @@ -123,6 +168,15 @@ public void AssertException<T>(IArchRule rule)

public Task AssertSnapshotMatches([CallerFilePath] string sourceFile = "")
{
if (!_recordSnapshot)
{
Assert.Fail(
"This helper was created with WithoutSnapshot(), so nothing was recorded and the "
+ "snapshot would verify as empty. Drop the WithoutSnapshot() call or the "
+ "AssertSnapshotMatches() call."
);
}

return Verifier
.Verify(_snapshot.ToString(), null, sourceFile)
.DisableDiff() // Don't open diff tool during the test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ public static void AssertNoViolations(this IArchRule archRule, AssemblyTestHelpe
testHelper.AssertNoViolations(archRule);
}

public static void AssertHasViolations(this IArchRule archRule, AssemblyTestHelper testHelper)
{
testHelper.AssertHasViolations(archRule);
}

public static void AssertAnyViolations(this IArchRule archRule, AssemblyTestHelper testHelper)
{
testHelper.AssertAnyViolations(archRule);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using ArchUnitNET.Domain;

namespace ArchUnitNETTests.AssemblyTestHelper;

public class SlicesAssemblyTestHelper : AssemblyTestHelper
{
public sealed override Architecture Architecture =>
StaticTestArchitectures.SlicesTestArchitecture;
}
8 changes: 8 additions & 0 deletions ArchUnitNETTests/Domain/PlantUml/PlantUmlFileBuilderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ public void BuildUmlByDependenciesWithObjectsWithNoDependenciesTest()
Assert.Equal(expectedUml, uml);
}

[Fact]
public void SliceWithHyperlinkAppendsHyperlinkTest()
{
var slice = new PlantUmlSlice("Slice1", hyperlink: "https://example.com");
var uml = slice.GetPlantUmlString(new RenderOptions());
Assert.Equal("[Slice1] [[https://example.com]] " + Environment.NewLine, uml);
}

[Fact]
public void HandleIllegalComponentNamesTest()
{
Expand Down
Loading
Loading