diff --git a/Directory.Packages.props b/Directory.Packages.props index 939606e8..d4f4fded 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -16,6 +16,7 @@ + @@ -57,4 +58,4 @@ - + \ No newline at end of file diff --git a/src/Microsoft.Sbom.Targets/Microsoft.Sbom.Targets.targets b/src/Microsoft.Sbom.Targets/Microsoft.Sbom.Targets.targets index 84012489..914941eb 100644 --- a/src/Microsoft.Sbom.Targets/Microsoft.Sbom.Targets.targets +++ b/src/Microsoft.Sbom.Targets/Microsoft.Sbom.Targets.targets @@ -30,10 +30,10 @@ $(AssemblyName) $(Version) 1.0.0 - http://spdx.org/spdxdocs/$(SbomGenerationPackageName)" + http://spdx.org/spdxdocs/$(SbomGenerationPackageName) false false - LogAlways + information SPDX:2.2 true diff --git a/src/Microsoft.Sbom.Targets/SbomCLIToolTask.cs b/src/Microsoft.Sbom.Targets/SbomCLIToolTask.cs index a050412a..99f3692e 100644 --- a/src/Microsoft.Sbom.Targets/SbomCLIToolTask.cs +++ b/src/Microsoft.Sbom.Targets/SbomCLIToolTask.cs @@ -93,7 +93,7 @@ private void SetOutputImportance() { this.StandardOutputImportance = "High"; - if (this.Verbosity.ToLower().Equals("Fatal")) + if (this.Verbosity.ToLower().Equals("fatal")) { this.StandardOutputImportance = "Low"; } diff --git a/src/Microsoft.Sbom.Targets/SbomInputValidator.cs b/src/Microsoft.Sbom.Targets/SbomInputValidator.cs index 6076c34c..747595ee 100644 --- a/src/Microsoft.Sbom.Targets/SbomInputValidator.cs +++ b/src/Microsoft.Sbom.Targets/SbomInputValidator.cs @@ -13,6 +13,9 @@ namespace Microsoft.Sbom.Targets; /// public partial class GenerateSbom { + private const string DefaultVerbosity = "Information"; + private const EventLevel DefaultEventLevel = EventLevel.Informational; + /// /// Ensure all required arguments are non-null/empty, /// and do not contain whitespaces, tabs, or newline characters. @@ -74,9 +77,9 @@ public EventLevel ValidateAndAssignVerbosity() // EventLevel value for the API. if (string.IsNullOrWhiteSpace(this.Verbosity)) { - Log.LogWarning($"No verbosity level specified. Setting verbosity level at Verbose"); - this.Verbosity = "Verbose"; - return EventLevel.Verbose; + Log.LogWarning($"No verbosity level specified. Setting verbosity level at {DefaultVerbosity}."); + this.Verbosity = DefaultVerbosity; + return DefaultEventLevel; } switch (this.Verbosity.ToLower().Trim()) @@ -94,9 +97,9 @@ public EventLevel ValidateAndAssignVerbosity() case "fatal": return EventLevel.Critical; default: - Log.LogWarning($"Unrecognized verbosity level specified. Setting verbosity level at Verbose"); - this.Verbosity = "Verbose"; - return EventLevel.Verbose; + Log.LogWarning($"Unrecognized verbosity level specified. Setting verbosity level at {DefaultVerbosity}."); + this.Verbosity = DefaultVerbosity; + return DefaultEventLevel; } } diff --git a/test/Microsoft.Sbom.Targets.Tests/AbstractGenerateSBomTaskInputTests.cs b/test/Microsoft.Sbom.Targets.Tests/AbstractGenerateSbomTaskInputTests.cs similarity index 61% rename from test/Microsoft.Sbom.Targets.Tests/AbstractGenerateSBomTaskInputTests.cs rename to test/Microsoft.Sbom.Targets.Tests/AbstractGenerateSbomTaskInputTests.cs index 7afe52e5..5274dbf5 100644 --- a/test/Microsoft.Sbom.Targets.Tests/AbstractGenerateSBomTaskInputTests.cs +++ b/test/Microsoft.Sbom.Targets.Tests/AbstractGenerateSbomTaskInputTests.cs @@ -4,31 +4,33 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Reflection; using System.Text.RegularExpressions; using Microsoft.Build.Framework; -using Microsoft.Sbom.Contracts; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; namespace Microsoft.Sbom.Targets.Tests; [TestClass] -public abstract class AbstractGenerateSBomTaskInputTests +public abstract class AbstractGenerateSbomTaskInputTests { - internal abstract SbomSpecification SbomSpecification { get; } + internal abstract string SbomSpecification { get; } - internal static readonly string CurrentDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + internal static readonly string CurrentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); internal static readonly string DefaultManifestDirectory = Path.Combine(CurrentDirectory, "_manifest"); internal static readonly string TemporaryDirectory = Path.Combine(CurrentDirectory, "_temporary"); internal static readonly string BuildComponentPath = Path.Combine(CurrentDirectory, "..", "..", ".."); internal static readonly string ExternalDocumentListFile = Path.GetRandomFileName(); + internal static string SbomToolPath = Path.Combine(Directory.GetCurrentDirectory(), "sbom-tool"); internal const string PackageSupplier = "Test-Microsoft"; internal const string PackageName = "CoseSignTool"; internal const string PackageVersion = "0.0.1"; internal const string NamespaceBaseUri = "https://base0.uri"; - private Mock buildEngine; private List errors; + private List messages; [TestInitialize] public void Startup() @@ -36,7 +38,9 @@ public void Startup() // Setup the build engine this.buildEngine = new Mock(); this.errors = new List(); + this.messages = new List(); this.buildEngine.Setup(x => x.LogErrorEvent(It.IsAny())).Callback(e => errors.Add(e)); + this.buildEngine.Setup(x => x.LogMessageEvent(It.IsAny())).Callback(msg => messages.Add(msg)); } [TestCleanup] @@ -68,7 +72,8 @@ public void Sbom_Fails_With_Null_Empty_And_WhiteSpace_Required_Params( string packageSupplier, string packageName, string packageVersion, - string namespaceBaseUri) + string namespaceBaseUri, + string sbomToolPath) { // Arrange. var task = new GenerateSbom @@ -78,8 +83,11 @@ public void Sbom_Fails_With_Null_Empty_And_WhiteSpace_Required_Params( PackageName = packageName, PackageVersion = packageVersion, NamespaceBaseUri = namespaceBaseUri, - ManifestInfo = this.SbomSpecification.ToString(), - BuildEngine = this.buildEngine.Object + ManifestInfo = this.SbomSpecification, + BuildEngine = this.buildEngine.Object, +#if NET472 + SbomToolPath = sbomToolPath, +#endif }; // Act @@ -91,29 +99,38 @@ public void Sbom_Fails_With_Null_Empty_And_WhiteSpace_Required_Params( private static IEnumerable GetNullRequiredParamsData() { - yield return new object[] { null, PackageSupplier, PackageName, PackageVersion, NamespaceBaseUri }; - yield return new object[] { CurrentDirectory, null, PackageName, PackageVersion, NamespaceBaseUri }; - yield return new object[] { CurrentDirectory, PackageSupplier, null, PackageVersion, NamespaceBaseUri }; - yield return new object[] { CurrentDirectory, PackageSupplier, PackageName, null, NamespaceBaseUri }; - yield return new object[] { CurrentDirectory, PackageSupplier, PackageName, PackageVersion, null }; + yield return new object[] { null, PackageSupplier, PackageName, PackageVersion, NamespaceBaseUri, SbomToolPath }; + yield return new object[] { CurrentDirectory, null, PackageName, PackageVersion, NamespaceBaseUri, SbomToolPath }; + yield return new object[] { CurrentDirectory, PackageSupplier, null, PackageVersion, NamespaceBaseUri, SbomToolPath }; + yield return new object[] { CurrentDirectory, PackageSupplier, PackageName, null, NamespaceBaseUri, SbomToolPath }; + yield return new object[] { CurrentDirectory, PackageSupplier, PackageName, PackageVersion, null, SbomToolPath }; +#if NET472 + yield return new object[] { CurrentDirectory, PackageSupplier, PackageName, PackageVersion, NamespaceBaseUri, null }; +#endif } private static IEnumerable GetEmptyRequiredParamsData() { - yield return new object[] { string.Empty, PackageSupplier, PackageName, PackageVersion, NamespaceBaseUri }; - yield return new object[] { CurrentDirectory, string.Empty, PackageName, PackageVersion, NamespaceBaseUri }; - yield return new object[] { CurrentDirectory, PackageSupplier, string.Empty, PackageVersion, NamespaceBaseUri }; - yield return new object[] { CurrentDirectory, PackageSupplier, PackageName, string.Empty, NamespaceBaseUri }; - yield return new object[] { CurrentDirectory, PackageSupplier, PackageName, PackageVersion, string.Empty }; + yield return new object[] { string.Empty, PackageSupplier, PackageName, PackageVersion, NamespaceBaseUri, SbomToolPath }; + yield return new object[] { CurrentDirectory, string.Empty, PackageName, PackageVersion, NamespaceBaseUri, SbomToolPath }; + yield return new object[] { CurrentDirectory, PackageSupplier, string.Empty, PackageVersion, NamespaceBaseUri, SbomToolPath }; + yield return new object[] { CurrentDirectory, PackageSupplier, PackageName, string.Empty, NamespaceBaseUri, SbomToolPath }; + yield return new object[] { CurrentDirectory, PackageSupplier, PackageName, PackageVersion, string.Empty, SbomToolPath }; +#if NET472 + yield return new object[] { CurrentDirectory, PackageSupplier, PackageName, PackageVersion, NamespaceBaseUri, string.Empty }; +#endif } private static IEnumerable GetWhiteSpace_Tabs_NewLineParamsData() { - yield return new object[] { " ", PackageSupplier, PackageName, PackageVersion, NamespaceBaseUri }; - yield return new object[] { CurrentDirectory, "\n", PackageName, PackageVersion, NamespaceBaseUri }; - yield return new object[] { CurrentDirectory, PackageSupplier, "\t", PackageVersion, NamespaceBaseUri }; - yield return new object[] { CurrentDirectory, PackageSupplier, PackageName, " \n \t \n \t \n ", NamespaceBaseUri }; - yield return new object[] { CurrentDirectory, PackageSupplier, PackageName, PackageVersion, "\t \t \t " }; + yield return new object[] { " ", PackageSupplier, PackageName, PackageVersion, NamespaceBaseUri, SbomToolPath }; + yield return new object[] { CurrentDirectory, "\n", PackageName, PackageVersion, NamespaceBaseUri, SbomToolPath }; + yield return new object[] { CurrentDirectory, PackageSupplier, "\t", PackageVersion, NamespaceBaseUri, SbomToolPath }; + yield return new object[] { CurrentDirectory, PackageSupplier, PackageName, " \n \t \n \t \n ", NamespaceBaseUri, SbomToolPath }; + yield return new object[] { CurrentDirectory, PackageSupplier, PackageName, PackageVersion, "\t \t \t ", SbomToolPath }; +#if NET472 + yield return new object[] { CurrentDirectory, PackageSupplier, PackageName, PackageVersion, NamespaceBaseUri, "\t \t \t " }; +#endif } /// @@ -137,8 +154,11 @@ public void Sbom_Fails_With_Invalid_NamespaceBaseUri(string namespaceBaseUri) PackageName = PackageName, PackageVersion = PackageVersion, NamespaceBaseUri = namespaceBaseUri, - ManifestInfo = this.SbomSpecification.ToString(), - BuildEngine = this.buildEngine.Object + ManifestInfo = this.SbomSpecification, + BuildEngine = this.buildEngine.Object, +#if NET472 + SbomToolPath = SbomToolPath, +#endif }; // Act @@ -173,8 +193,11 @@ public void Sbom_Generation_Fails_For_Invalid_NamespaceUriUniquePart(string name PackageVersion = PackageVersion, NamespaceBaseUri = NamespaceBaseUri, NamespaceUriUniquePart = namespaceUriUniquePart, - ManifestInfo = this.SbomSpecification.ToString(), - BuildEngine = this.buildEngine.Object + ManifestInfo = this.SbomSpecification, + BuildEngine = this.buildEngine.Object, +#if NET472 + SbomToolPath = SbomToolPath, +#endif }; // Act @@ -192,9 +215,9 @@ public void Sbom_Generation_Fails_For_Invalid_NamespaceUriUniquePart(string name public void Sbom_Generation_Succeeds_For_Null_Verbosity() { // Arrange - // If Verbosity is null, the default value should be Verbose and is printed in the + // If Verbosity is null, the default value should be Information and is printed in the // tool's standard output. - var pattern = new Regex("Verbosity=.*Value=Verbose"); + var pattern = new Regex("Verbosity=.*Value=Information"); var stringWriter = new StringWriter(); Console.SetOut(stringWriter); var task = new GenerateSbom @@ -204,9 +227,12 @@ public void Sbom_Generation_Succeeds_For_Null_Verbosity() PackageName = PackageName, PackageVersion = PackageVersion, NamespaceBaseUri = NamespaceBaseUri, - ManifestInfo = this.SbomSpecification.ToString(), + ManifestInfo = this.SbomSpecification, Verbosity = null, - BuildEngine = this.buildEngine.Object + BuildEngine = this.buildEngine.Object, +#if NET472 + SbomToolPath = SbomToolPath, +#endif }; // Act @@ -215,7 +241,11 @@ public void Sbom_Generation_Succeeds_For_Null_Verbosity() // Assert Assert.IsTrue(result); +#if NET472 + Assert.IsTrue(this.messages.Any(msg => pattern.IsMatch(msg.Message))); +#else Assert.IsTrue(pattern.IsMatch(output)); +#endif } /// @@ -226,9 +256,9 @@ public void Sbom_Generation_Succeeds_For_Null_Verbosity() public void Sbom_Generation_Succeeds_For_Invalid_Verbosity() { // Arrange - // If an invalid Verbosity is specified, the default value should be Verbose and is printed in the - // tool's standard output. - var pattern = new Regex("Verbosity=.*Value=Verbose"); + // If an invalid Verbosity is specified, the default value should be Information. It is also printed in the + // tool's standard output for the MSBuild Core task. + var pattern = new Regex("Verbosity=.*Value=Information"); var stringWriter = new StringWriter(); Console.SetOut(stringWriter); var task = new GenerateSbom @@ -239,8 +269,11 @@ public void Sbom_Generation_Succeeds_For_Invalid_Verbosity() PackageVersion = PackageVersion, NamespaceBaseUri = NamespaceBaseUri, Verbosity = "Invalid Verbosity", - ManifestInfo = this.SbomSpecification.ToString(), - BuildEngine = this.buildEngine.Object + ManifestInfo = this.SbomSpecification, + BuildEngine = this.buildEngine.Object, +#if NET472 + SbomToolPath = SbomToolPath, +#endif }; // Act @@ -249,22 +282,32 @@ public void Sbom_Generation_Succeeds_For_Invalid_Verbosity() // Assert Assert.IsTrue(result); +#if NET472 + Assert.IsTrue(this.messages.Any(msg => pattern.IsMatch(msg.Message))); +#else Assert.IsTrue(pattern.IsMatch(output)); +#endif } +#if !NET472 /// /// Test to ensure GenerateSbom correctly parses and provides each EventLevel verbosity /// values to the SBOM API. /// [TestMethod] - [DataRow("FATAL", "Fatal")] - [DataRow("information", "Information")] - [DataRow("vErBose", "Verbose")] - [DataRow("Warning", "Warning")] - [DataRow("eRRor", "Error")] - [DataRow("Debug", "Verbose")] - public void Sbom_Generation_Assigns_Correct_Verbosity_IgnoreCase(string inputVerbosity, string mappedVerbosity) + [DataRow("FATAL", "Fatal", false)] + [DataRow("information", "Information", true)] + [DataRow("vErBose", "Verbose", true)] + [DataRow("Warning", "Warning", false)] + [DataRow("eRRor", "Error", false)] + [DataRow("DeBug", "Verbose", true)] + public void Sbom_Generation_Assigns_Correct_Verbosity_IgnoreCase(string inputVerbosity, string mappedVerbosity, bool messageShouldBeLogged) { + if (!messageShouldBeLogged) + { + Assert.Inconclusive("Cases where the input Verbosity is more restrictive than `Information` are failing due to this issue: https://github.com/microsoft/sbom-tool/issues/616"); + } + // Arrange var pattern = new Regex($"Verbosity=.*Value={mappedVerbosity}"); var stringWriter = new StringWriter(); @@ -277,8 +320,8 @@ public void Sbom_Generation_Assigns_Correct_Verbosity_IgnoreCase(string inputVer PackageVersion = PackageVersion, NamespaceBaseUri = NamespaceBaseUri, Verbosity = inputVerbosity, - ManifestInfo = this.SbomSpecification.ToString(), - BuildEngine = this.buildEngine.Object + ManifestInfo = this.SbomSpecification, + BuildEngine = this.buildEngine.Object, }; // Act @@ -286,7 +329,47 @@ public void Sbom_Generation_Assigns_Correct_Verbosity_IgnoreCase(string inputVer var output = stringWriter.ToString(); // Assert - Assert.IsTrue(result); - Assert.IsTrue(pattern.IsMatch(output)); + Assert.IsTrue(result, $"result: {result} is not set to true"); + Assert.AreEqual(messageShouldBeLogged, pattern.IsMatch(output)); + } +#else + /// + /// Test to ensure GenerateSbom correctly parses and provides each verbosity option + /// to the SBOM CLI. + /// + [TestMethod] + [DataRow("FATAL", "Fatal", false)] + [DataRow("information", "Information", true)] + [DataRow("vErBose", "Verbose", true)] + [DataRow("Warning", "Warning", false)] + [DataRow("eRRor", "Error", false)] + [DataRow("DeBug", "Debug", true)] + public void Sbom_Generation_Assigns_Correct_Verbosity_IgnoreCase(string inputVerbosity, string mappedVerbosity, bool messageShouldBeLogged) + { + // Arrange + var pattern = new Regex($"Verbosity=.*Value={mappedVerbosity}"); + var stringWriter = new StringWriter(); + Console.SetOut(stringWriter); + var task = new GenerateSbom + { + BuildDropPath = CurrentDirectory, + PackageSupplier = PackageSupplier, + PackageName = PackageName, + PackageVersion = PackageVersion, + NamespaceBaseUri = NamespaceBaseUri, + Verbosity = inputVerbosity, + ManifestInfo = this.SbomSpecification, + BuildEngine = this.buildEngine.Object, + SbomToolPath = SbomToolPath, + }; + + // Act + var result = task.Execute(); + var output = stringWriter.ToString(); + + // Assert + Assert.IsTrue(result, $"result: {result} is not set to true"); + Assert.AreEqual(messageShouldBeLogged, this.messages.Any(msg => pattern.IsMatch(msg.Message))); } +#endif } diff --git a/test/Microsoft.Sbom.Targets.Tests/AbstractGenerateSbomTaskTests.cs b/test/Microsoft.Sbom.Targets.Tests/AbstractGenerateSbomTaskTests.cs index c43d072d..3008bd84 100644 --- a/test/Microsoft.Sbom.Targets.Tests/AbstractGenerateSbomTaskTests.cs +++ b/test/Microsoft.Sbom.Targets.Tests/AbstractGenerateSbomTaskTests.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using System.IO; +using System.Reflection; using Microsoft.Build.Framework; -using Microsoft.Sbom.Contracts; using Microsoft.Sbom.Targets.Tests.Utility; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -17,11 +17,16 @@ namespace Microsoft.Sbom.Targets.Tests; [TestClass] public abstract class AbstractGenerateSbomTaskTests { - internal abstract SbomSpecification SbomSpecification { get; } + internal abstract string SbomSpecificationName { get; } - internal static readonly string CurrentDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + internal abstract string SbomSpecificationVersion { get; } + + internal static readonly string CurrentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); internal static readonly string DefaultManifestDirectory = Path.Combine(CurrentDirectory, "_manifest"); internal static readonly string TemporaryDirectory = Path.Combine(CurrentDirectory, "_temp"); + internal static readonly string ExternalDocumentListFile = Path.GetRandomFileName(); + internal static string SbomToolPath = Path.Combine(Directory.GetCurrentDirectory(), "sbom-tool"); + internal const string PackageSupplier = "Test-Microsoft"; internal const string PackageName = "CoseSignTool"; internal const string PackageVersion = "0.0.1"; @@ -32,16 +37,12 @@ public abstract class AbstractGenerateSbomTaskTests internal string ManifestPath; internal GeneratedSbomValidator GeneratedSbomValidator; - internal string SbomSpecificationDirectoryName => $"{this.SbomSpecification.Name}_{this.SbomSpecification.Version}".ToLowerInvariant(); + internal string SbomSpecification => $"{this.SbomSpecificationName}:{this.SbomSpecificationVersion}"; - [TestInitialize] - public void Startup() - { - // Setup the build engine - this.BuildEngine = new Mock(); - this.Errors = new List(); - this.BuildEngine.Setup(x => x.LogErrorEvent(It.IsAny())).Callback(e => Errors.Add(e)); + internal string SbomSpecificationDirectoryName => $"{this.SbomSpecificationName}_{this.SbomSpecificationVersion}".ToLowerInvariant(); + private void CleanupManifestDirectory() + { // Clean up the manifest directory if (Directory.Exists(DefaultManifestDirectory)) { @@ -53,9 +54,29 @@ public void Startup() { Directory.Delete(TemporaryDirectory, true); } + } + + [TestInitialize] + public void Startup() + { + // Setup the build engine + this.BuildEngine = new Mock(); + this.Errors = new List(); + this.BuildEngine.Setup(x => x.LogErrorEvent(It.IsAny())).Callback(e => Errors.Add(e)); + + this.CleanupManifestDirectory(); this.ManifestPath = Path.Combine(DefaultManifestDirectory, this.SbomSpecificationDirectoryName, "manifest.spdx.json"); this.GeneratedSbomValidator = new(this.SbomSpecification); +#if NET472 + Assert.IsTrue(Directory.Exists(SbomToolPath)); +#endif + } + + [TestCleanup] + public void Cleanup() + { + this.CleanupManifestDirectory(); } [TestMethod] @@ -70,7 +91,10 @@ public void Sbom_Is_Successfully_Generated() PackageVersion = PackageVersion, NamespaceBaseUri = NamespaceBaseUri, BuildEngine = this.BuildEngine.Object, - ManifestInfo = this.SbomSpecification.ToString(), + ManifestInfo = this.SbomSpecification, +#if NET472 + SbomToolPath = SbomToolPath, +#endif }; // Act @@ -100,7 +124,10 @@ public void Sbom_Is_Successfully_Generated_Valid_URI(string namespaceBaseUri) PackageVersion = PackageVersion, NamespaceBaseUri = namespaceBaseUri, BuildEngine = this.BuildEngine.Object, - ManifestInfo = this.SbomSpecification.ToString(), + ManifestInfo = this.SbomSpecification, +#if NET472 + SbomToolPath = SbomToolPath, +#endif }; // Act @@ -126,7 +153,10 @@ public void Sbom_Is_Successfully_Generated_Valid_RequiredParams(string packageSu PackageVersion = packageVersion, NamespaceBaseUri = NamespaceBaseUri, BuildEngine = this.BuildEngine.Object, - ManifestInfo = this.SbomSpecification.ToString(), + ManifestInfo = this.SbomSpecification, +#if NET472 + SbomToolPath = SbomToolPath, +#endif }; // Act @@ -176,7 +206,10 @@ public void Sbom_Is_Successfully_Generated_In_Specified_Location() PackageVersion = PackageVersion, NamespaceBaseUri = NamespaceBaseUri, BuildEngine = this.BuildEngine.Object, - ManifestInfo = this.SbomSpecification.ToString(), + ManifestInfo = this.SbomSpecification, +#if NET472 + SbomToolPath = SbomToolPath, +#endif }; // Act @@ -201,7 +234,10 @@ public void Sbom_Generation_Fails_With_NotFound_BuildDropPath() PackageVersion = PackageVersion, NamespaceBaseUri = NamespaceBaseUri, BuildEngine = this.BuildEngine.Object, - ManifestInfo = this.SbomSpecification.ToString(), + ManifestInfo = this.SbomSpecification, +#if NET472 + SbomToolPath = SbomToolPath, +#endif }; // Act @@ -224,7 +260,10 @@ public void Sbom_Generation_Fails_With_NotFound_BuildComponentPath() PackageVersion = PackageVersion, NamespaceBaseUri = NamespaceBaseUri, BuildEngine = this.BuildEngine.Object, - ManifestInfo = this.SbomSpecification.ToString(), + ManifestInfo = this.SbomSpecification, +#if NET472 + SbomToolPath = SbomToolPath, +#endif }; // Act @@ -248,7 +287,10 @@ public void Sbom_Generation_Fails_With_NotFound_ExternalDocumentListFile() PackageVersion = PackageVersion, NamespaceBaseUri = NamespaceBaseUri, BuildEngine = this.BuildEngine.Object, - ManifestInfo = this.SbomSpecification.ToString(), + ManifestInfo = this.SbomSpecification, +#if NET472 + SbomToolPath = SbomToolPath, +#endif }; // Act @@ -272,7 +314,10 @@ public void Sbom_Generation_Fails_With_NotFound_ManifestDirPath() PackageVersion = PackageVersion, NamespaceBaseUri = NamespaceBaseUri, BuildEngine = this.BuildEngine.Object, - ManifestInfo = this.SbomSpecification.ToString(), + ManifestInfo = this.SbomSpecification, +#if NET472 + SbomToolPath = SbomToolPath, +#endif }; // Act @@ -299,7 +344,10 @@ public void Sbom_Is_Successfully_Generated_With_Component_Path() PackageVersion = PackageVersion, NamespaceBaseUri = NamespaceBaseUri, BuildEngine = this.BuildEngine.Object, - ManifestInfo = this.SbomSpecification.ToString(), + ManifestInfo = this.SbomSpecification, +#if NET472 + SbomToolPath = SbomToolPath, +#endif }; // Act @@ -327,14 +375,81 @@ public void Sbom_Is_Successfully_Generated_With_Unique_Namespace_Part_Defined(st NamespaceBaseUri = NamespaceBaseUri, NamespaceUriUniquePart = uniqueNamespacePart, BuildEngine = this.BuildEngine.Object, - ManifestInfo = this.SbomSpecification.ToString(), + ManifestInfo = this.SbomSpecification, +#if NET472 + SbomToolPath = SbomToolPath, +#endif }; // Act var result = task.Execute(); // Assert - Assert.IsTrue(result); + Assert.IsTrue(result, $"{result} is not set to true."); this.GeneratedSbomValidator.AssertSbomIsValid(this.ManifestPath, CurrentDirectory, PackageName, PackageVersion, PackageSupplier, NamespaceBaseUri, expectedNamespaceUriUniquePart: uniqueNamespacePart); } + +#if NET472 + [TestMethod] + public void Sbom_Generation_Fails_With_Tool_Path_Not_Found() + { + // Arrange + var task = new GenerateSbom + { + BuildDropPath = CurrentDirectory, + PackageSupplier = PackageSupplier, + PackageName = PackageName, + PackageVersion = PackageVersion, + NamespaceBaseUri = NamespaceBaseUri, + BuildEngine = this.BuildEngine.Object, + ManifestInfo = this.SbomSpecification, + SbomToolPath = "C:\\Not-Found\\Path\\", + }; + + // Act + var result = task.Execute(); + + // Assert + Assert.IsFalse(result); + Assert.IsFalse(Directory.Exists(DefaultManifestDirectory)); + } +#endif + + // This test is failing due to this issue: https://github.com/microsoft/sbom-tool/issues/615 + [TestMethod] + public void Sbom_Fails_To_Generate_Due_To_File_In_Use() + { + var manifestDirPath = Path.Combine(TemporaryDirectory, "sub-directory"); + this.ManifestPath = Path.Combine(manifestDirPath, "_manifest", this.SbomSpecificationDirectoryName, "manifest.spdx.json"); + Directory.CreateDirectory(manifestDirPath); + // Arrange + var task = new GenerateSbom + { + BuildDropPath = CurrentDirectory, + ManifestDirPath = manifestDirPath, + PackageSupplier = PackageSupplier, + PackageName = PackageName, + PackageVersion = PackageVersion, + NamespaceBaseUri = NamespaceBaseUri, + BuildEngine = this.BuildEngine.Object, + ManifestInfo = this.SbomSpecification, +#if NET472 + SbomToolPath = SbomToolPath, +#endif + }; + + // Write JSON content to the manifest file, and create the directory if it doesn't exist + var jsonContent = "{}"; + Directory.CreateDirectory(Path.GetDirectoryName(ManifestPath)); + File.WriteAllText(ManifestPath, jsonContent); + // Open a handle to the manifest file to simulate it being in use + using (var fileStream = File.Open(this.ManifestPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)) + { + // Act + var result = task.Execute(); + + // Assert + Assert.IsFalse(result); + } + } } diff --git a/test/Microsoft.Sbom.Targets.Tests/GenerateSbomTaskSPDX_2_2InputTests.cs b/test/Microsoft.Sbom.Targets.Tests/GenerateSbomTaskSPDX_2_2InputTests.cs index 92431f09..1efe7e8a 100644 --- a/test/Microsoft.Sbom.Targets.Tests/GenerateSbomTaskSPDX_2_2InputTests.cs +++ b/test/Microsoft.Sbom.Targets.Tests/GenerateSbomTaskSPDX_2_2InputTests.cs @@ -3,15 +3,13 @@ namespace Microsoft.Sbom.Targets.Tests; -using Microsoft.Sbom.Api.Utils; -using Microsoft.Sbom.Contracts; using Microsoft.VisualStudio.TestTools.UnitTesting; /// /// Class to test the generation of SBOM using SPDX 2.2 specification. /// [TestClass] -public class GenerateSbomTaskSPDX_2_2InputTests : AbstractGenerateSBomTaskInputTests +public class GenerateSbomTaskSPDX_2_2InputTests : AbstractGenerateSbomTaskInputTests { - internal override SbomSpecification SbomSpecification => Constants.SPDX22Specification; + internal override string SbomSpecification => "SPDX:2.2"; } diff --git a/test/Microsoft.Sbom.Targets.Tests/GenerateSbomTaskSPDX_2_2Tests.cs b/test/Microsoft.Sbom.Targets.Tests/GenerateSbomTaskSPDX_2_2Tests.cs index a3acf920..63e66701 100644 --- a/test/Microsoft.Sbom.Targets.Tests/GenerateSbomTaskSPDX_2_2Tests.cs +++ b/test/Microsoft.Sbom.Targets.Tests/GenerateSbomTaskSPDX_2_2Tests.cs @@ -8,8 +8,6 @@ namespace Microsoft.Sbom.Targets.Tests; using System.Linq; using System.Text; using System.Threading.Tasks; -using Microsoft.Sbom.Api.Utils; -using Microsoft.Sbom.Contracts; using Microsoft.VisualStudio.TestTools.UnitTesting; /// @@ -18,5 +16,7 @@ namespace Microsoft.Sbom.Targets.Tests; [TestClass] public class GenerateSbomTaskSPDX_2_2Tests : AbstractGenerateSbomTaskTests { - internal override SbomSpecification SbomSpecification => Constants.SPDX22Specification; + internal override string SbomSpecificationName => "SPDX"; + + internal override string SbomSpecificationVersion => "2.2"; } diff --git a/test/Microsoft.Sbom.Targets.Tests/Microsoft.Sbom.Targets.Tests.csproj b/test/Microsoft.Sbom.Targets.Tests/Microsoft.Sbom.Targets.Tests.csproj index 4278eba1..73da9053 100644 --- a/test/Microsoft.Sbom.Targets.Tests/Microsoft.Sbom.Targets.Tests.csproj +++ b/test/Microsoft.Sbom.Targets.Tests/Microsoft.Sbom.Targets.Tests.csproj @@ -1,11 +1,13 @@ - net8.0 + net8.0;net472 + net8.0 false True Microsoft.Sbom.Targets.Tests - $(StrongNameSigningKeyFilePath) + net8.0 + $(MSBuildThisFileDirectory)..\..\src\Microsoft.Sbom.Tool\ @@ -13,12 +15,28 @@ + + + + + + + + + <_SbomToolFiles Include="$(SBOMCLIToolProjectDir)bin\$(Configuration)\$(SbomCLIToolTargetFramework)\publish\**\*.*"> + false + + + + + - - - + + + + diff --git a/test/Microsoft.Sbom.Targets.Tests/Utility/GeneratedSbomValidator.cs b/test/Microsoft.Sbom.Targets.Tests/Utility/GeneratedSbomValidator.cs index 5e775811..1ee8c094 100644 --- a/test/Microsoft.Sbom.Targets.Tests/Utility/GeneratedSbomValidator.cs +++ b/test/Microsoft.Sbom.Targets.Tests/Utility/GeneratedSbomValidator.cs @@ -10,8 +10,6 @@ namespace Microsoft.Sbom.Targets.Tests.Utility; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; -using Microsoft.Sbom.Api.Utils; -using Microsoft.Sbom.Contracts; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; @@ -21,9 +19,10 @@ namespace Microsoft.Sbom.Targets.Tests.Utility; #pragma warning disable CA5350 // Suppress Do Not Use Weak Cryptographic Algorithms as we use SHA1 intentionally internal class GeneratedSbomValidator { - private readonly SbomSpecification sbomSpecification; + private const string SPDX22Specification = "SPDX:2.2"; + private readonly string sbomSpecification; - public GeneratedSbomValidator(SbomSpecification sbomSpecification) + public GeneratedSbomValidator(string sbomSpecification) { this.sbomSpecification = sbomSpecification; } @@ -36,14 +35,14 @@ internal void AssertSbomIsValid(string manifestPath, string buildDropPath, strin var manifestContent = File.ReadAllText(manifestPath); var manifest = JsonConvert.DeserializeObject(manifestContent); - if (this.sbomSpecification.Equals(Constants.SPDX22Specification)) + if (this.sbomSpecification.Equals(SPDX22Specification)) { // Check the manifest has expected file data var filesValue = manifest["files"]; Assert.IsNotNull(filesValue); var expectedFilesHashes = this.GetBuildDropFileHashes(buildDropPath); - Assert.AreEqual(expectedFilesHashes.Count, filesValue.Count); + Assert.AreEqual(expectedFilesHashes.Count, filesValue.Count, $"Manifest {manifestPath} has {filesValue.Count} files instead of {expectedFilesHashes.Count}"); foreach (var file in filesValue) { var filePath = Path.GetFullPath(Path.Combine(buildDropPath, (string)file["fileName"])); @@ -92,7 +91,7 @@ internal void AssertSbomIsValid(string manifestPath, string buildDropPath, strin } else { - Assert.IsTrue(namespaceValue.Contains($"{expectedNamespaceUriBase.Trim()}/{expectedPackageName}/{expectedPackageVersion}", StringComparison.InvariantCultureIgnoreCase)); + Assert.IsTrue(namespaceValue.Contains($"{expectedNamespaceUriBase.Trim()}/{expectedPackageName}/{expectedPackageVersion}")); } } } @@ -128,7 +127,7 @@ private IDictionary> GetBuildDropFileHashes( private IList<(string, Func)> GetListOfHashAlgorithmCreators() { - if (this.sbomSpecification.Equals(Constants.SPDX22Specification)) + if (this.sbomSpecification.Equals(SPDX22Specification)) { return [("SHA1", SHA1.Create), ("SHA256", SHA256.Create)]; }