diff --git a/Source/GenerateSharp/Opal/Logger/Log.cs b/Source/GenerateSharp/Opal/Logger/Log.cs index 9cb1bb7a..d7f03f54 100644 --- a/Source/GenerateSharp/Opal/Logger/Log.cs +++ b/Source/GenerateSharp/Opal/Logger/Log.cs @@ -4,121 +4,121 @@ namespace Opal { - using global::System; - - /// - /// The static logger class. - /// - public class Log - { - private static int activeId = 0; - private static TraceListener? listener = null; - - /// - /// Register the single event listener. - /// - /// The listener. - public static void RegisterListener(TraceListener? listener) - { - Log.listener = listener; - } - - /// - /// Get access to the single event listener. - /// - public static TraceListener EnsureListener() - { - if (listener == null) - throw new InvalidOperationException("No Listener registered."); - return listener; - } - - /// - /// Set the active ids to use for each event. - /// - public static int GetActiveId() - { - return activeId; - } - - public static void SetActiveId(int value) - { - activeId = value; - } - - /// - /// Log a high priority message. - /// - /// The message. - /// The message id. - public static void HighPriority(string message, int id) - { - EnsureListener().TraceEvent(TraceEventFlag.HighPriority, id, message); - } - - public static void HighPriority(string message) - { - HighPriority(message, activeId); - } - - /// - /// Log a generic infomational message. - /// - /// The message. - /// The message id. - public static void Info(string message, int id) - { - EnsureListener().TraceEvent(TraceEventFlag.Information, id, message); - } - - public static void Info(string message) - { - Info(message, activeId); - } - - /// - /// Log a diagnostic message. - /// - /// The message. - /// The message id. - public static void Diag(string message, int id) - { - EnsureListener().TraceEvent(TraceEventFlag.Diagnostic, id, message); - } - - public static void Diag(string message) - { - Diag(message, activeId); - } - - /// - /// Log a warning message. - /// - /// The message. - /// The message id. - public static void Warning(string message, int id) - { - EnsureListener().TraceEvent(TraceEventFlag.Warning, id, message); - } - - public static void Warning(string message) - { - Warning(message, activeId); - } - - /// - /// Log an error message. - /// - /// The message. - /// The message id. - public static void Error(string message, int id) - { - EnsureListener().TraceEvent(TraceEventFlag.Error, id, message); - } - - public static void Error(string message) - { - Error(message, activeId); - } - } + using global::System; + + /// + /// The static logger class. + /// + public class Log + { + private static int activeId = 0; + private static TraceListener? listener = null; + + /// + /// Register the single event listener. + /// + /// The listener. + public static void RegisterListener(TraceListener? listener) + { + Log.listener = listener; + } + + /// + /// Get access to the single event listener. + /// + public static TraceListener EnsureListener() + { + if (listener == null) + throw new InvalidOperationException("No Listener registered."); + return listener; + } + + /// + /// Set the active ids to use for each event. + /// + public static int GetActiveId() + { + return activeId; + } + + public static void SetActiveId(int value) + { + activeId = value; + } + + /// + /// Log a high priority message. + /// + /// The message. + /// The message id. + public static void HighPriority(string message, int id) + { + EnsureListener().TraceEvent(TraceEventFlag.HighPriority, id, message); + } + + public static void HighPriority(string message) + { + HighPriority(message, activeId); + } + + /// + /// Log a generic infomational message. + /// + /// The message. + /// The message id. + public static void Info(string message, int id) + { + EnsureListener().TraceEvent(TraceEventFlag.Information, id, message); + } + + public static void Info(string message) + { + Info(message, activeId); + } + + /// + /// Log a diagnostic message. + /// + /// The message. + /// The message id. + public static void Diag(string message, int id) + { + EnsureListener().TraceEvent(TraceEventFlag.Diagnostic, id, message); + } + + public static void Diag(string message) + { + Diag(message, activeId); + } + + /// + /// Log a warning message. + /// + /// The message. + /// The message id. + public static void Warning(string message, int id) + { + EnsureListener().TraceEvent(TraceEventFlag.Warning, id, message); + } + + public static void Warning(string message) + { + Warning(message, activeId); + } + + /// + /// Log an error message. + /// + /// The message. + /// The message id. + public static void Error(string message, int id) + { + EnsureListener().TraceEvent(TraceEventFlag.Error, id, message); + } + + public static void Error(string message) + { + Error(message, activeId); + } + } } diff --git a/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/DotNetSDKUtilitiesUnitTests.cs b/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/DotNetSDKUtilitiesUnitTests.cs new file mode 100644 index 00000000..7fb00f09 --- /dev/null +++ b/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/DotNetSDKUtilitiesUnitTests.cs @@ -0,0 +1,109 @@ +// +// Copyright (c) Soup. All rights reserved. +// + +using Opal; +using Opal.System; +using System.Collections.Generic; +using Xunit; + +namespace Soup.Build.Discover.UnitTests +{ + [Collection("Opal")] + public class DotNetSDKUtilitiesUnitTests + { + [Fact] + public void FindDotNet6Refs() + { + // Register the test listener + var testListener = new TestTraceListener(); + using var scopedTraceListener = new ScopedTraceListenerRegister(testListener); + + var mockFileSystem = new MockFileSystem(); + using var scopedFileSystem = new ScopedSingleton(mockFileSystem); + + mockFileSystem.RegisterDirectoryChildren( + new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/"), + new List() + { + new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/5.0.0"), + new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/6.0.7"), + new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/6.0.8"), + new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/6.0.9"), + }); + + var result = DotNetSDKUtilities.FindDotNet6Refs(); + + Assert.Equal("6.0.9", result.Version); + Assert.Equal(new Path("C:/Program Files/dotnet/"), result.Path); + + // Verify expected logs + Assert.Equal( + new List() + { + "HIGH: FindNewestDotNet6RuntimeVersion: C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/", + "INFO: CheckFile: 5.0.0", + "INFO: CheckFile: 6.0.7", + "INFO: CheckFile: 6.0.8", + "INFO: CheckFile: 6.0.9", + }, + testListener.GetMessages()); + + // Verify expected file system requests + Assert.Equal( + new List() + { + "GetDirectoryChildren: C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/", + }, + mockFileSystem.GetRequests()); + } + + [Fact] + public void FindDotNet6Refs_SkipReleaseCandidate() + { + // Register the test listener + var testListener = new TestTraceListener(); + using var scopedTraceListener = new ScopedTraceListenerRegister(testListener); + + var mockFileSystem = new MockFileSystem(); + using var scopedFileSystem = new ScopedSingleton(mockFileSystem); + + mockFileSystem.RegisterDirectoryChildren( + new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/"), + new List() + { + new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/5.0.0"), + new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/6.0.7"), + new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/6.0.8"), + new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/6.0.9"), + new Path("C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/7.0.0-rc.1.22426.10"), + }); + + var result = DotNetSDKUtilities.FindDotNet6Refs(); + + Assert.Equal("6.0.9", result.Version); + Assert.Equal(new Path("C:/Program Files/dotnet/"), result.Path); + + // Verify expected logs + Assert.Equal( + new List() + { + "HIGH: FindNewestDotNet6RuntimeVersion: C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/", + "INFO: CheckFile: 5.0.0", + "INFO: CheckFile: 6.0.7", + "INFO: CheckFile: 6.0.8", + "INFO: CheckFile: 6.0.9", + "INFO: CheckFile: 7.0.0-rc.1.22426.10", + }, + testListener.GetMessages()); + + // Verify expected file system requests + Assert.Equal( + new List() + { + "GetDirectoryChildren: C:/Program Files/dotnet/packs/Microsoft.NETCore.App.Ref/", + }, + mockFileSystem.GetRequests()); + } + } +} diff --git a/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/SwhereManagerUnitTests.cs b/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/SwhereManagerUnitTests.cs index f6b9fdb6..c1c1a01e 100644 --- a/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/SwhereManagerUnitTests.cs +++ b/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/SwhereManagerUnitTests.cs @@ -11,6 +11,7 @@ namespace Soup.Build.Discover.UnitTests { + [Collection("Opal")] public class SwhereManagerUnitTests { [Fact] diff --git a/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/VSWhereUtilitiesUnitTests.cs b/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/VSWhereUtilitiesUnitTests.cs new file mode 100644 index 00000000..ce49b7f6 --- /dev/null +++ b/Source/GenerateSharp/Swhere.Core.UnitTests/Utilities/VSWhereUtilitiesUnitTests.cs @@ -0,0 +1,221 @@ +// +// Copyright (c) Soup. All rights reserved. +// + +using Opal; +using Opal.System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Soup.Build.Discover.UnitTests +{ + [Collection("Opal")] + public class VSWhereUtilitiesUnitTests + { + [Fact] + public async Task FindRoslynInstallAsync() + { + // Register the test listener + var testListener = new TestTraceListener(); + using var scopedTraceListener = new ScopedTraceListenerRegister(testListener); + + var mockProcessManager = new MockProcessManager(); + using var scopedProcessManager = new ScopedSingleton(mockProcessManager); + + mockProcessManager.RegisterExecuteResult( + "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.Roslyn.Compiler -property installationPath", + "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\n"); + + bool includePrerelease = false; + var result = await VSWhereUtilities.FindRoslynInstallAsync(includePrerelease); + + Assert.Equal(new Path("C:/Program Files/Microsoft Visual Studio/2022/Community/MSBuild/Current/Bin/Roslyn/"), result); + + // Verify expected logs + Assert.Equal( + new List() + { + "INFO: C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.Roslyn.Compiler -property installationPath", + "HIGH: Using VS Installation: C:/Program Files/Microsoft Visual Studio/2022/Community", + }, + testListener.GetMessages()); + + // Verify expected process requests + Assert.Equal( + new List() + { + "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.Roslyn.Compiler -property installationPath", + "ProcessStart: 1", + "WaitForExit: 1", + "GetStandardOutput: 1", + "GetStandardError: 1", + "GetExitCode: 1", + }, + mockProcessManager.GetRequests()); + } + + [Fact] + public async Task FindRoslynInstallAsync_Prerelease() + { + // Register the test listener + var testListener = new TestTraceListener(); + using var scopedTraceListener = new ScopedTraceListenerRegister(testListener); + + var mockProcessManager = new MockProcessManager(); + using var scopedProcessManager = new ScopedSingleton(mockProcessManager); + + mockProcessManager.RegisterExecuteResult( + "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.Roslyn.Compiler -property installationPath -prerelease", + "C:\\Program Files\\Microsoft Visual Studio\\2022\\Preview\n"); + + bool includePrerelease = true; + var result = await VSWhereUtilities.FindRoslynInstallAsync(includePrerelease); + + Assert.Equal(new Path("C:/Program Files/Microsoft Visual Studio/2022/Preview/MSBuild/Current/Bin/Roslyn/"), result); + + // Verify expected logs + Assert.Equal( + new List() + { + "INFO: C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.Roslyn.Compiler -property installationPath -prerelease", + "HIGH: Using VS Installation: C:/Program Files/Microsoft Visual Studio/2022/Preview", + }, + testListener.GetMessages()); + + // Verify expected process requests + Assert.Equal( + new List() + { + "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.Roslyn.Compiler -property installationPath -prerelease", + "ProcessStart: 1", + "WaitForExit: 1", + "GetStandardOutput: 1", + "GetStandardError: 1", + "GetExitCode: 1", + }, + mockProcessManager.GetRequests()); + } + + [Fact] + public async Task FindMSVCInstallAsync() + { + // Register the test listener + var testListener = new TestTraceListener(); + using var scopedTraceListener = new ScopedTraceListenerRegister(testListener); + + var mockProcessManager = new MockProcessManager(); + using var scopedProcessManager = new ScopedSingleton(mockProcessManager); + + var mockFileSystem = new MockFileSystem(); + using var scopedFileSystem = new ScopedSingleton(mockFileSystem); + + mockProcessManager.RegisterExecuteResult( + "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath", + "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\n"); + + mockFileSystem.CreateMockFile( + new Path("C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt"), + new MockFile(new System.IO.MemoryStream(Encoding.UTF8.GetBytes("14.33.31629\r\n")))); + + bool includePrerelease = false; + var result = await VSWhereUtilities.FindMSVCInstallAsync(includePrerelease); + + Assert.Equal("14.33.31629", result.Version); + Assert.Equal(new Path("C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.33.31629/"), result.Path); + + // Verify expected logs + Assert.Equal( + new List() + { + "INFO: C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath", + "HIGH: Using VS Installation: C:/Program Files/Microsoft Visual Studio/2022/Community", + "HIGH: Using VC Version: 14.33.31629", + }, + testListener.GetMessages()); + + // Verify expected file system requests + Assert.Equal( + new List() + { + "Exists: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt", + "OpenRead: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt", + }, + mockFileSystem.GetRequests()); + + // Verify expected process requests + Assert.Equal( + new List() + { + "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath", + "ProcessStart: 1", + "WaitForExit: 1", + "GetStandardOutput: 1", + "GetStandardError: 1", + "GetExitCode: 1", + }, + mockProcessManager.GetRequests()); + } + + [Fact] + public async Task FindMSVCInstallAsync_Prerelease() + { + // Register the test listener + var testListener = new TestTraceListener(); + using var scopedTraceListener = new ScopedTraceListenerRegister(testListener); + + var mockFileSystem = new MockFileSystem(); + using var scopedFileSystem = new ScopedSingleton(mockFileSystem); + + var mockProcessManager = new MockProcessManager(); + using var scopedProcessManager = new ScopedSingleton(mockProcessManager); + + mockProcessManager.RegisterExecuteResult( + "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath -prerelease", + "C:\\Program Files\\Microsoft Visual Studio\\2022\\Preview\n"); + + mockFileSystem.CreateMockFile( + new Path("C:/Program Files/Microsoft Visual Studio/2022/Preview/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt"), + new MockFile(new System.IO.MemoryStream(Encoding.UTF8.GetBytes("14.34.31823\r\n")))); + + bool includePrerelease = true; + var result = await VSWhereUtilities.FindMSVCInstallAsync(includePrerelease); + + Assert.Equal("14.34.31823", result.Version); + Assert.Equal(new Path("C:/Program Files/Microsoft Visual Studio/2022/Preview/VC/Tools/MSVC/14.34.31823/"), result.Path); + + // Verify expected logs + Assert.Equal( + new List() + { + "INFO: C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath -prerelease", + "HIGH: Using VS Installation: C:/Program Files/Microsoft Visual Studio/2022/Preview", + "HIGH: Using VC Version: 14.34.31823", + }, + testListener.GetMessages()); + + // Verify expected file system requests + Assert.Equal( + new List() + { + "Exists: C:/Program Files/Microsoft Visual Studio/2022/Preview/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt", + "OpenRead: C:/Program Files/Microsoft Visual Studio/2022/Preview/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt", + }, + mockFileSystem.GetRequests()); + + // Verify expected process requests + Assert.Equal( + new List() + { + "CreateProcess: 1 [./] C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath -prerelease", + "ProcessStart: 1", + "WaitForExit: 1", + "GetStandardOutput: 1", + "GetStandardError: 1", + "GetExitCode: 1", + }, + mockProcessManager.GetRequests()); + } + } +} diff --git a/Source/GenerateSharp/Swhere.Core/DotNetSDKUtilities.cs b/Source/GenerateSharp/Swhere.Core/DotNetSDKUtilities.cs index 9e39724d..d820665d 100644 --- a/Source/GenerateSharp/Swhere.Core/DotNetSDKUtilities.cs +++ b/Source/GenerateSharp/Swhere.Core/DotNetSDKUtilities.cs @@ -7,7 +7,7 @@ namespace Soup.Build.Discover { - internal static class DotNetSDKUtilities + public static class DotNetSDKUtilities { public static (string Version, Path Path) FindDotNet6Refs() { @@ -29,10 +29,12 @@ private static string FindNewestDotNet6RuntimeVersion(Path dotnetSDKInstallPath) var name = child.Path.GetFileName(); Log.Info("CheckFile: " + name); - // Parse the version string - var version = SemanticVersion.Parse(name); - if (version.Major == 6 && version > currentVersion) - currentVersion = version; + // Attempt to parse the version string + if (SemanticVersion.TryParse(name, out var version)) + { + if (version.Major == 6 && version > currentVersion) + currentVersion = version; + } } if (currentVersion == new SemanticVersion(0, 0, 0)) diff --git a/Source/GenerateSharp/Swhere.Core/VSWhereUtilities.cs b/Source/GenerateSharp/Swhere.Core/VSWhereUtilities.cs index 9fd995f7..a431e280 100644 --- a/Source/GenerateSharp/Swhere.Core/VSWhereUtilities.cs +++ b/Source/GenerateSharp/Swhere.Core/VSWhereUtilities.cs @@ -10,7 +10,7 @@ namespace Soup.Build.Discover { - internal static class VSWhereUtilities + public static class VSWhereUtilities { /// /// Attempt to find Roslyn compiler installation diff --git a/global.json b/global.json new file mode 100644 index 00000000..dc98c6e0 --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "6.0.401" + } +} \ No newline at end of file