Skip to content

Commit 0db29cc

Browse files
author
eddie.stanley
committed
[andrewabestGH-87] Added convention to enforce a project file sets a property
Some examples: - Convention.MustSetPropertyValue("Nullable", "enable") // Ensure nullable types are enabled for the project - Convention.MustSetPropertyValue("IsPackable", "true") // Ensure the project can be packed for NuGet See https://learn.microsoft.com/en-us/visualstudio/msbuild/property-element-msbuild?view=vs-2022#example
1 parent ee0b163 commit 0db29cc

File tree

4 files changed

+110
-0
lines changed

4 files changed

+110
-0
lines changed

src/Core/Conventional.Tests/Conventional/Conventions/Assemblies/AssemblyConventionSpecificationTests.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,5 +281,60 @@ public void MustNotReferencePackage_Failure()
281281

282282
result.IsSatisfied.Should().BeFalse();
283283
}
284+
285+
[Test]
286+
public void MustSetPropertyValue_SingleValue_Success()
287+
{
288+
var result = TheAssembly
289+
.WithNameMatching("SdkClassLibrary1")
290+
.MustConformTo(Convention.MustSetPropertyValue("TheUniversalAnswer", "42"));
291+
292+
result.IsSatisfied.Should().BeTrue();
293+
}
294+
295+
[Theory]
296+
[TestCase("Potato")]
297+
[TestCase("Carrot")]
298+
public void MustSetPropertyValue_MultipleValues_Success(string value)
299+
{
300+
var result = TheAssembly
301+
.WithNameMatching("SdkClassLibrary1")
302+
.MustConformTo(Convention.MustSetPropertyValue("Vegetable", value));
303+
304+
result.IsSatisfied.Should().BeTrue();
305+
}
306+
307+
[Test]
308+
public void MustSetPropertyValue_SingleValue_Failure()
309+
{
310+
var result = TheAssembly
311+
.WithNameMatching("SdkClassLibrary1")
312+
.MustConformTo(Convention.MustSetPropertyValue("TheUniversalAnswer", "41.999"));
313+
314+
result.IsSatisfied.Should().BeFalse();
315+
result.Failures.Single().Should().Be("SdkClassLibrary1 should have property TheUniversalAnswer with value 41.999");
316+
}
317+
318+
[Test]
319+
public void MustSetPropertyValue_MultipleValues_Failure()
320+
{
321+
var result = TheAssembly
322+
.WithNameMatching("SdkClassLibrary1")
323+
.MustConformTo(Convention.MustSetPropertyValue("Vegetable", "Turnip")); // There's no <Vegetable>Turnip</Vegetable> in the csproj
324+
325+
result.IsSatisfied.Should().BeFalse();
326+
result.Failures.Single().Should().Be("SdkClassLibrary1 should have property Vegetable with value Turnip");
327+
}
328+
329+
[Test]
330+
public void MustSetPropertyValue_NoValues_Failure()
331+
{
332+
var result = TheAssembly
333+
.WithNameMatching("SdkClassLibrary1")
334+
.MustConformTo(Convention.MustSetPropertyValue("ThisPropertyShouldNeverEverExist", "x")); // There's no <ThisPropertyShouldNeverEverExist>x</ThisPropertyShouldNeverEverExist> in the csproj
335+
336+
result.IsSatisfied.Should().BeFalse();
337+
result.Failures.Single().Should().Be("SdkClassLibrary1 should have property ThisPropertyShouldNeverEverExist with value x");
338+
}
284339
}
285340
}

src/Core/Conventional/Convention.Assembly.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,17 @@ public static MustNotReferencePackageAssemblyConventionSpecification MustNotRefe
9494
{
9595
return new MustNotReferencePackageAssemblyConventionSpecification(packageName);
9696
}
97+
98+
/// <summary>
99+
/// Require this project to set a <see href="https://learn.microsoft.com/en-us/visualstudio/msbuild/property-element-msbuild?view=vs-2022#example">property value</see>
100+
/// </summary>
101+
/// <param name="propertyName">The name of the property</param>
102+
/// <param name="value">The value the property should have</param>
103+
/// <remarks>This convention is currently ignorant of <see href="https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-conditions">MSBuild conditions</see></remarks>
104+
public static MustSetPropertyValueAssemblyConventionSpecification MustSetPropertyValue(
105+
string propertyName, string value)
106+
{
107+
return new MustSetPropertyValueAssemblyConventionSpecification(propertyName, value);
108+
}
97109
}
98110
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.Linq;
3+
using System.Xml.Linq;
4+
using System.Xml.XPath;
5+
6+
namespace Conventional.Conventions.Assemblies
7+
{
8+
public class MustSetPropertyValueAssemblyConventionSpecification : AssemblyConventionSpecification
9+
{
10+
private string ExpectedPropertyName { get; }
11+
private string ExpectedPropertyValue { get; }
12+
13+
public MustSetPropertyValueAssemblyConventionSpecification(string expectedPropertyName, string expectedPropertyValue)
14+
{
15+
ExpectedPropertyName = expectedPropertyName;
16+
ExpectedPropertyValue = expectedPropertyValue;
17+
}
18+
19+
protected override ConventionResult IsSatisfiedByLegacyCsprojFormat(string assemblyName, XDocument projectDocument)
20+
{
21+
return IsSatisfiedBy(assemblyName, projectDocument);
22+
}
23+
24+
protected override ConventionResult IsSatisfiedBy(string assemblyName, XDocument projectDocument)
25+
{
26+
var matchingProperties = projectDocument.XPathSelectElements($"/Project/PropertyGroup/{ExpectedPropertyName}")
27+
.Select(propertyElement => propertyElement.Value)
28+
.Where(propertyValue => string.Equals(ExpectedPropertyValue, propertyValue, StringComparison.InvariantCulture));
29+
30+
return matchingProperties.Count() == 1
31+
? ConventionResult.Satisfied(assemblyName)
32+
: ConventionResult.NotSatisfied(assemblyName, string.Format(FailureMessage, assemblyName));
33+
}
34+
35+
protected override string FailureMessage => "{0} should have property " + ExpectedPropertyName + " with value " + ExpectedPropertyValue;
36+
}
37+
}

src/Core/TestSolution/TestSolution.TestProject/SdkClassLibrary1/SdkClassLibrary1.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

3+
<PropertyGroup>
4+
<TheUniversalAnswer>42</TheUniversalAnswer>
5+
<Vegetable>Potato</Vegetable>
6+
<Vegetable>Carrot</Vegetable>
7+
</PropertyGroup>
8+
39
<PropertyGroup>
410
<TargetFramework>netstandard2.1</TargetFramework>
511
</PropertyGroup>

0 commit comments

Comments
 (0)