diff --git a/CycloneDX.Tests/CycloneDX.Tests.csproj b/CycloneDX.Tests/CycloneDX.Tests.csproj index 4bbf0c19..c73bf001 100644 --- a/CycloneDX.Tests/CycloneDX.Tests.csproj +++ b/CycloneDX.Tests/CycloneDX.Tests.csproj @@ -51,6 +51,15 @@ Always + + Always + + + Always + + + Always + Always diff --git a/CycloneDX.Tests/FunctionalTests/Issue669-IsTestProjectEvaluation/Issue669.cs b/CycloneDX.Tests/FunctionalTests/Issue669-IsTestProjectEvaluation/Issue669.cs new file mode 100644 index 00000000..85c6877f --- /dev/null +++ b/CycloneDX.Tests/FunctionalTests/Issue669-IsTestProjectEvaluation/Issue669.cs @@ -0,0 +1,40 @@ +using System.IO; +using System.IO.Abstractions; +using System.Reflection; +using CycloneDX.Interfaces; +using CycloneDX.Models; +using CycloneDX.Services; +using Moq; +using Xunit; + +namespace CycloneDX.Tests.FunctionalTests +{ + public sealed class Issue669 + { + [Theory] + [InlineData("testproject.csproj.xml", true)] + [InlineData("nontestproject.csproj.xml", false)] + public void IsTestProjectTrueWithImportedPropsTargets(string projectFile, bool expectedIsTestProject) + { + var mockDotnetUtilsService = new Mock(); + mockDotnetUtilsService + .Setup(s => s.Restore(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(new DotnetUtilsResult()); + mockDotnetUtilsService + .Setup(s => s.GetAssetsPath(It.IsAny())) + .Returns(new DotnetUtilsResult() { Result = "" }); + var mockPackageFileService = new Mock(); + var mockProjectAssetsFileService = new Mock(); + + var projectFileService = new ProjectFileService( + new FileSystem(), + mockDotnetUtilsService.Object, + mockPackageFileService.Object, + mockProjectAssetsFileService.Object); + + string physicalProjectPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "FunctionalTests", "Issue669-IsTestProjectEvaluation", projectFile); + + Assert.Equal(projectFileService.IsTestProject(physicalProjectPath), expectedIsTestProject); + } + } +} diff --git a/CycloneDX.Tests/FunctionalTests/Issue669-IsTestProjectEvaluation/makeMeTestProject.props b/CycloneDX.Tests/FunctionalTests/Issue669-IsTestProjectEvaluation/makeMeTestProject.props new file mode 100644 index 00000000..d885d18d --- /dev/null +++ b/CycloneDX.Tests/FunctionalTests/Issue669-IsTestProjectEvaluation/makeMeTestProject.props @@ -0,0 +1,6 @@ + + + + true + + diff --git a/CycloneDX.Tests/FunctionalTests/Issue669-IsTestProjectEvaluation/nontestproject.csproj.xml b/CycloneDX.Tests/FunctionalTests/Issue669-IsTestProjectEvaluation/nontestproject.csproj.xml new file mode 100644 index 00000000..8131dc5b --- /dev/null +++ b/CycloneDX.Tests/FunctionalTests/Issue669-IsTestProjectEvaluation/nontestproject.csproj.xml @@ -0,0 +1,9 @@ + + + + + + false + + + diff --git a/CycloneDX.Tests/FunctionalTests/Issue669-IsTestProjectEvaluation/testproject.csproj.xml b/CycloneDX.Tests/FunctionalTests/Issue669-IsTestProjectEvaluation/testproject.csproj.xml new file mode 100644 index 00000000..76fb3644 --- /dev/null +++ b/CycloneDX.Tests/FunctionalTests/Issue669-IsTestProjectEvaluation/testproject.csproj.xml @@ -0,0 +1,9 @@ + + + + false + + + + + diff --git a/CycloneDX/CycloneDX.csproj b/CycloneDX/CycloneDX.csproj index 64240016..d64edaea 100644 --- a/CycloneDX/CycloneDX.csproj +++ b/CycloneDX/CycloneDX.csproj @@ -1,4 +1,4 @@ - + Exe @@ -36,6 +36,8 @@ + + diff --git a/CycloneDX/Services/ProjectFileService.cs b/CycloneDX/Services/ProjectFileService.cs index b6788ad2..0f801f89 100755 --- a/CycloneDX/Services/ProjectFileService.cs +++ b/CycloneDX/Services/ProjectFileService.cs @@ -17,15 +17,16 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Xml; using System.IO; using System.IO.Abstractions; +using System.Linq; +using System.Text.RegularExpressions; using System.Threading.Tasks; +using System.Xml; using CycloneDX.Interfaces; using CycloneDX.Models; -using System.Text.RegularExpressions; -using System.Reflection; +using Microsoft.Build.Evaluation; +using Microsoft.Build.Locator; namespace CycloneDX.Services { @@ -38,6 +39,12 @@ public DotnetRestoreException(string message, Exception innerException) : base(m public class ProjectFileService : IProjectFileService { + static ProjectFileService() + { + // Register the MSBuild instance (do this once per process) + MSBuildLocator.RegisterDefaults(); + } + private XmlReaderSettings _xmlReaderSettings = new XmlReaderSettings { Async = true @@ -67,18 +74,17 @@ public bool IsTestProject(string projectFilePath) return false; } - XmlDocument xmldoc = new XmlDocument(); using var fileStream = _fileSystem.FileStream.New(projectFilePath, FileMode.Open, FileAccess.Read); - xmldoc.Load(fileStream); + using var xmlReader = XmlReader.Create(fileStream); - XmlElement testSdkReference = xmldoc.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.NET.Test.Sdk']") as XmlElement; - if (testSdkReference != null) - { - return true; - } - // if this is meant for old csproj file format, then it's probably not working because there is no namespace given - XmlElement testProjectPropertyGroup = xmldoc.SelectSingleNode("/Project/PropertyGroup[IsTestProject='true']") as XmlElement; - return testProjectPropertyGroup != null; + // load the project (all imports and SDKs are processed) + var project = new Project(xmlReader); + + // get property value after evaluation + string propertyValue = project.GetPropertyValue("IsTestProject"); + + //return is expected parsed boolean value, default false + return bool.TryParse(propertyValue, out bool isTestProject) && isTestProject; } private (string name, string version) GetAssemblyNameAndVersion(string projectFilePath) diff --git a/Directory.Packages.props b/Directory.Packages.props index 6acade1f..7327e6cf 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,7 +5,7 @@ - +