diff --git a/src/Core/Conventional.Tests/AllAssembliesScenarios.cs b/src/Core/Conventional.Tests/AllAssembliesScenarios.cs
index d2194a9..ef3fd82 100644
--- a/src/Core/Conventional.Tests/AllAssembliesScenarios.cs
+++ b/src/Core/Conventional.Tests/AllAssembliesScenarios.cs
@@ -10,7 +10,7 @@ public void GivenAPattern_LocatesAndReturnsAllAssembliesForThatPattern()
{
var assemblySpecimen = AllAssemblies.WithNamesMatching("*");
- assemblySpecimen.Should().HaveCount(5);
+ assemblySpecimen.Should().HaveCount(6);
}
}
}
\ No newline at end of file
diff --git a/src/Core/Conventional.Tests/Conventional/Conventions/Assemblies/AssemblyConventionSpecificationTests.cs b/src/Core/Conventional.Tests/Conventional/Conventions/Assemblies/AssemblyConventionSpecificationTests.cs
index de1592b..74bac29 100644
--- a/src/Core/Conventional.Tests/Conventional/Conventions/Assemblies/AssemblyConventionSpecificationTests.cs
+++ b/src/Core/Conventional.Tests/Conventional/Conventions/Assemblies/AssemblyConventionSpecificationTests.cs
@@ -241,5 +241,45 @@ public void MustNotIncludeProjectReferences_Failure()
result.IsSatisfied.Should().BeFalse();
result.Failures.Single().Should().StartWith("Conventional.Tests includes reference to project");
}
+
+ [Test]
+ public void MustReferencePackage_Success()
+ {
+ var result = TheAssembly
+ .WithNameMatching("SdkClassLibrary1")
+ .MustConformTo(Convention.MustReferencePackage("coverlet.collector"));
+
+ result.IsSatisfied.Should().BeTrue();
+ }
+
+ [Test]
+ public void MustReferencePackage_Failure()
+ {
+ var result = TheAssembly
+ .WithNameMatching("SdkClassLibrary1")
+ .MustConformTo(Convention.MustReferencePackage("koverlet.kollector"));
+
+ result.IsSatisfied.Should().BeFalse();
+ }
+
+ [Test]
+ public void MustNotReferencePackage_Success()
+ {
+ var result = TheAssembly
+ .WithNameMatching("SdkClassLibrary1")
+ .MustConformTo(Convention.MustNotReferencePackage("foo.bar.baz"));
+
+ result.IsSatisfied.Should().BeTrue();
+ }
+
+ [Test]
+ public void MustNotReferencePackage_Failure()
+ {
+ var result = TheAssembly
+ .WithNameMatching("SdkClassLibrary1")
+ .MustConformTo(Convention.MustNotReferencePackage("coverlet.collector"));
+
+ result.IsSatisfied.Should().BeFalse();
+ }
}
}
\ No newline at end of file
diff --git a/src/Core/Conventional/Convention.Assembly.cs b/src/Core/Conventional/Convention.Assembly.cs
index 2b7eab8..7803d38 100644
--- a/src/Core/Conventional/Convention.Assembly.cs
+++ b/src/Core/Conventional/Convention.Assembly.cs
@@ -88,5 +88,25 @@ public static MustBeIncludedInSetOfAssembliesConventionSpecification MustBeInclu
/// This convention is unaware of shared build prop files (Directory.Build.Props + Directory.Build.Targets) - see https://github.com/andrewabest/Conventional/issues/88
public static MustNotIncludeProjectReferencesConventionSpecification MustNotIncludeProjectReferences =>
new MustNotIncludeProjectReferencesConventionSpecification();
+
+ ///
+ /// Requires that this assembly (project) references the specified package
+ ///
+ /// The name of the package that must be referenced
+ /// This convention is unaware of shared build prop files (Directory.Build.Props + Directory.Build.Targets) - see https://github.com/andrewabest/Conventional/issues/88
+ public static MustReferencePackageAssemblyConventionSpecification MustReferencePackage(string packageName)
+ {
+ return new MustReferencePackageAssemblyConventionSpecification(packageName);
+ }
+
+ ///
+ /// Requires that this assembly (project) does not reference the specified package
+ ///
+ /// The name of the package that must not be referenced
+ /// This convention is unaware of shared build prop files (Directory.Build.Props + Directory.Build.Targets) - see https://github.com/andrewabest/Conventional/issues/88
+ public static MustNotReferencePackageAssemblyConventionSpecification MustNotReferencePackage(string packageName)
+ {
+ return new MustNotReferencePackageAssemblyConventionSpecification(packageName);
+ }
}
}
\ No newline at end of file
diff --git a/src/Core/Conventional/Conventions/Assemblies/MustNotReferencePackageAssemblyConventionSpecification.cs b/src/Core/Conventional/Conventions/Assemblies/MustNotReferencePackageAssemblyConventionSpecification.cs
new file mode 100644
index 0000000..ccdf475
--- /dev/null
+++ b/src/Core/Conventional/Conventions/Assemblies/MustNotReferencePackageAssemblyConventionSpecification.cs
@@ -0,0 +1,27 @@
+using System.Linq;
+using System.Xml.Linq;
+
+namespace Conventional.Conventions.Assemblies
+{
+ public class MustNotReferencePackageAssemblyConventionSpecification : PackageReferenceAssemblyConventionSpecification
+ {
+ private readonly string _needlePackage;
+
+ public MustNotReferencePackageAssemblyConventionSpecification(string needlePackage)
+ {
+ _needlePackage = needlePackage;
+ }
+
+ protected override ConventionResult IsSatisfiedBy(string assemblyName, XDocument projectDocument)
+ {
+ if (GetPackageReferences(projectDocument).Contains(_needlePackage))
+ {
+ return ConventionResult.NotSatisfied(assemblyName, string.Format(FailureMessage, assemblyName));
+ }
+
+ return ConventionResult.Satisfied(assemblyName);
+ }
+
+ protected override string FailureMessage => "{0} should not reference package " + _needlePackage;
+ }
+}
\ No newline at end of file
diff --git a/src/Core/Conventional/Conventions/Assemblies/MustReferencePackageAssemblyConventionSpecification.cs b/src/Core/Conventional/Conventions/Assemblies/MustReferencePackageAssemblyConventionSpecification.cs
new file mode 100644
index 0000000..f4dd5ad
--- /dev/null
+++ b/src/Core/Conventional/Conventions/Assemblies/MustReferencePackageAssemblyConventionSpecification.cs
@@ -0,0 +1,27 @@
+using System.Linq;
+using System.Xml.Linq;
+
+namespace Conventional.Conventions.Assemblies
+{
+ public class MustReferencePackageAssemblyConventionSpecification : PackageReferenceAssemblyConventionSpecification
+ {
+ private readonly string _needlePackage;
+
+ public MustReferencePackageAssemblyConventionSpecification(string needlePackage)
+ {
+ _needlePackage = needlePackage;
+ }
+
+ protected override ConventionResult IsSatisfiedBy(string assemblyName, XDocument projectDocument)
+ {
+ if (GetPackageReferences(projectDocument).Contains(_needlePackage))
+ {
+ return ConventionResult.Satisfied(assemblyName);
+ }
+
+ return ConventionResult.NotSatisfied(assemblyName, string.Format(FailureMessage, assemblyName));
+ }
+
+ protected override string FailureMessage => "{0} should reference package " + _needlePackage;
+ }
+}
\ No newline at end of file
diff --git a/src/Core/Conventional/Conventions/Assemblies/PackageReferenceAssemblyConventionSpecification.cs b/src/Core/Conventional/Conventions/Assemblies/PackageReferenceAssemblyConventionSpecification.cs
new file mode 100644
index 0000000..4eb8b5c
--- /dev/null
+++ b/src/Core/Conventional/Conventions/Assemblies/PackageReferenceAssemblyConventionSpecification.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Xml.Linq;
+using System.Xml.XPath;
+
+namespace Conventional.Conventions.Assemblies
+{
+ public abstract class PackageReferenceAssemblyConventionSpecification : AssemblyConventionSpecification
+ {
+ protected IEnumerable GetPackageReferences(XDocument projectDocument)
+ {
+ // Note: The Project element (and descendants) are namespaced in legacy csproj files, so our XPath ignores the
+ // Note: namespace by considering the local element name only. Once we no-longer need to support legacy csproj
+ // Note: files, the XPath can be simplified to /Project/ItemGroup/PackageReference
+ return projectDocument.XPathSelectElements("/*[local-name() = 'Project']/*[local-name() = 'ItemGroup']/*[local-name() = 'PackageReference']")
+ .Select(referenceElement => referenceElement.Attribute("Include")?.Value)
+ .Where(value => value != null);
+ }
+
+ protected override ConventionResult IsSatisfiedByLegacyCsprojFormat(string assemblyName, XDocument projectDocument)
+ {
+ return IsSatisfiedBy(assemblyName, projectDocument);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Core/TestSolution/TestSolution.TestProject/SdkClassLibrary1/SdkClassLibrary1.csproj b/src/Core/TestSolution/TestSolution.TestProject/SdkClassLibrary1/SdkClassLibrary1.csproj
new file mode 100644
index 0000000..8918493
--- /dev/null
+++ b/src/Core/TestSolution/TestSolution.TestProject/SdkClassLibrary1/SdkClassLibrary1.csproj
@@ -0,0 +1,14 @@
+
+
+
+ netstandard2.1
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
diff --git a/src/Core/TestSolution/TestSolution.TestProject/TestSolution.TestProject.sln b/src/Core/TestSolution/TestSolution.TestProject/TestSolution.TestProject.sln
index 872fdb7..15b9473 100644
--- a/src/Core/TestSolution/TestSolution.TestProject/TestSolution.TestProject.sln
+++ b/src/Core/TestSolution/TestSolution.TestProject/TestSolution.TestProject.sln
@@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestSolution.TestProject",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProjectTwo", "..\TestProjectTwo\TestProjectTwo.csproj", "{DA39482D-C4B4-41B8-9908-BF715AE9DD7C}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SdkClassLibrary1", "SdkClassLibrary1\SdkClassLibrary1.csproj", "{C0988207-0023-4364-BFE0-04F8954501C4}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
{DA39482D-C4B4-41B8-9908-BF715AE9DD7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA39482D-C4B4-41B8-9908-BF715AE9DD7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA39482D-C4B4-41B8-9908-BF715AE9DD7C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C0988207-0023-4364-BFE0-04F8954501C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C0988207-0023-4364-BFE0-04F8954501C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C0988207-0023-4364-BFE0-04F8954501C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C0988207-0023-4364-BFE0-04F8954501C4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE