From 5fd34008e8fea0f75e1686dce10df85dbadf50d5 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Fri, 3 Mar 2023 12:35:23 -0500 Subject: [PATCH] macOS port of the upgrade-assistant (#1409) * Use a hard-coded PackageId for Extensions.Default.Analyzers Don't use $(MSBuildProjectFullPath)* because it includes the full path and breaks NuGet caching logic on non-Windows platforms. * Do not require the ENABLE_CROSS_PLATFORM feature flag to run on macOS * Don't crash in VisualStudioFinder.Configure() due to COM exceptions * Use sudo on macOS when running `dotnet workload install maui` * Trim trailing whitespace (CRLF) from the using directive when searching for it in the template This is an actual code-change fix that helps unit tests pass * A bunch of fixes to the unit tests Most of these fixes fit into the following categories: 1. File path construction (using the correct path separator for the platform) 2. Using .ReplaceLineEndings() on strings that represent file content so we get consistent line endings when comparing expected/actual results 3. Adding TextSpan file offsets to use on Unix platforms (e.g. macOS) which will be different from the TextSpans on Windows * Fixed MappedSubTextTests (and found a legit bug in MappedSubText regarding line ending assumptions) * Fixed RazorHelperUpdaterTests File paths needed to be sorted. The returned order is different on macOS than on Windows for some reason. * Fixed RazorMappedTextReplacerTests and RazorSourceUpdaterTests Use .ReplaceLineEndings() on source strings * Fixed WCFUpdaterTests * Fixed pruning of duplicate Compile/None items on macOS Needed to canonicalize the EvaluatedInclude paths before comparing them. * More path directory separator fixes * Need to compare item.Include/EvaluatedInclude using canonical paths * Moved GetProjectName() call out of the inner loop * More canonicalization of paths when used in comparisons * More canonicalization of paths * Fixed up paths in and * Needed to add more .ReplaceLineEndings() in the unit tests for Razor * Only use the VisualStudioPath/Version on the Windows platform * Create a temp MSBuildExtensionsPath on macOS The upgrade-assistant uses dotnet's MSBuild while older Xamarin.iOS/Mac/Android/etc projects used Mono's XBuild (an MSBuild clone). Unfortunately, there's no way to specify that dotnet's MSBuild should look in both /usr/local/share/dotnet/sdk/{version} directory *and* in the /Library/Frameworks/Mono.framework/External/xbuild directories for the $(MSBuildExtensionsPath) imports. In order to be able to load Xamarin.* projects, we need to create a temp dir that includes *both* the standard targets/props files *and* the Xamarin props/targets files and the only way to do that seems to be to create a temp directory full of symlinks. * Optimized ProjectRootElementExtensionsForConversion.GetProjectName() Instead of using projectPath.Split('/').Last() and then result.Substring(), just get the start/end indexes of the substring we want and only do 1 Substring() operation. * Add a start-up warning for MacOS * Use "dotnet-upgrade-assistant" in the temp directory path * Use a predictable ~/.dotnet-upgrade-assistant/dotnet-sdk/{version} directory * Use !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) when deciding whether or not to use sudo dotnet commands * Disable the PCL test on non-Windows * Disable some DependencyInjections Options tests on non-Windows platforms * Disable WinUI and WPF migrations on non-Windows platforms. These probably don't make sense to try to migrate on macOS. * Disable Razor UpgradeSteps on non-Windows platforms * Disable VisualBasic and WCF UpgradeSteps on non-Windows platforms * Updated warning message for MacOS * Reduce code duplication in conversion between file path separators * Don't swallow exceptions thrown while creating MSBuildExtensionsPath symlinks Surface these exceptions to the user. * Removed FIXME that is no longer necessary * File.Exists() returns false for symlinks to directories Use a different approach to avoid exceptions trying to create a symlink that already exists. --- .../LocalizedStrings.Designer.cs | 9 + .../LocalizedStrings.resx | 3 + .../Program.cs | 6 +- .../FeatureFlags.cs | 4 - .../PathHelpers.cs | 33 +++ .../MSBuildProject.File.cs | 16 +- .../MSBuildWorkspaceUpgradeContext.cs | 116 ++++++++- .../UpgraderMsBuildExtensions.cs | 4 +- .../VisualStudioFinder.cs | 11 +- .../PackageUpdaterStep.cs | 19 +- ...istant.Extensions.Default.Analyzers.csproj | 2 +- .../MauiWorkloadUpgradeStep.cs | 23 +- .../MSBuild.Abstractions/PathComparer.cs | 49 ++++ .../MSBuild.Abstractions/PathHelpers.cs | 33 +++ .../ProjectItemComparer.cs | 47 +++- ...ojectRootElementExtensionsForConversion.cs | 26 +- .../EnableMyDotSupportSubStep.cs | 3 +- .../VisualBasicProjectUpdaterStep.cs | 3 +- .../SourceCodeUpdater.cs | 6 +- .../WCFUpdateStep.cs | 85 ++++-- .../MappedSubText.cs | 17 +- .../RazorUpdaterStep.cs | 3 +- .../RazorUpdaterSubStep.cs | 3 +- .../WindowsDesktopUpdateStep.cs | 5 +- .../WindowsDesktopUpdaterSubStep.cs | 3 +- .../ExtensionConfigurationTests.cs | 16 ++ .../BackupStepTests.cs | 9 + .../AnalyzerTests.cs | 244 +++++++++--------- .../ExpectedDiagnostic.cs | 13 +- .../SourceCodeUpdaterTest.cs | 5 +- .../UpdaterFactoryTest.cs | 9 + .../WCFUpdateStepTest.cs | 43 +-- .../MappedSubTextTests.cs | 12 +- .../RazorHelperUpdaterTests.cs | 18 +- .../RazorMappedTextReplacerTests.cs | 16 +- .../RazorSourceUpdaterTests.cs | 24 +- .../RazorUpdaterStepTests.cs | 6 + .../ExpectedDiagnostic.cs | 13 +- .../UWPToWinAppSDKTests.cs | 59 ++--- tests/tool/Integration.Tests/E2ETest.cs | 64 +++-- 40 files changed, 775 insertions(+), 305 deletions(-) create mode 100644 src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions/PathHelpers.cs create mode 100644 src/extensions/try-convert/MSBuild.Abstractions/PathComparer.cs create mode 100644 src/extensions/try-convert/MSBuild.Abstractions/PathHelpers.cs diff --git a/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/LocalizedStrings.Designer.cs b/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/LocalizedStrings.Designer.cs index 3f3128f1c..233723e32 100644 --- a/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/LocalizedStrings.Designer.cs +++ b/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/LocalizedStrings.Designer.cs @@ -240,6 +240,15 @@ internal static string ListExtensionItem { } } + /// + /// Looks up a localized string similar to MacOS support for this tool is limited to migrating Xamarin.Forms to MAUI. Other migration paths are not supported and may or may not work correctly.. + /// + internal static string MacOSWarning { + get { + return ResourceManager.GetString("MacOSWarning", resourceCulture); + } + } + /// /// Looks up a localized string similar to This tool is not supported on non-Windows platforms due to dependencies on Visual Studio.. /// diff --git a/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/LocalizedStrings.resx b/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/LocalizedStrings.resx index a7528b134..90f3cef39 100644 --- a/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/LocalizedStrings.resx +++ b/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/LocalizedStrings.resx @@ -178,6 +178,9 @@ {Name}: {Source} + + MacOS support for this tool is limited to migrating Xamarin.Forms to MAUI. Other migration paths are not supported and may or may not work correctly. + This tool is not supported on non-Windows platforms due to dependencies on Visual Studio. diff --git a/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/Program.cs b/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/Program.cs index e30aa2650..b8de68f0a 100644 --- a/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/Program.cs +++ b/src/cli/Microsoft.DotNet.UpgradeAssistant.Cli/Program.cs @@ -17,7 +17,11 @@ public static class Program { public static Task Main(string[] args) { - if (FeatureFlags.IsWindowsCheckEnabled && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + Console.WriteLine(LocalizedStrings.MacOSWarning); + } + else if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Console.WriteLine(LocalizedStrings.NonWindowsWarning); return Task.FromResult(ErrorCodes.PlatformNotSupported); diff --git a/src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions.Internal/FeatureFlags.cs b/src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions.Internal/FeatureFlags.cs index 54414d131..347d5a700 100644 --- a/src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions.Internal/FeatureFlags.cs +++ b/src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions.Internal/FeatureFlags.cs @@ -13,12 +13,10 @@ public static class FeatureFlags { private const string AnalyzeBinaries = "ANALYZE_BINARIES"; private const string SolutionWideSdkConversion = "SOLUTION_WIDE_SDK_CONVERSION"; - private const string EnableCrossPlatform = "ENABLE_CROSS_PLATFORM"; public static readonly IReadOnlyCollection RegisteredFeatures = new[] { SolutionWideSdkConversion, - EnableCrossPlatform, AnalyzeBinaries }; @@ -38,8 +36,6 @@ private static ICollection CreateFeatures() public static bool IsRegistered(string name) => _features.Contains(name); - public static bool IsWindowsCheckEnabled => !_features.Contains(EnableCrossPlatform); - public static bool IsAnalyzeBinariesEnabled => _features.Contains(AnalyzeBinaries); public static bool IsSolutionWideSdkConversionEnabled => _features.Contains(SolutionWideSdkConversion); diff --git a/src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions/PathHelpers.cs b/src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions/PathHelpers.cs new file mode 100644 index 000000000..adf469935 --- /dev/null +++ b/src/common/Microsoft.DotNet.UpgradeAssistant.Abstractions/PathHelpers.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace Microsoft.DotNet.UpgradeAssistant +{ + public static class PathHelpers + { + public static string GetNativePath(string path) + { + if (Path.DirectorySeparatorChar == '/') + { + return path.Replace('\\', '/'); + } + + return path; + } + + public static string GetIncludePath(string path) + { + if (Path.DirectorySeparatorChar == '/') + { + return path.Replace('/', '\\'); + } + + return path; + } + } +} diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.File.cs b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.File.cs index 326454583..7d9e50431 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.File.cs +++ b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildProject.File.cs @@ -164,17 +164,17 @@ public void AddItem(ProjectItemDescriptor projectItem) var item = ProjectRoot.CreateItemElement(projectItem.ItemType.Name); if (projectItem.Include is not null) { - item.Include = projectItem.Include; + item.Include = PathHelpers.GetIncludePath(projectItem.Include); } if (projectItem.Exclude is not null) { - item.Exclude = projectItem.Remove; + item.Exclude = PathHelpers.GetIncludePath(projectItem.Exclude); } if (projectItem.Remove is not null) { - item.Remove = projectItem.Remove; + item.Remove = PathHelpers.GetIncludePath(projectItem.Remove); } itemGroup.AppendChild(item); @@ -230,9 +230,11 @@ public void RemoveProperty(string propertyName) } } - private static string GetPathRelativeToProject(string path, string projectDir) => - Path.IsPathFullyQualified(path) - ? path - : Path.Combine(projectDir, path); + private static string GetPathRelativeToProject(string path, string projectDir) + { + path = PathHelpers.GetNativePath(path); + + return Path.IsPathFullyQualified(path) ? path : Path.Combine(projectDir, path); + } } } diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildWorkspaceUpgradeContext.cs b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildWorkspaceUpgradeContext.cs index d8b31d5ac..5ff97223c 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildWorkspaceUpgradeContext.cs +++ b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/MSBuildWorkspaceUpgradeContext.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Build.Evaluation; @@ -18,6 +19,8 @@ namespace Microsoft.DotNet.UpgradeAssistant.MSBuild { internal sealed class MSBuildWorkspaceUpgradeContext : IUpgradeContext, IDisposable { + private const string MacOSMonoFrameworkMSBuildExtensionsDir = "/Library/Frameworks/Mono.framework/External/xbuild"; + private readonly ILogger _logger; private readonly Dictionary _projectCache; private readonly IOptions _options; @@ -140,20 +143,119 @@ public IEnumerable Projects } } + private static void CreateSymbolicLinks(string targetDir, string sourceDir) + { + foreach (var entry in Directory.EnumerateFileSystemEntries(sourceDir)) + { + var target = Path.Combine(targetDir, Path.GetFileName(entry)); + + var fileInfo = new FileInfo(target); + if (fileInfo.Exists) + { + if (fileInfo.LinkTarget is not null && fileInfo.LinkTarget.Equals(entry, StringComparison.Ordinal)) + { + continue; + } + + File.Delete(target); + } + else + { + var dirInfo = new DirectoryInfo(target); + if (dirInfo.Exists) + { + if (dirInfo.LinkTarget is not null && dirInfo.LinkTarget.Equals(entry, StringComparison.Ordinal)) + { + continue; + } + + Directory.Delete(target); + } + } + + File.CreateSymbolicLink(target, entry); + } + } + + private static string? GetMacOSMSBuildExtensionsPath(WorkspaceOptions options) + { + const string DefaultDotnetSdkLocation = "/usr/local/share/dotnet/sdk/"; + + if (options.MSBuildPath == null || !options.MSBuildPath.StartsWith(DefaultDotnetSdkLocation, StringComparison.Ordinal)) + { + return null; + } + + string? msbuildExtensionsPath = null; + + if (Directory.Exists(MacOSMonoFrameworkMSBuildExtensionsDir)) + { + // Check to see if the specified MSBuildPath contains the Mono.framework build extensions. + var monoExtensionDirectories = Directory.GetDirectories(MacOSMonoFrameworkMSBuildExtensionsDir); + var createTempExtensionsDir = false; + + foreach (var monoExtensionDir in monoExtensionDirectories) + { + var dotnetExtensionDir = Path.Combine(options.MSBuildPath, Path.GetFileName(monoExtensionDir)); + if (!Directory.Exists(dotnetExtensionDir)) + { + createTempExtensionsDir = true; + break; + } + } + + // If the specified MSBuildPath does not contain the Mono.framework build extensions, create a temp + // directory that we'll use to symlink everything. + if (createTempExtensionsDir) + { + var homeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + var versionDir = Path.GetFileName(options.MSBuildPath.TrimEnd('/')); + + msbuildExtensionsPath = Path.Combine(homeDir, ".dotnet-upgrade-assistant", "dotnet-sdk", versionDir); + + if (!Directory.Exists(msbuildExtensionsPath)) + { + Directory.CreateDirectory(msbuildExtensionsPath); + } + + // First, create symbolic links to all of the dotnet MSBuild file system entries. + CreateSymbolicLinks(msbuildExtensionsPath, options.MSBuildPath); + + // Then create the symbolic links to the Mono.framework/External/xbuild system entries. + CreateSymbolicLinks(msbuildExtensionsPath, MacOSMonoFrameworkMSBuildExtensionsDir); + } + } + + return msbuildExtensionsPath; + } + private static Dictionary CreateProperties(WorkspaceOptions options) { var properties = new Dictionary(); - if (options.VisualStudioPath is string vsPath) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - properties.Add("VSINSTALLDIR", vsPath); - properties.Add("MSBuildExtensionsPath32", Path.Combine(vsPath, "MSBuild")); - properties.Add("MSBuildExtensionsPath", Path.Combine(vsPath, "MSBuild")); - } + if (options.VisualStudioPath is string vsPath) + { + properties.Add("VSINSTALLDIR", vsPath); + properties.Add("MSBuildExtensionsPath32", Path.Combine(vsPath, "MSBuild")); + properties.Add("MSBuildExtensionsPath", Path.Combine(vsPath, "MSBuild")); + } - if (options.VisualStudioVersion is int version) + if (options.VisualStudioVersion is int version) + { + properties.Add("VisualStudioVersion", $"{version}.0"); + } + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - properties.Add("VisualStudioVersion", $"{version}.0"); + var msbuildExtensionsPath = GetMacOSMSBuildExtensionsPath(options); + + if (msbuildExtensionsPath != null) + { + properties.Add("MSBuildExtensionsPath32", msbuildExtensionsPath); + properties.Add("MSBuildExtensionsPath", msbuildExtensionsPath); + } } return properties; diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/UpgraderMsBuildExtensions.cs b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/UpgraderMsBuildExtensions.cs index 22805ae7d..2cfbbbcd0 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/UpgraderMsBuildExtensions.cs +++ b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/UpgraderMsBuildExtensions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.IO; using System.Linq; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; @@ -78,7 +79,8 @@ internal static void WorkAroundRoslynIssue36781(this ProjectRootElement rootElem } // Skip items that are only included once - if (project.Items.Count(i2 => i2.EvaluatedInclude.Equals(i.EvaluatedInclude, StringComparison.Ordinal)) <= 1) + var path = PathHelpers.GetIncludePath(i.EvaluatedInclude); + if (project.Items.Count(i2 => PathHelpers.GetIncludePath(i2.EvaluatedInclude).Equals(path, StringComparison.OrdinalIgnoreCase)) <= 1) { return false; } diff --git a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/VisualStudioFinder.cs b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/VisualStudioFinder.cs index 6e2e39563..f637adb0d 100644 --- a/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/VisualStudioFinder.cs +++ b/src/components/Microsoft.DotNet.UpgradeAssistant.MSBuild/VisualStudioFinder.cs @@ -30,7 +30,16 @@ public VisualStudioFinder(ILogger logger) public void Configure(WorkspaceOptions options) { - (options.VisualStudioPath, options.VisualStudioVersion) = GetLatestVisualStudioPath(options.VisualStudioPath); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + (options.VisualStudioPath, options.VisualStudioVersion) = GetLatestVisualStudioPath(options.VisualStudioPath); + } + else + { + // MSBuildWorkspaceUpgradeContext.CreateProperties() uses the VS path to set the MSBuildExtensionsPath[32] + // environment variables and there is some logging in UpgraderMsBuildExtensions.AddMsBuild(). + _logger.LogInformation("Visual Studio path not required on macOS"); + } } private (string? Path, int? Version) GetLatestVisualStudioPath(string? suppliedPath) diff --git a/src/extensions/default/Microsoft.DotNet.UpgradeAssistant.Steps.Packages/PackageUpdaterStep.cs b/src/extensions/default/Microsoft.DotNet.UpgradeAssistant.Steps.Packages/PackageUpdaterStep.cs index 99bcbe415..a8d6cb8ba 100644 --- a/src/extensions/default/Microsoft.DotNet.UpgradeAssistant.Steps.Packages/PackageUpdaterStep.cs +++ b/src/extensions/default/Microsoft.DotNet.UpgradeAssistant.Steps.Packages/PackageUpdaterStep.cs @@ -163,15 +163,20 @@ private static IEnumerable> UpdatePackageAddition(IDep var containsService = false; foreach (var f in files) { - var root = CSharpSyntaxTree.ParseText(File.ReadAllText(f)).GetRoot(); - if (ContainsIdentifier(root, "ChannelFactory") || ContainsIdentifier(root, "ClientBase")) - { - return packages.Additions; - } + var path = PathHelpers.GetNativePath(f); - if (!containsService && ContainsIdentifier(root, "ServiceHost")) + if (File.Exists(path)) { - containsService = true; + var root = CSharpSyntaxTree.ParseText(File.ReadAllText(path)).GetRoot(); + if (ContainsIdentifier(root, "ChannelFactory") || ContainsIdentifier(root, "ClientBase")) + { + return packages.Additions; + } + + if (!containsService && ContainsIdentifier(root, "ServiceHost")) + { + containsService = true; + } } } diff --git a/src/extensions/default/analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.csproj b/src/extensions/default/analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.csproj index 9d90b4a24..b61c2401e 100644 --- a/src/extensions/default/analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.csproj +++ b/src/extensions/default/analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.csproj @@ -3,7 +3,7 @@ netstandard2.0 false - *$(MSBuildProjectFullPath)* + Real.Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers diff --git a/src/extensions/maui/Microsoft.DotNet.UpgradeAssistant.Extensions.Maui/MauiWorkloadUpgradeStep.cs b/src/extensions/maui/Microsoft.DotNet.UpgradeAssistant.Extensions.Maui/MauiWorkloadUpgradeStep.cs index b985c1d9c..61e3dcc71 100644 --- a/src/extensions/maui/Microsoft.DotNet.UpgradeAssistant.Extensions.Maui/MauiWorkloadUpgradeStep.cs +++ b/src/extensions/maui/Microsoft.DotNet.UpgradeAssistant.Extensions.Maui/MauiWorkloadUpgradeStep.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -63,7 +64,7 @@ protected override async Task ApplyImplAsync(IUpgradeCon } StringBuilder resultBuilder = new(); - _installSucceeded = await RunDotnetCommandAsync(context, "workload install maui", (_, message) => + _installSucceeded = await RunDotnetCommandAsync(context, "workload install maui", true, (_, message) => { resultBuilder.AppendLine(message); return LogLevel.Information; @@ -106,14 +107,14 @@ protected async override Task InitializeImplAsync(I // We only need to display the dotnet info debug information once // Save the output to add to result details in case of a workload install failure - await RunDotnetCommandAsync(context, "--info", (_, message) => + await RunDotnetCommandAsync(context, "--info", false, (_, message) => { _infoResult.AppendLine(message); return LogLevel.Debug; }, token).ConfigureAwait(false); } - var result = await RunDotnetCommandAsync(context, "workload list", (_, message) => + var result = await RunDotnetCommandAsync(context, "workload list", false, (_, message) => { var workload = message.Split(' ').First(); if (MauiWorkloadMap.TryGetValue(workload, out var component)) @@ -172,19 +173,27 @@ protected override async Task IsApplicableImplAsync(IUpgradeContext contex } /// - /// Run specified `dotnet workload` command. + /// Run specified `dotnet` command. /// - public Task RunDotnetCommandAsync(IUpgradeContext context, string command, Func getMessageLogLevel, CancellationToken token) + public Task RunDotnetCommandAsync(IUpgradeContext context, string arguments, bool requiresAdmin, Func getMessageLogLevel, CancellationToken token) { if (context is null) { throw new ArgumentNullException(nameof(context)); } + string command = "dotnet"; + + if (requiresAdmin && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + arguments = $"dotnet {arguments}"; + command = "sudo"; + } + return _runner.RunProcessAsync(new ProcessInfo { - Command = "dotnet", - Arguments = command, + Command = command, + Arguments = arguments, EnvironmentVariables = context.GlobalProperties, Name = "dotnet", GetMessageLogLevel = getMessageLogLevel, diff --git a/src/extensions/try-convert/MSBuild.Abstractions/PathComparer.cs b/src/extensions/try-convert/MSBuild.Abstractions/PathComparer.cs new file mode 100644 index 000000000..65fcaf2bf --- /dev/null +++ b/src/extensions/try-convert/MSBuild.Abstractions/PathComparer.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace MSBuild.Abstractions +{ + public class PathComparer : IEqualityComparer + { + public static readonly PathComparer Default = new PathComparer(); + + public PathComparer() + { + } + + public bool Equals([AllowNull] string x, [AllowNull] string y) + { + if (x == null && y == null) + { + return true; + } + + if (x == null) + { + return false; + } + + if (y == null) + { + return false; + } + + var xPath = PathHelpers.GetIncludePath(x); + var yPath = PathHelpers.GetIncludePath(y); + + return xPath.Equals(yPath, StringComparison.OrdinalIgnoreCase); + } + + public int GetHashCode(string obj) + { + var path = PathHelpers.GetIncludePath(obj ?? string.Empty); + + return path.GetHashCode(); + } + } +} diff --git a/src/extensions/try-convert/MSBuild.Abstractions/PathHelpers.cs b/src/extensions/try-convert/MSBuild.Abstractions/PathHelpers.cs new file mode 100644 index 000000000..57cc379a8 --- /dev/null +++ b/src/extensions/try-convert/MSBuild.Abstractions/PathHelpers.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; + +namespace MSBuild.Abstractions +{ + public static class PathHelpers + { + public static string GetNativePath(string path) + { + if (Path.DirectorySeparatorChar == '/') + { + return path.Replace('\\', '/'); + } + + return path; + } + + public static string GetIncludePath(string path) + { + if (Path.DirectorySeparatorChar == '/') + { + return path.Replace('/', '\\'); + } + + return path; + } + } +} diff --git a/src/extensions/try-convert/MSBuild.Abstractions/ProjectItemComparer.cs b/src/extensions/try-convert/MSBuild.Abstractions/ProjectItemComparer.cs index ff96de281..ea286e3ad 100644 --- a/src/extensions/try-convert/MSBuild.Abstractions/ProjectItemComparer.cs +++ b/src/extensions/try-convert/MSBuild.Abstractions/ProjectItemComparer.cs @@ -1,8 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Linq; namespace MSBuild.Abstractions @@ -12,8 +14,8 @@ public class ProjectItemComparer : IEqualityComparer private readonly bool _compareMetadata; #pragma warning disable SA1401 // Fields should be private - this enables sugar like ProjectItemComparer.IncludeComparer - public static ProjectItemComparer IncludeComparer = new ProjectItemComparer(compareMetadata: false); - public static ProjectItemComparer MetadataComparer = new ProjectItemComparer(compareMetadata: true); + public static readonly ProjectItemComparer IncludeComparer = new ProjectItemComparer(compareMetadata: false); + public static readonly ProjectItemComparer MetadataComparer = new ProjectItemComparer(compareMetadata: true); #pragma warning restore SA1401 // Fields should be private private ProjectItemComparer(bool compareMetadata) @@ -38,20 +40,43 @@ public bool Equals([AllowNull] IProjectItem x, [AllowNull] IProjectItem y) return false; } - // If y has all the metadata that x has then we declare them as equal. This is because - // the sdk can add new metadata but there's not reason to remove them during conversion. - var metadataEqual = _compareMetadata ? - x.DirectMetadata.All(xmd => y.DirectMetadata.Any( - ymd => xmd.Name.Equals(ymd.Name, System.StringComparison.OrdinalIgnoreCase) && - xmd.EvaluatedValue.Equals(ymd.EvaluatedValue, System.StringComparison.OrdinalIgnoreCase))) - : true; + if (x.ItemType != y.ItemType) + { + return false; + } + + var xPath = PathHelpers.GetIncludePath(x.EvaluatedInclude); + var yPath = PathHelpers.GetIncludePath(y.EvaluatedInclude); + + if (!xPath.Equals(yPath, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (_compareMetadata) + { + // If y has all the metadata that x has then we declare them as equal. This is because + // the sdk can add new metadata but there's not reason to remove them during conversion. + var metadataEqual = x.DirectMetadata.All(xmd => y.DirectMetadata.Any( + ymd => xmd.Name.Equals(ymd.Name, StringComparison.OrdinalIgnoreCase) && + xmd.EvaluatedValue.Equals(ymd.EvaluatedValue, StringComparison.OrdinalIgnoreCase))); - return x.ItemType == y.ItemType && x.EvaluatedInclude.Equals(y.EvaluatedInclude, System.StringComparison.OrdinalIgnoreCase) && metadataEqual; + if (!metadataEqual) + { + return false; + } + } + + return true; } public int GetHashCode(IProjectItem obj) { - return (obj.EvaluatedInclude.ToLowerInvariant() + obj.ItemType).GetHashCode(); + obj = obj ?? throw new ArgumentNullException(nameof(obj)); + + var path = PathHelpers.GetIncludePath(obj.EvaluatedInclude); + + return (obj.ItemType + ":" + path).GetHashCode(StringComparison.Ordinal); } } } diff --git a/src/extensions/try-convert/MSBuild.Conversion.Project/ProjectRootElementExtensionsForConversion.cs b/src/extensions/try-convert/MSBuild.Conversion.Project/ProjectRootElementExtensionsForConversion.cs index 5b04ea63f..73d1ec705 100644 --- a/src/extensions/try-convert/MSBuild.Conversion.Project/ProjectRootElementExtensionsForConversion.cs +++ b/src/extensions/try-convert/MSBuild.Conversion.Project/ProjectRootElementExtensionsForConversion.cs @@ -134,6 +134,8 @@ public static IProjectRootElement RemoveDefaultedProperties(this IProjectRootEle public static IProjectRootElement RemoveUnnecessaryPropertiesNotInSDKByDefault(this IProjectRootElement projectRootElement, ProjectStyle projectStyle) { + var projectName = GetProjectName(projectRootElement.FullPath); + foreach (var propGroup in projectRootElement.PropertyGroups) { foreach (var prop in propGroup.Properties) @@ -158,7 +160,7 @@ public static IProjectRootElement RemoveUnnecessaryPropertiesNotInSDKByDefault(t { propGroup.RemoveChild(prop); } - else if (ProjectPropertyHelpers.IsNameDefault(prop, GetProjectName(projectRootElement.FullPath))) + else if (ProjectPropertyHelpers.IsNameDefault(prop, projectName)) { propGroup.RemoveChild(prop); } @@ -199,8 +201,15 @@ public static IProjectRootElement RemoveUnnecessaryPropertiesNotInSDKByDefault(t static string GetProjectName(string projectPath) { - var projName = projectPath.Split('\\').Last(); - return projName.Substring(0, projName.LastIndexOf('.')); + int startIndex = projectPath.LastIndexOf(Path.DirectorySeparatorChar) + 1; + int endIndex = projectPath.LastIndexOf('.'); + + if (endIndex > startIndex) + { + return projectPath.Substring(startIndex, endIndex - startIndex); + } + + return projectPath.Substring(startIndex); } } @@ -302,7 +311,7 @@ static void UpdateBasedOnDiff(ImmutableArray itemsDiff, ProjectItemGr if (!itemTypeDiff.DefaultedItems.IsDefault) { var defaultedItems = itemTypeDiff.DefaultedItems.Select(i => i.EvaluatedInclude); - if (defaultedItems.Contains(item.Include, StringComparer.OrdinalIgnoreCase)) + if (defaultedItems.Contains(item.Include, PathComparer.Default)) { itemGroup.RemoveChild(item); } @@ -311,9 +320,9 @@ static void UpdateBasedOnDiff(ImmutableArray itemsDiff, ProjectItemGr if (!itemTypeDiff.ChangedItems.IsDefault) { var changedItems = itemTypeDiff.ChangedItems.Select(i => i.EvaluatedInclude); - if (changedItems.Contains(item.Include, StringComparer.OrdinalIgnoreCase)) + if (changedItems.Contains(item.Include, PathComparer.Default)) { - var path = item.Include; + var path = PathHelpers.GetIncludePath(item.Include); item.Include = null; item.Update = path; } @@ -348,9 +357,10 @@ public static IProjectRootElement AddItemRemovesForIntroducedItems(this IProject var itemGroup = projectRootElement.AddItemGroup(); foreach (var introducedItem in introducedItems) { - var item = itemGroup.AddItem(introducedItem.ItemType, introducedItem.EvaluatedInclude); + var include = PathHelpers.GetIncludePath(introducedItem.EvaluatedInclude); + var item = itemGroup.AddItem(introducedItem.ItemType, include); item.Include = null; - item.Remove = introducedItem.EvaluatedInclude; + item.Remove = include; } } diff --git a/src/extensions/vb/Microsoft.DotNet.UpgradeAssistant.Extensions.VisualBasic/EnableMyDotSupportSubStep.cs b/src/extensions/vb/Microsoft.DotNet.UpgradeAssistant.Extensions.VisualBasic/EnableMyDotSupportSubStep.cs index f974ce3ff..6ee950fd6 100644 --- a/src/extensions/vb/Microsoft.DotNet.UpgradeAssistant.Extensions.VisualBasic/EnableMyDotSupportSubStep.cs +++ b/src/extensions/vb/Microsoft.DotNet.UpgradeAssistant.Extensions.VisualBasic/EnableMyDotSupportSubStep.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -37,7 +38,7 @@ public EnableMyDotSupportSubStep(VisualBasicProjectUpdaterStep vbProjectUpdaterS protected override Task IsApplicableImplAsync(IUpgradeContext context, CancellationToken token) { // VB updates don't apply until a project is selected - if (context?.CurrentProject is null) + if (context?.CurrentProject is null || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return Task.FromResult(false); } diff --git a/src/extensions/vb/Microsoft.DotNet.UpgradeAssistant.Extensions.VisualBasic/VisualBasicProjectUpdaterStep.cs b/src/extensions/vb/Microsoft.DotNet.UpgradeAssistant.Extensions.VisualBasic/VisualBasicProjectUpdaterStep.cs index b68c33dbf..edcec3e28 100644 --- a/src/extensions/vb/Microsoft.DotNet.UpgradeAssistant.Extensions.VisualBasic/VisualBasicProjectUpdaterStep.cs +++ b/src/extensions/vb/Microsoft.DotNet.UpgradeAssistant.Extensions.VisualBasic/VisualBasicProjectUpdaterStep.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -61,7 +62,7 @@ public VisualBasicProjectUpdaterStep(ILogger logg protected override async Task IsApplicableImplAsync(IUpgradeContext context, CancellationToken token) { // The VisualBasicProjectUpdaterStep is only applicable when a project is loaded - if (context?.CurrentProject is null) + if (context?.CurrentProject is null || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return false; } diff --git a/src/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater/SourceCodeUpdater.cs b/src/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater/SourceCodeUpdater.cs index a5bd22c48..eb60511c7 100644 --- a/src/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater/SourceCodeUpdater.cs +++ b/src/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater/SourceCodeUpdater.cs @@ -66,9 +66,9 @@ where ContainsIdentifier("ServiceModel", r) template = Constants.TemplateUsingShort; } - var currDirectives = from d in root.DescendantNodes().OfType() - where template.IndexOf(d.ToFullString(), StringComparison.Ordinal) >= 0 - select d.ToFullString().Trim(); + var currDirectives = (from d in root.DescendantNodes().OfType() + where template.IndexOf(d.ToFullString().Trim(), StringComparison.Ordinal) >= 0 + select d.ToFullString().Trim()).ToArray(); var result = string.Empty; foreach (var line in template.Split(System.Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) { diff --git a/src/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater/WCFUpdateStep.cs b/src/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater/WCFUpdateStep.cs index 5c6028b18..8282a89af 100644 --- a/src/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater/WCFUpdateStep.cs +++ b/src/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater/WCFUpdateStep.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -69,7 +70,7 @@ public WCFUpdateStep(ILogger logger, ILoggerFactory loggerFactory _path = new FilePath(); } - protected override Task IsApplicableImplAsync(IUpgradeContext context, CancellationToken token) => Task.FromResult(context?.CurrentProject is not null); + protected override Task IsApplicableImplAsync(IUpgradeContext context, CancellationToken token) => Task.FromResult(context?.CurrentProject is not null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); protected override Task InitializeImplAsync(IUpgradeContext context, CancellationToken token) { @@ -210,29 +211,71 @@ public Task Apply() private void FindPath(IProject project) { - var csFile = project.FindFiles(".cs", ProjectItemType.Compile); - var main = from f in csFile - where File.Exists(f) && File.ReadAllText(f).Replace(" ", string.Empty).IndexOf("Main(", StringComparison.Ordinal) >= 0 - select f; - var directives = from f in csFile - where File.Exists(f) && File.ReadAllText(f).IndexOf("using System.ServiceModel", StringComparison.Ordinal) >= 0 - && File.ReadAllText(f).Replace(" ", string.Empty).IndexOf("Main(", StringComparison.Ordinal) < 0 - select f; - var config = from f in project.FindFiles(".config", ProjectItemType.None) - where File.Exists(f) && File.ReadAllText(f).IndexOf("", StringComparison.Ordinal) >= 0 - select f; - - if (!main.Any()) + var compileItems = project.FindFiles(".cs", ProjectItemType.Compile); + var configItems = project.FindFiles(".config", ProjectItemType.None); + var distinct = new HashSet(); + var serviceHostDetected = false; + var directives = new List(); + var config = new List(); + var main = new List(); + + foreach (var item in compileItems) + { + var path = PathHelpers.GetNativePath(item); + + // Note: on macOS, we'll sometimes get duplicate items because one will be from an explicit + // which will use \'s in the path and the + // other will be an implicit which will obviously use /'s. + if (distinct.Add(path) && File.Exists(path)) + { + var text = File.ReadAllText(path); + + if (text.Replace(" ", string.Empty).IndexOf("Main(", StringComparison.Ordinal) >= 0) + { + if (main.Count == 0 && text.IndexOf("ServiceHost", StringComparison.Ordinal) >= 0) + { + serviceHostDetected = true; + } + + main.Add(path); + } + else if (text.IndexOf("using System.ServiceModel", StringComparison.Ordinal) >= 0) + { + directives.Add(path); + } + } + } + + distinct.Clear(); + foreach (var item in configItems) + { + var path = PathHelpers.GetNativePath(item); + + // Note: on macOS, we'll sometimes get duplicate items because one will be from an explicit + // which will use \'s in the path and the other will be an implicit + // which will obviously use /'s. + if (distinct.Add(path) && File.Exists(path)) + { + var text = File.ReadAllText(path); + + if (text.IndexOf("", StringComparison.Ordinal) >= 0) + { + config.Add(path); + } + } + } + + if (main.Count == 0) { Logger.LogWarning("Can not find .cs file with Main() method. The project is not applicable for automated WCF update. No more work needs to be done and this step is complete."); } - else if (main.Count() > 1) + else if (main.Count > 1) { Logger.LogWarning("Found more than one .cs file with Main() method. The project is not applicable for automated WCF update. No more work needs to be done and this step is complete."); } - else if (!config.Any()) + else if (config.Count == 0) { - if (File.ReadAllText(main.Single()).IndexOf("ServiceHost", StringComparison.Ordinal) >= 0) + if (serviceHostDetected) { Logger.LogWarning("ServiceHost instance was detected in code but can not find .config file that configures system.serviceModel. " + "Automated update cannot be applied. Please update the project to CoreWCF manually (https://github.com/CoreWCF/CoreWCF)."); @@ -242,10 +285,10 @@ where File.Exists(f) && File.ReadAllText(f).IndexOf("", Str } else { - _path.MainFile = main.Single(); - Logger.LogTrace($"This following file: {main.Single()} needs source code update to replace ServiceHost instance."); - _path.Config = config.Single(); - Logger.LogTrace($"This following config file: {config.Single()} needs to be updated."); + _path.MainFile = main[0]; + Logger.LogTrace($"This following file: {main[0]} needs source code update to replace ServiceHost instance."); + _path.Config = config[0]; + Logger.LogTrace($"This following config file: {config[0]} needs to be updated."); _path.ProjectFile = project.GetFile().FilePath; Logger.LogTrace($"This following project file: {project.GetFile().FilePath} needs to be updated"); diff --git a/src/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor/MappedSubText.cs b/src/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor/MappedSubText.cs index 1a5747493..15e34d542 100644 --- a/src/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor/MappedSubText.cs +++ b/src/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor/MappedSubText.cs @@ -61,8 +61,21 @@ public static async Task> GetMappedSubTextsAsync(Docu // Close the previous sub-text if (currentMappedSubText is not null) { - // Subtract two from directive.SpanStart to account for end-of-line trivia - ret.Add(currentMappedSubText with { Text = documentText.GetSubText(new TextSpan(subTextStart, directive.SpanStart - 2 - subTextStart)) }); + // Calculate the length of the new-line sequence (which may or may not be the native Environment.NewLine length) + var text = documentText.GetSubText(new TextSpan(subTextStart, directive.SpanStart - subTextStart)); + int lineEndingLength = 0; + + if (text[text.Length - 1] == '\n') + { + lineEndingLength++; + if (text[text.Length - 2] == '\r') + { + lineEndingLength++; + } + } + + // Capture the mapped subtext minus the new-line sequence. + ret.Add(currentMappedSubText with { Text = documentText.GetSubText(new TextSpan(subTextStart, directive.SpanStart - lineEndingLength - subTextStart)) }); currentMappedSubText = null; } diff --git a/src/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor/RazorUpdaterStep.cs b/src/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor/RazorUpdaterStep.cs index 30a29e14f..4af1fdf89 100644 --- a/src/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor/RazorUpdaterStep.cs +++ b/src/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor/RazorUpdaterStep.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Razor.Extensions; @@ -81,7 +82,7 @@ public RazorUpdaterStep(IEnumerable> razorUpdaters, protected override async Task IsApplicableImplAsync(IUpgradeContext context, CancellationToken token) { // The RazorUpdaterStep is only applicable when a project is loaded - if (context?.CurrentProject is null) + if (context?.CurrentProject is null || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return false; } diff --git a/src/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor/RazorUpdaterSubStep.cs b/src/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor/RazorUpdaterSubStep.cs index bc686d45b..feac378af 100644 --- a/src/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor/RazorUpdaterSubStep.cs +++ b/src/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor/RazorUpdaterSubStep.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.Language; @@ -36,7 +37,7 @@ public RazorUpdaterSubStep(RazorUpdaterStep razorUpdaterStep, IUpdater IsApplicableImplAsync(IUpgradeContext context, CancellationToken token) { // Razor updates don't apply until a project is selected - if (context?.CurrentProject is null) + if (context?.CurrentProject is null || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return Task.FromResult(false); } diff --git a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/WindowsDesktopUpdateStep.cs b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/WindowsDesktopUpdateStep.cs index b0c114308..b626dce36 100644 --- a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/WindowsDesktopUpdateStep.cs +++ b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/WindowsDesktopUpdateStep.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -58,7 +59,7 @@ public WindowsDesktopUpdateStep(IEnumerable> winformsUpdaters /// True if the Winforms updater step might apply, false otherwise. protected override async Task IsApplicableImplAsync(IUpgradeContext context, CancellationToken token) { - if (context?.CurrentProject is null) + if (context?.CurrentProject is null || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return false; } @@ -76,7 +77,7 @@ protected override async Task IsApplicableImplAsync(IUpgradeContext contex private async Task> GetApplicableSubSteps(IUpgradeContext context, CancellationToken token) { - if (context?.CurrentProject is null) + if (context?.CurrentProject is null || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return ImmutableList.Create(); } diff --git a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/WindowsDesktopUpdaterSubStep.cs b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/WindowsDesktopUpdaterSubStep.cs index 0dadb8fbf..c030d0191 100644 --- a/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/WindowsDesktopUpdaterSubStep.cs +++ b/src/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows/WindowsDesktopUpdaterSubStep.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Immutable; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -35,7 +36,7 @@ public WindowsDesktopUpdaterSubStep(WindowsDesktopUpdateStep windowsDesktopUpdat protected override Task IsApplicableImplAsync(IUpgradeContext context, CancellationToken token) { - if (context?.CurrentProject is null) + if (context?.CurrentProject is null || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return Task.FromResult(false); } diff --git a/tests/components/Microsoft.DotNet.UpgradeAssistant.Extensions.Tests/ExtensionConfigurationTests.cs b/tests/components/Microsoft.DotNet.UpgradeAssistant.Extensions.Tests/ExtensionConfigurationTests.cs index 062bafc6f..03d92303e 100644 --- a/tests/components/Microsoft.DotNet.UpgradeAssistant.Extensions.Tests/ExtensionConfigurationTests.cs +++ b/tests/components/Microsoft.DotNet.UpgradeAssistant.Extensions.Tests/ExtensionConfigurationTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using AutoFixture; @@ -28,6 +29,11 @@ public ExtensionConfigurationTests() [Fact] public void ItemsSet() { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + // Arrange var expected = _fixture.Create(); var manifests = new[] @@ -48,6 +54,11 @@ public void ItemsSet() [Fact] public void SingleArray() { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + // Arrange var manifests = new[] { @@ -66,6 +77,11 @@ public void SingleArray() [Fact] public void LastArrayIsUsed() { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + // Arrange var manifests = new[] { diff --git a/tests/extensions/default/Microsoft.DotNet.UpgradeAssistant.Steps.Backup.Tests/BackupStepTests.cs b/tests/extensions/default/Microsoft.DotNet.UpgradeAssistant.Steps.Backup.Tests/BackupStepTests.cs index 675d584a4..e84cff30e 100644 --- a/tests/extensions/default/Microsoft.DotNet.UpgradeAssistant.Steps.Backup.Tests/BackupStepTests.cs +++ b/tests/extensions/default/Microsoft.DotNet.UpgradeAssistant.Steps.Backup.Tests/BackupStepTests.cs @@ -26,6 +26,10 @@ public class BackupStepTests [InlineData("B\\B.sln", "B\\B.csproj", true, "B.backup\\B.0")] public async Task InitializeTests(string inputPath, string projectPath, bool backupComplete, string expectedBackupPath) { + inputPath = inputPath.Replace('\\', Path.DirectorySeparatorChar); + projectPath = projectPath.Replace('\\', Path.DirectorySeparatorChar); + expectedBackupPath = expectedBackupPath.Replace('\\', Path.DirectorySeparatorChar); + // Arrange inputPath = Path.GetFullPath(Path.Combine("TestAssets", inputPath)); projectPath = Path.GetFullPath(Path.Combine("TestAssets", projectPath)); @@ -62,6 +66,11 @@ public async Task InitializeTests(string inputPath, string projectPath, bool bac [InlineData("B\\B.sln", "B\\B.csproj", true, "B.backup", "B.backup\\B.0")] public async Task ApplyTests(string inputPath, string projectPath, bool backupComplete, string expectedBaseBackupPath, string expectedBackupPath) { + inputPath = inputPath.Replace('\\', Path.DirectorySeparatorChar); + projectPath = projectPath.Replace('\\', Path.DirectorySeparatorChar); + expectedBaseBackupPath = expectedBaseBackupPath.Replace('\\', Path.DirectorySeparatorChar); + expectedBackupPath = expectedBackupPath.Replace('\\', Path.DirectorySeparatorChar); + // Arrange inputPath = Path.GetFullPath(Path.Combine("TestAssets", inputPath)); projectPath = Path.GetFullPath(Path.Combine("TestAssets", projectPath)); diff --git a/tests/extensions/default/analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test/AnalyzerTests.cs b/tests/extensions/default/analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test/AnalyzerTests.cs index 51a289474..78cfad482 100644 --- a/tests/extensions/default/analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test/AnalyzerTests.cs +++ b/tests/extensions/default/analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test/AnalyzerTests.cs @@ -28,10 +28,10 @@ public AnalyzerTests(ITestOutputHelper output) "UA0001", new[] { - new ExpectedDiagnostic("UA0001", new TextSpan(15, 17)), - new ExpectedDiagnostic("UA0001", new TextSpan(34, 23)), - new ExpectedDiagnostic("UA0001", new TextSpan(59, 37)), - new ExpectedDiagnostic("UA0001", new TextSpan(184, 11)) + new ExpectedDiagnostic("UA0001", new TextSpan(15, 17), new TextSpan(14, 17)), + new ExpectedDiagnostic("UA0001", new TextSpan(34, 23), new TextSpan(32, 23)), + new ExpectedDiagnostic("UA0001", new TextSpan(59, 37), new TextSpan(56, 37)), + new ExpectedDiagnostic("UA0001", new TextSpan(184, 11), new TextSpan(177, 11)) } }, @@ -40,10 +40,10 @@ public AnalyzerTests(ITestOutputHelper output) "UA0005", new[] { - new ExpectedDiagnostic("UA0005", new TextSpan(241, 19)), - new ExpectedDiagnostic("UA0005", new TextSpan(385, 23)), - new ExpectedDiagnostic("UA0005", new TextSpan(431, 30)), - new ExpectedDiagnostic("UA0005", new TextSpan(494, 19)) + new ExpectedDiagnostic("UA0005", new TextSpan(241, 19), new TextSpan(231, 19)), + new ExpectedDiagnostic("UA0005", new TextSpan(385, 23), new TextSpan(369, 23)), + new ExpectedDiagnostic("UA0005", new TextSpan(431, 30), new TextSpan(414, 30)), + new ExpectedDiagnostic("UA0005", new TextSpan(494, 19), new TextSpan(476, 19)) } }, @@ -52,10 +52,10 @@ public AnalyzerTests(ITestOutputHelper output) "UA0006", new[] { - new ExpectedDiagnostic("UA0006", new TextSpan(155, 38)), - new ExpectedDiagnostic("UA0006", new TextSpan(300, 20)), - new ExpectedDiagnostic("UA0006", new TextSpan(403, 44)), - new ExpectedDiagnostic("UA0006", new TextSpan(497, 42)) + new ExpectedDiagnostic("UA0006", new TextSpan(155, 38), new TextSpan(148, 38)), + new ExpectedDiagnostic("UA0006", new TextSpan(300, 20), new TextSpan(288, 20)), + new ExpectedDiagnostic("UA0006", new TextSpan(403, 44), new TextSpan(388, 44)), + new ExpectedDiagnostic("UA0006", new TextSpan(497, 42), new TextSpan(479, 42)) } }, @@ -64,16 +64,16 @@ public AnalyzerTests(ITestOutputHelper output) "UA0007", new[] { - new ExpectedDiagnostic("UA0007", new TextSpan(116, 25)), - new ExpectedDiagnostic("UA0007", new TextSpan(310, 14)), - new ExpectedDiagnostic("UA0007", new TextSpan(362, 25)), - new ExpectedDiagnostic("UA0007", new TextSpan(402, 25)), - new ExpectedDiagnostic("UA0007", new TextSpan(593, 14)), - new ExpectedDiagnostic("UA0007", new TextSpan(617, 10)), - new ExpectedDiagnostic("UA0007", new TextSpan(660, 25)), - new ExpectedDiagnostic("UA0007", new TextSpan(735, 10)), - new ExpectedDiagnostic("UA0007", new TextSpan(811, 25)), - new ExpectedDiagnostic("UA0007", new TextSpan(871, 10)), + new ExpectedDiagnostic("UA0007", new TextSpan(116, 25), new TextSpan(111, 25)), + new ExpectedDiagnostic("UA0007", new TextSpan(310, 14), new TextSpan(298, 14)), + new ExpectedDiagnostic("UA0007", new TextSpan(362, 25), new TextSpan(348, 25)), + new ExpectedDiagnostic("UA0007", new TextSpan(402, 25), new TextSpan(388, 25)), + new ExpectedDiagnostic("UA0007", new TextSpan(593, 14), new TextSpan(573, 14)), + new ExpectedDiagnostic("UA0007", new TextSpan(617, 10), new TextSpan(597, 10)), + new ExpectedDiagnostic("UA0007", new TextSpan(660, 25), new TextSpan(638, 25)), + new ExpectedDiagnostic("UA0007", new TextSpan(735, 10), new TextSpan(712, 10)), + new ExpectedDiagnostic("UA0007", new TextSpan(811, 25), new TextSpan(787, 25)), + new ExpectedDiagnostic("UA0007", new TextSpan(871, 10), new TextSpan(846, 10)), } }, @@ -82,12 +82,12 @@ public AnalyzerTests(ITestOutputHelper output) "UA0008", new[] { - new ExpectedDiagnostic("UA0008", new TextSpan(64, 24)), - new ExpectedDiagnostic("UA0008", new TextSpan(112, 9)), - new ExpectedDiagnostic("UA0008", new TextSpan(135, 24)), - new ExpectedDiagnostic("UA0008", new TextSpan(187, 13)), - new ExpectedDiagnostic("UA0008", new TextSpan(287, 9)), - new ExpectedDiagnostic("UA0008", new TextSpan(316, 24)), + new ExpectedDiagnostic("UA0008", new TextSpan(64, 24), new TextSpan(62, 24)), + new ExpectedDiagnostic("UA0008", new TextSpan(112, 9), new TextSpan(108, 9)), + new ExpectedDiagnostic("UA0008", new TextSpan(135, 24), new TextSpan(131, 24)), + new ExpectedDiagnostic("UA0008", new TextSpan(187, 13), new TextSpan(181, 13)), + new ExpectedDiagnostic("UA0008", new TextSpan(287, 9), new TextSpan(279, 9)), + new ExpectedDiagnostic("UA0008", new TextSpan(316, 24), new TextSpan(306, 24)), } }, @@ -96,9 +96,9 @@ public AnalyzerTests(ITestOutputHelper output) "UA0010", new[] { - new ExpectedDiagnostic("UA0010", new TextSpan(150, 9)), - new ExpectedDiagnostic("UA0010", new TextSpan(240, 18)), - new ExpectedDiagnostic("UA0010", new TextSpan(426, 24)), + new ExpectedDiagnostic("UA0010", new TextSpan(150, 9), new TextSpan(143, 9)), + new ExpectedDiagnostic("UA0010", new TextSpan(240, 18), new TextSpan(229, 18)), + new ExpectedDiagnostic("UA0010", new TextSpan(426, 24), new TextSpan(408, 24)), } }, @@ -107,14 +107,14 @@ public AnalyzerTests(ITestOutputHelper output) "UA0012", new[] { - new ExpectedDiagnostic("UA0012", new TextSpan(2256, 28)), - new ExpectedDiagnostic("UA0012", new TextSpan(3169, 28)), - new ExpectedDiagnostic("UA0012", new TextSpan(4096, 39)), - new ExpectedDiagnostic("UA0012", new TextSpan(4950, 39)), - new ExpectedDiagnostic("UA0012", new TextSpan(2287, 28), Language.VisualBasic), - new ExpectedDiagnostic("UA0012", new TextSpan(3212, 28), Language.VisualBasic), - new ExpectedDiagnostic("UA0012", new TextSpan(4287, 39), Language.VisualBasic), - new ExpectedDiagnostic("UA0012", new TextSpan(5150, 39), Language.VisualBasic), + new ExpectedDiagnostic("UA0012", new TextSpan(2256, 28), new TextSpan(2196, 28)), + new ExpectedDiagnostic("UA0012", new TextSpan(3169, 28), new TextSpan(3084, 28)), + new ExpectedDiagnostic("UA0012", new TextSpan(4096, 39), new TextSpan(3986, 39)), + new ExpectedDiagnostic("UA0012", new TextSpan(4950, 39), new TextSpan(4817, 39)), + new ExpectedDiagnostic("UA0012", new TextSpan(2287, 28), new TextSpan(2240, 28), Language.VisualBasic), + new ExpectedDiagnostic("UA0012", new TextSpan(3212, 28), new TextSpan(3146, 28), Language.VisualBasic), + new ExpectedDiagnostic("UA0012", new TextSpan(4287, 39), new TextSpan(4203, 39), Language.VisualBasic), + new ExpectedDiagnostic("UA0012", new TextSpan(5150, 39), new TextSpan(5049, 39), Language.VisualBasic), } }, @@ -123,125 +123,125 @@ public AnalyzerTests(ITestOutputHelper output) "HelperResultUpgrade", new[] { - new ExpectedDiagnostic("UA0002", new TextSpan(115, 12)), - new ExpectedDiagnostic("UA0002", new TextSpan(136, 32)), - new ExpectedDiagnostic("UA0002", new TextSpan(209, 32)), - new ExpectedDiagnostic("UA0002", new TextSpan(256, 12)), + new ExpectedDiagnostic("UA0002", new TextSpan(115, 12), new TextSpan(109, 12)), + new ExpectedDiagnostic("UA0002", new TextSpan(136, 32), new TextSpan(130, 32)), + new ExpectedDiagnostic("UA0002", new TextSpan(209, 32), new TextSpan(201, 32)), + new ExpectedDiagnostic("UA0002", new TextSpan(256, 12), new TextSpan(248, 12)), } }, { "HtmlStringUpgrade", new[] { - new ExpectedDiagnostic("UA0002", new TextSpan(132, 11)), - new ExpectedDiagnostic("UA0002", new TextSpan(182, 10)), - new ExpectedDiagnostic("UA0002", new TextSpan(307, 21)), - new ExpectedDiagnostic("UA0002", new TextSpan(371, 28)), - new ExpectedDiagnostic("UA0002", new TextSpan(445, 13)), - new ExpectedDiagnostic("UA0002", new TextSpan(497, 13)) + new ExpectedDiagnostic("UA0002", new TextSpan(132, 11), new TextSpan(125, 11)), + new ExpectedDiagnostic("UA0002", new TextSpan(182, 10), new TextSpan(174, 10)), + new ExpectedDiagnostic("UA0002", new TextSpan(307, 21), new TextSpan(297, 21)), + new ExpectedDiagnostic("UA0002", new TextSpan(371, 28), new TextSpan(361, 28)), + new ExpectedDiagnostic("UA0002", new TextSpan(445, 13), new TextSpan(434, 13)), + new ExpectedDiagnostic("UA0002", new TextSpan(497, 13), new TextSpan(485, 13)), } }, { "ResultUpgrade", new[] { - new ExpectedDiagnostic("UA0002", new TextSpan(240, 25)), - new ExpectedDiagnostic("UA0002", new TextSpan(382, 14)), - new ExpectedDiagnostic("UA0002", new TextSpan(423, 12)), - new ExpectedDiagnostic("UA0002", new TextSpan(477, 27)), - new ExpectedDiagnostic("UA0002", new TextSpan(513, 25)), - new ExpectedDiagnostic("UA0002", new TextSpan(563, 14)), - new ExpectedDiagnostic("UA0002", new TextSpan(612, 18)) + new ExpectedDiagnostic("UA0002", new TextSpan(240, 25), new TextSpan(230, 25)), + new ExpectedDiagnostic("UA0002", new TextSpan(382, 14), new TextSpan(367, 14)), + new ExpectedDiagnostic("UA0002", new TextSpan(423, 12), new TextSpan(406, 12)), + new ExpectedDiagnostic("UA0002", new TextSpan(477, 27), new TextSpan(458, 27)), + new ExpectedDiagnostic("UA0002", new TextSpan(513, 25), new TextSpan(494, 25)), + new ExpectedDiagnostic("UA0002", new TextSpan(563, 14), new TextSpan(543, 14)), + new ExpectedDiagnostic("UA0002", new TextSpan(612, 18), new TextSpan(590, 18)), } }, { "FilterUpgrade", new[] { - new ExpectedDiagnostic("UA0002", new TextSpan(88, 28)), - new ExpectedDiagnostic("UA0002", new TextSpan(162, 36)), - new ExpectedDiagnostic("UA0002", new TextSpan(327, 37)), - new ExpectedDiagnostic("UA0002", new TextSpan(615, 36)), - new ExpectedDiagnostic("UA0002", new TextSpan(707, 37)), - new ExpectedDiagnostic("UA0002", new TextSpan(872, 13)), - new ExpectedDiagnostic("UA0002", new TextSpan(909, 13)), - new ExpectedDiagnostic("UA0002", new TextSpan(989, 21)) + new ExpectedDiagnostic("UA0002", new TextSpan(88, 28), new TextSpan(84, 28)), + new ExpectedDiagnostic("UA0002", new TextSpan(162, 36), new TextSpan(156, 36)), + new ExpectedDiagnostic("UA0002", new TextSpan(327, 37), new TextSpan(316, 37)), + new ExpectedDiagnostic("UA0002", new TextSpan(615, 36), new TextSpan(597, 36)), + new ExpectedDiagnostic("UA0002", new TextSpan(707, 37), new TextSpan(687, 37)), + new ExpectedDiagnostic("UA0002", new TextSpan(872, 13), new TextSpan(849, 13)), + new ExpectedDiagnostic("UA0002", new TextSpan(909, 13), new TextSpan(885, 13)), + new ExpectedDiagnostic("UA0002", new TextSpan(989, 21), new TextSpan(962, 21)) } }, { "ControllerUpgrade", new[] { - new ExpectedDiagnostic("UA0002", new TextSpan(187, 13)), - new ExpectedDiagnostic("UA0002", new TextSpan(615, 29)), - new ExpectedDiagnostic("UA0002", new TextSpan(1030, 25)), - new ExpectedDiagnostic("UA0002", new TextSpan(1079, 10)), - new ExpectedDiagnostic("UA0002", new TextSpan(1102, 13)), - new ExpectedDiagnostic("UA0002", new TextSpan(1160, 25)), - new ExpectedDiagnostic("UA0002", new TextSpan(1702, 10)), - new ExpectedDiagnostic("UA0002", new TextSpan(1802, 13)), - new ExpectedDiagnostic("UA0002", new TextSpan(1873, 10)), - new ExpectedDiagnostic("UA0002", new TextSpan(1902, 13)), - new ExpectedDiagnostic("UA0002", new TextSpan(2013, 10)), - new ExpectedDiagnostic("UA0002", new TextSpan(2074, 13)), + new ExpectedDiagnostic("UA0002", new TextSpan(187, 13), new TextSpan(180, 13)), + new ExpectedDiagnostic("UA0002", new TextSpan(615, 29), new TextSpan(590, 29)), + new ExpectedDiagnostic("UA0002", new TextSpan(1030, 25), new TextSpan(986, 25)), + new ExpectedDiagnostic("UA0002", new TextSpan(1079, 10), new TextSpan(1033, 10)), + new ExpectedDiagnostic("UA0002", new TextSpan(1102, 13), new TextSpan(1056, 13)), + new ExpectedDiagnostic("UA0002", new TextSpan(1160, 25), new TextSpan(1112, 25)), + new ExpectedDiagnostic("UA0002", new TextSpan(1702, 10), new TextSpan(1636, 10)), + new ExpectedDiagnostic("UA0002", new TextSpan(1802, 13), new TextSpan(1734, 13)), + new ExpectedDiagnostic("UA0002", new TextSpan(1873, 10), new TextSpan(1804, 10)), + new ExpectedDiagnostic("UA0002", new TextSpan(1902, 13), new TextSpan(1832, 13)), + new ExpectedDiagnostic("UA0002", new TextSpan(2013, 10), new TextSpan(1939, 10)), + new ExpectedDiagnostic("UA0002", new TextSpan(2074, 13), new TextSpan(1999, 13)), - new ExpectedDiagnostic("UA0002", new TextSpan(177, 13), Language.VisualBasic), - new ExpectedDiagnostic("UA0002", new TextSpan(463, 29), Language.VisualBasic), - new ExpectedDiagnostic("UA0002", new TextSpan(982, 10), Language.VisualBasic), - new ExpectedDiagnostic("UA0002", new TextSpan(1265, 25), Language.VisualBasic), + new ExpectedDiagnostic("UA0002", new TextSpan(177, 13), new TextSpan(170, 13), Language.VisualBasic), + new ExpectedDiagnostic("UA0002", new TextSpan(463, 29), new TextSpan(445, 29), Language.VisualBasic), + new ExpectedDiagnostic("UA0002", new TextSpan(982, 10), new TextSpan(946, 10), Language.VisualBasic), + new ExpectedDiagnostic("UA0002", new TextSpan(1265, 25), new TextSpan(1218, 25), Language.VisualBasic), } }, { "AttributesTest", new[] { - new ExpectedDiagnostic("UA0010", new TextSpan(370, 9)), - new ExpectedDiagnostic("UA0010", new TextSpan(458, 20)), - new ExpectedDiagnostic("UA0010", new TextSpan(527, 21)), - new ExpectedDiagnostic("UA0010", new TextSpan(549, 33)), - new ExpectedDiagnostic("UA0010", new TextSpan(684, 24)), - new ExpectedDiagnostic("UA0010", new TextSpan(716, 22)), - new ExpectedDiagnostic("UA0010", new TextSpan(782, 4)), - new ExpectedDiagnostic("UA0010", new TextSpan(874, 13)), + new ExpectedDiagnostic("UA0010", new TextSpan(370, 9), new TextSpan(359, 9)), + new ExpectedDiagnostic("UA0010", new TextSpan(458, 20), new TextSpan(441, 20)), + new ExpectedDiagnostic("UA0010", new TextSpan(527, 21), new TextSpan(505, 21)), + new ExpectedDiagnostic("UA0010", new TextSpan(549, 33), new TextSpan(527, 33)), + new ExpectedDiagnostic("UA0010", new TextSpan(684, 24), new TextSpan(656, 24)), + new ExpectedDiagnostic("UA0010", new TextSpan(716, 22), new TextSpan(687, 22)), + new ExpectedDiagnostic("UA0010", new TextSpan(782, 4), new TextSpan(750, 4)), + new ExpectedDiagnostic("UA0010", new TextSpan(874, 13), new TextSpan(840, 13)), - new ExpectedDiagnostic("UA0010", new TextSpan(295, 11), Language.VisualBasic), - new ExpectedDiagnostic("UA0010", new TextSpan(392, 22), Language.VisualBasic), - new ExpectedDiagnostic("UA0010", new TextSpan(475, 23), Language.VisualBasic), - new ExpectedDiagnostic("UA0010", new TextSpan(500, 33), Language.VisualBasic), - new ExpectedDiagnostic("UA0010", new TextSpan(638, 26), Language.VisualBasic), - new ExpectedDiagnostic("UA0010", new TextSpan(672, 24), Language.VisualBasic), - new ExpectedDiagnostic("UA0010", new TextSpan(733, 6), Language.VisualBasic), - new ExpectedDiagnostic("UA0010", new TextSpan(845, 13), Language.VisualBasic), + new ExpectedDiagnostic("UA0010", new TextSpan(295, 11), new TextSpan(285, 11), Language.VisualBasic), + new ExpectedDiagnostic("UA0010", new TextSpan(392, 22), new TextSpan(377, 22), Language.VisualBasic), + new ExpectedDiagnostic("UA0010", new TextSpan(475, 23), new TextSpan(456, 23), Language.VisualBasic), + new ExpectedDiagnostic("UA0010", new TextSpan(500, 33), new TextSpan(481, 33), Language.VisualBasic), + new ExpectedDiagnostic("UA0010", new TextSpan(638, 26), new TextSpan(614, 26), Language.VisualBasic), + new ExpectedDiagnostic("UA0010", new TextSpan(672, 24), new TextSpan(647, 24), Language.VisualBasic), + new ExpectedDiagnostic("UA0010", new TextSpan(733, 6), new TextSpan(706, 6), Language.VisualBasic), + new ExpectedDiagnostic("UA0010", new TextSpan(845, 13), new TextSpan(815, 13), Language.VisualBasic), } }, { "ApiAlert", new[] { - new ExpectedDiagnostic("UA0013_D", new TextSpan(272, 29)), - new ExpectedDiagnostic("UA0013_D", new TextSpan(310, 29)), - new ExpectedDiagnostic("UA0013_A", new TextSpan(534, 11)), - new ExpectedDiagnostic("UA0013_A", new TextSpan(547, 23)), - new ExpectedDiagnostic("UA0013_D", new TextSpan(604, 29)), - new ExpectedDiagnostic("UA0013_D", new TextSpan(848, 29)), - new ExpectedDiagnostic("UA0013_D", new TextSpan(916, 29)), - new ExpectedDiagnostic("UA0013_E", new TextSpan(987, 15)), - new ExpectedDiagnostic("UA0013_B", new TextSpan(1046, 15)), - new ExpectedDiagnostic("UA0013_E", new TextSpan(1091, 39)), - new ExpectedDiagnostic("UA0013_E", new TextSpan(1139, 28)), - new ExpectedDiagnostic("UA0013_B", new TextSpan(1184, 11)), - new ExpectedDiagnostic("UA0013_C", new TextSpan(1249, 16)), - new ExpectedDiagnostic("UA0013_C", new TextSpan(1270, 33)), - new ExpectedDiagnostic("UA0013_G", new TextSpan(1354, 27)), - new ExpectedDiagnostic("UA0013_F", new TextSpan(1477, 23)), - new ExpectedDiagnostic("UA0013_F", new TextSpan(1568, 39)), + new ExpectedDiagnostic("UA0013_D", new TextSpan(272, 29), new TextSpan(264, 29)), + new ExpectedDiagnostic("UA0013_D", new TextSpan(310, 29), new TextSpan(301, 29)), + new ExpectedDiagnostic("UA0013_A", new TextSpan(534, 11), new TextSpan(519, 11)), + new ExpectedDiagnostic("UA0013_A", new TextSpan(547, 23), new TextSpan(532, 23)), + new ExpectedDiagnostic("UA0013_D", new TextSpan(604, 29), new TextSpan(587, 29)), + new ExpectedDiagnostic("UA0013_D", new TextSpan(848, 29), new TextSpan(825, 29)), + new ExpectedDiagnostic("UA0013_D", new TextSpan(916, 29), new TextSpan(892, 29)), + new ExpectedDiagnostic("UA0013_E", new TextSpan(987, 15), new TextSpan(960, 15)), + new ExpectedDiagnostic("UA0013_B", new TextSpan(1046, 15), new TextSpan(1018, 15)), + new ExpectedDiagnostic("UA0013_E", new TextSpan(1091, 39), new TextSpan(1061, 39)), + new ExpectedDiagnostic("UA0013_E", new TextSpan(1139, 28), new TextSpan(1109, 28)), + new ExpectedDiagnostic("UA0013_B", new TextSpan(1184, 11), new TextSpan(1153, 11)), + new ExpectedDiagnostic("UA0013_C", new TextSpan(1249, 16), new TextSpan(1217, 16)), + new ExpectedDiagnostic("UA0013_C", new TextSpan(1270, 33), new TextSpan(1238, 33)), + new ExpectedDiagnostic("UA0013_G", new TextSpan(1354, 27), new TextSpan(1319, 27)), + new ExpectedDiagnostic("UA0013_F", new TextSpan(1477, 23), new TextSpan(1440, 23)), + new ExpectedDiagnostic("UA0013_F", new TextSpan(1568, 39), new TextSpan(1530, 39)), // Once for the namespace, once for the type - new ExpectedDiagnostic("UA0013_G", new TextSpan(1658, 25)), - new ExpectedDiagnostic("UA0013_G", new TextSpan(1658, 60)), + new ExpectedDiagnostic("UA0013_G", new TextSpan(1658, 25), new TextSpan(1619, 25)), + new ExpectedDiagnostic("UA0013_G", new TextSpan(1658, 60), new TextSpan(1619, 60)), - new ExpectedDiagnostic("UA0013_H", new TextSpan(2057, 15)), - new ExpectedDiagnostic("UA0013_H", new TextSpan(2131, 29)), + new ExpectedDiagnostic("UA0013_H", new TextSpan(2057, 15), new TextSpan(2008, 15)), + new ExpectedDiagnostic("UA0013_H", new TextSpan(2131, 29), new TextSpan(2079, 29)), } }, }; @@ -323,8 +323,8 @@ public async Task UpgradeCodeFixer(string scenarioName) } using var workspace = new AdhocWorkspace(); - var fixedText = await workspace.FixSourceAsync(language, scenarioName, expectedDiagnostics.Select(d => d.Id).Distinct()).ConfigureAwait(false); - var expectedText = TestHelper.GetSource(language, $"{scenarioName}.Fixed"); + var fixedText = (await workspace.FixSourceAsync(language, scenarioName, expectedDiagnostics.Select(d => d.Id).Distinct()).ConfigureAwait(false)).ReplaceLineEndings(); + var expectedText = TestHelper.GetSource(language, $"{scenarioName}.Fixed").ReplaceLineEndings(); _output.WriteLine("Expected:"); _output.WriteLine(expectedText); @@ -342,9 +342,11 @@ private static void AssertDiagnosticsCorrect(IEnumerable diagnostics var count = 0; foreach (var d in diagnostics.OrderBy(d => d.Location.SourceSpan.Start)) { - var expected = $"{expectedDiagnostics.ElementAt(count).SourceSpan}"; + var e = expectedDiagnostics.ElementAt(count); + var expected = $"{e.SourceSpan}"; var actual = $"{d.Location.SourceSpan}"; - Assert.True(expectedDiagnostics.ElementAt(count).Matches(d), $"Expected {expectedDiagnostics.ElementAt(count).Language} diagnostic {count} to be at {expectedDiagnostics.ElementAt(count).SourceSpan}; actually at {d.Location.SourceSpan}"); + + Assert.True(e.Matches(d), $"Expected {e.Language} diagnostic {count} to be at {e.SourceSpan}; actually at {d.Location.SourceSpan}"); count++; } } diff --git a/tests/extensions/default/analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test/ExpectedDiagnostic.cs b/tests/extensions/default/analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test/ExpectedDiagnostic.cs index e04ba969f..5faa9486b 100644 --- a/tests/extensions/default/analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test/ExpectedDiagnostic.cs +++ b/tests/extensions/default/analyzers/Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers.Test/ExpectedDiagnostic.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; @@ -15,11 +16,19 @@ public class ExpectedDiagnostic public Language Language { get; } - public ExpectedDiagnostic(string id, TextSpan sourceSpan, Language lang = Language.CSharp) + public ExpectedDiagnostic(string id, TextSpan windowsSourceSpan, TextSpan unixSourceSpan, Language lang = Language.CSharp) { Id = id; - SourceSpan = sourceSpan; Language = lang; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + SourceSpan = windowsSourceSpan; + } + else + { + SourceSpan = unixSourceSpan; + } } public bool Matches(Diagnostic diagnostic) => (diagnostic?.Id.Equals(Id, StringComparison.Ordinal) ?? false) && diagnostic.Location.SourceSpan.Equals(SourceSpan); diff --git a/tests/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater.Test/SourceCodeUpdaterTest.cs b/tests/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater.Test/SourceCodeUpdaterTest.cs index e8a44fff3..b1a2eca09 100644 --- a/tests/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater.Test/SourceCodeUpdaterTest.cs +++ b/tests/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater.Test/SourceCodeUpdaterTest.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using Microsoft.CodeAnalysis.CSharp; using Microsoft.Extensions.Logging.Abstractions; using Xunit; @@ -237,11 +238,11 @@ static void Main() public void UpdateDirectivesTest(string replace, string expected) { var updater = new SourceCodeUpdater(CSharpSyntaxTree.ParseText(Input.Replace("using System.ServiceModel.Security;", replace)), Template, _logger); - var result = updater.UpdateDirectives().ToFullString().Replace(" ", string.Empty); + var result = updater.UpdateDirectives().ToFullString().Replace(" ", string.Empty).ReplaceLineEndings(); var outdated = "using System.ServiceModel;using System.ServiceModel.Security;"; Assert.DoesNotContain(outdated, result); - Assert.Contains(expected.Replace(" ", string.Empty), result); + Assert.Contains(expected.Replace(" ", string.Empty).ReplaceLineEndings(), result, StringComparison.Ordinal); } [Theory] diff --git a/tests/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater.Test/UpdaterFactoryTest.cs b/tests/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater.Test/UpdaterFactoryTest.cs index dda0335d0..f56226176 100644 --- a/tests/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater.Test/UpdaterFactoryTest.cs +++ b/tests/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater.Test/UpdaterFactoryTest.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Xml.Linq; @@ -26,6 +27,14 @@ public class UpdaterFactoryTest [InlineData("TestInputFiles\\MultiServicesConfig.txt", "TestExpectedFiles\\MultiServicesTemplateCode.txt")] public void UpdateFactoryTemplateTest(string input, string expected) { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + + input = input.Replace('\\', Path.DirectorySeparatorChar); + expected = expected.Replace('\\', Path.DirectorySeparatorChar); + var context = new ConfigContext(new ConfigUpdater(XDocument.Load(input), _configLogger)); var actual = UpdaterFactory.UpdateTemplateCode(context, _logger); Assert.Equal(File.ReadAllText(expected), actual); diff --git a/tests/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater.Test/WCFUpdateStepTest.cs b/tests/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater.Test/WCFUpdateStepTest.cs index 3a4781f69..49b6757df 100644 --- a/tests/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater.Test/WCFUpdateStepTest.cs +++ b/tests/extensions/wcf/Microsoft.DotNet.UpgradeAssistant.Extensions.WCFUpdater.Test/WCFUpdateStepTest.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Runtime.InteropServices; using System.Threading.Tasks; using Autofac.Extras.Moq; using Microsoft.CodeAnalysis.CSharp; @@ -28,10 +29,10 @@ public WCFUpdateStepTest(ITestOutputHelper output) { _output = output; original = new Dictionary(); - original.Add("directive", File.ReadAllText(Directive)); - original.Add("proj", File.ReadAllText(Proj)); - original.Add("main", File.ReadAllText(Main)); - original.Add("config", File.ReadAllText(Config)); + original.Add("directive", File.ReadAllText(Directive.Replace('\\', Path.DirectorySeparatorChar))); + original.Add("proj", File.ReadAllText(Proj.Replace('\\', Path.DirectorySeparatorChar))); + original.Add("main", File.ReadAllText(Main.Replace('\\', Path.DirectorySeparatorChar))); + original.Add("config", File.ReadAllText(Config.Replace('\\', Path.DirectorySeparatorChar))); } [Theory] @@ -42,6 +43,16 @@ public WCFUpdateStepTest(ITestOutputHelper output) [InlineData(Proj, "TestInputFiles\\MultiServicesSourceCode.txt", "TestInputFiles\\MultiServicesConfig.txt", "", UpgradeStepStatus.Incomplete)] // Multiple services case public void WCFUpdateTest(string proj, string main, string config, string directive, UpgradeStepStatus expected) { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + + proj = proj.Replace('\\', Path.DirectorySeparatorChar); + main = main.Replace('\\', Path.DirectorySeparatorChar); + config = config.Replace('\\', Path.DirectorySeparatorChar); + directive = directive.Replace('\\', Path.DirectorySeparatorChar); + // Arrange using var mock = AutoMock.GetLoose(); @@ -89,11 +100,11 @@ public void WCFUpdateTest(string proj, string main, string config, string direct try { updater.Apply(); - Assert.Equal(File.ReadAllText("TestExpectedFiles\\ExpectedConfig.txt"), File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), "TestInputFiles\\wcf.config"))); - Assert.Equal(File.ReadAllText("TestExpectedFiles\\ExpectedOldConfig.txt"), File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), config))); - Assert.Equal(File.ReadAllText("TestExpectedFiles\\ExpectedSourceCode.txt"), File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), main))); - Assert.Equal(File.ReadAllText("TestExpectedFiles\\ExpectedDirective.txt"), File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), directive))); - Assert.Equal(File.ReadAllText("TestExpectedFiles\\ExpectedProj.txt"), File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), proj))); + Assert.Equal(File.ReadAllText(Path.Combine("TestExpectedFiles", "ExpectedConfig.txt")), File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), "TestInputFiles", "wcf.config"))); + Assert.Equal(File.ReadAllText(Path.Combine("TestExpectedFiles", "ExpectedOldConfig.txt")), File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), config))); + Assert.Equal(File.ReadAllText(Path.Combine("TestExpectedFiles", "ExpectedSourceCode.txt")), File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), main))); + Assert.Equal(File.ReadAllText(Path.Combine("TestExpectedFiles", "ExpectedDirective.txt")), File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), directive))); + Assert.Equal(File.ReadAllText(Path.Combine("TestExpectedFiles", "ExpectedProj.txt")), File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), proj))); Reset(); } catch @@ -102,13 +113,13 @@ public void WCFUpdateTest(string proj, string main, string config, string direct throw; } } - else if (main.Equals("TestInputFiles\\MultiServicesSourceCode.txt", StringComparison.Ordinal)) + else if (main.Equals(Path.Combine("TestInputFiles", "MultiServicesSourceCode.txt"), StringComparison.Ordinal)) { try { updater.Apply(); - Assert.Equal(File.ReadAllText("TestExpectedFiles\\ExpectedMultiServicesConfig.txt"), File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), "TestInputFiles\\wcf.config"))); - Assert.Equal(File.ReadAllText("TestExpectedFiles\\ExpectedMultiServicesSourceCode.txt"), File.ReadAllText(main)); + Assert.Equal(File.ReadAllText(Path.Combine("TestExpectedFiles", "ExpectedMultiServicesConfig.txt")), File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), "TestInputFiles", "wcf.config"))); + Assert.Equal(File.ReadAllText(Path.Combine("TestExpectedFiles", "ExpectedMultiServicesSourceCode.txt")), File.ReadAllText(main)); Reset(); } catch @@ -121,10 +132,10 @@ public void WCFUpdateTest(string proj, string main, string config, string direct private void Reset() { - File.WriteAllText(Main, original["main"]); - File.WriteAllText(Proj, original["proj"]); - File.WriteAllText(Config, original["config"]); - File.WriteAllText(Directive, original["directive"]); + File.WriteAllText(Main.Replace('\\', Path.DirectorySeparatorChar), original["main"]); + File.WriteAllText(Proj.Replace('\\', Path.DirectorySeparatorChar), original["proj"]); + File.WriteAllText(Config.Replace('\\', Path.DirectorySeparatorChar), original["config"]); + File.WriteAllText(Directive.Replace('\\', Path.DirectorySeparatorChar), original["directive"]); } } } diff --git a/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/MappedSubTextTests.cs b/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/MappedSubTextTests.cs index ec94fe9c8..b8b6cd075 100644 --- a/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/MappedSubTextTests.cs +++ b/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/MappedSubTextTests.cs @@ -117,15 +117,15 @@ private static Document CreateDoc(AdhocWorkspace workspace, string source) => new object?[] { - File.ReadAllText("MappedSubTextTestData.cs"), + File.ReadAllText("MappedSubTextTestData.cs").ReplaceLineEndings(), "Bar.cshtml", new MappedSubText[] { - new MappedSubText(SourceText.From("// Auto-generated by Razor engine\r\n\r\nnamespace Razor\r\n{"), "Bar.cshtml", 0), - new MappedSubText(SourceText.From(" using System;\r\n"), "test.cshtml", 1), - new MappedSubText(SourceText.From("\r\n var foo = \"Hello World!\";\r\n"), "test.cshtml", 3), - new MappedSubText(SourceText.From(" __PTagHelper.FooProp = 123;\r\n"), "test.cshtml", 7), - new MappedSubText(SourceText.From(" WriteLiteral(foo);\r\n"), "test.cshtml", 7), + new MappedSubText(SourceText.From("// Auto-generated by Razor engine\r\n\r\nnamespace Razor\r\n{".ReplaceLineEndings()), "Bar.cshtml", 0), + new MappedSubText(SourceText.From(" using System;\r\n".ReplaceLineEndings()), "test.cshtml", 1), + new MappedSubText(SourceText.From("\r\n var foo = \"Hello World!\";\r\n".ReplaceLineEndings()), "test.cshtml", 3), + new MappedSubText(SourceText.From(" __PTagHelper.FooProp = 123;\r\n".ReplaceLineEndings()), "test.cshtml", 7), + new MappedSubText(SourceText.From(" WriteLiteral(foo);\r\n".ReplaceLineEndings()), "test.cshtml", 7), } } }; diff --git a/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/RazorHelperUpdaterTests.cs b/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/RazorHelperUpdaterTests.cs index 2dbef030f..425f10c0e 100644 --- a/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/RazorHelperUpdaterTests.cs +++ b/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/RazorHelperUpdaterTests.cs @@ -73,7 +73,11 @@ public async Task IsApplicableTests(string projectPath, FileUpdaterResult expect // Assert var fileUpdaterResult = Assert.IsType(result); Assert.Equal(expectedResult.Result, fileUpdaterResult.Result); - Assert.Collection(fileUpdaterResult.FilePaths, expectedResult.FilePaths.Select>(e => a => Assert.EndsWith(e, a, StringComparison.Ordinal)).ToArray()); + + var sortedFilePaths = fileUpdaterResult.FilePaths.ToList(); + sortedFilePaths.Sort(); + + Assert.Collection(sortedFilePaths, expectedResult.FilePaths.Select>(e => a => Assert.EndsWith(e.Replace('\\', Path.DirectorySeparatorChar), a, StringComparison.Ordinal)).ToArray()); } [Fact] @@ -102,14 +106,20 @@ public async Task ApplyTests(string projectPath, FileUpdaterResult expectedResul // Assert var fileUpdaterResult = Assert.IsType(result); Assert.True(fileUpdaterResult.Result); - Assert.Collection(fileUpdaterResult.FilePaths, expectedResult.FilePaths.Select>(e => a => Assert.EndsWith(e, a, StringComparison.Ordinal)).ToArray()); + + var sortedFilePaths = fileUpdaterResult.FilePaths.ToList(); + sortedFilePaths.Sort(); + + Assert.Collection(sortedFilePaths, expectedResult.FilePaths.Select>(e => a => Assert.EndsWith(e.Replace('\\', Path.DirectorySeparatorChar), a, StringComparison.Ordinal)).ToArray()); // Confirm that files are updated as expected var projectFiles = context.CurrentProject!.FileInfo.Directory!.GetFiles("*.*", new EnumerationOptions { RecurseSubdirectories = true }); var expectedFiles = new DirectoryInfo($"{Path.GetDirectoryName(projectPath)}.Fixed").GetFiles("*.*", new EnumerationOptions { RecurseSubdirectories = true }); Assert.Collection(projectFiles, expectedFiles.Select>(e => a => { - Assert.Equal(File.ReadAllText(e.FullName), File.ReadAllText(a.FullName)); + var expectedText = File.ReadAllText(e.FullName).ReplaceLineEndings(); + var actualText = File.ReadAllText(a.FullName).ReplaceLineEndings(); + Assert.Equal(expectedText, actualText); }).ToArray()); } @@ -124,7 +134,7 @@ public async Task ApplyTests(string projectPath, FileUpdaterResult expectedResul new object[] { "RazorHelperUpdaterViews/HelperInSubDir/Test.csproj", - new FileUpdaterResult("RULE0001", "RuleName", "Full Description", true, new[] { @"\OneHelper\MyView.cshtml", @"\OneHelper\AnotherHelper\MyView.cshtml" }) + new FileUpdaterResult("RULE0001", "RuleName", "Full Description", true, new[] { @"\OneHelper\AnotherHelper\MyView.cshtml", @"\OneHelper\MyView.cshtml" }) }, new object[] { diff --git a/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/RazorMappedTextReplacerTests.cs b/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/RazorMappedTextReplacerTests.cs index bc001db11..aaeb453e7 100644 --- a/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/RazorMappedTextReplacerTests.cs +++ b/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/RazorMappedTextReplacerTests.cs @@ -65,7 +65,11 @@ public void ApplyTextReplacementsPositiveTests(string testCaseId, IEnumerable>(e => a => { Assert.Equal(Path.GetFileName(e), Path.GetFileName(a)); - Assert.Equal(File.ReadAllText(e), File.ReadAllText(a)); + + var expectedText = File.ReadAllText(e).ReplaceLineEndings().TrimEnd(); + var actualText = File.ReadAllText(a).ReplaceLineEndings().TrimEnd(); + + Assert.Equal(expectedText, actualText); }).ToArray()); } @@ -98,8 +102,8 @@ public void ApplyTextReplacementsPositiveTests(string testCaseId, IEnumerable\r\n

@Model[1]

\r\n \r\n}\r\n", - "

\r\n Hi!\r\n

", + "{\r\n
\r\n

@Model[1]

\r\n
\r\n}\r\n".ReplaceLineEndings(), + "

\r\n Hi!\r\n

".ReplaceLineEndings(), GetPath("View.cshtml"), 16) } @@ -112,7 +116,7 @@ public void ApplyTextReplacementsPositiveTests(string testCaseId, IEnumerable\r\n ", "
\r\n", GetPath("View.cshtml"), 9), + new MappedTextReplacement("
\r\n ".ReplaceLineEndings(), "
\r\n".ReplaceLineEndings(), GetPath("View.cshtml"), 9), } }, @@ -122,7 +126,7 @@ public void ApplyTextReplacementsPositiveTests(string testCaseId, IEnumerable\\r\\n

\")"), + new LocationLookup("RazorSourceUpdaterStepViews\\TestViews\\View.cshtml.cs", "WriteLiteral(\"

\\r\\n

\")".ReplaceLineEndings()), }, }, new[] { GetFullPath("RazorSourceUpdaterStepViews\\TestViews\\View.cshtml") }, @@ -297,10 +297,10 @@ public async Task ApplyTests(LocationLookup[][] diagnosticLocations, string[] ex }, new[] { - new MappedTextReplacement("using Microsoft.AspNetCore.Mvc;\r\n", "using Microsoft.AspNetCore.Mvc; /* Test! */\r\n", GetFullPath("RazorSourceUpdaterStepViews\\_ViewImports.cshtml"), 1), - new MappedTextReplacement(" Write(DateTime.Now.ToString());\r\n", " Write(DateTime.Now.ToString() /* Test! */);\r\n", GetFullPath("RazorSourceUpdaterStepViews\\TestViews\\Simple.cshtml"), 1), - new MappedTextReplacement(" Write(Model[0]);\r\n", " Write(Model[0] /* Test! */);\r\n", GetFullPath("RazorSourceUpdaterStepViews\\TestViews\\View.cshtml"), 6), - new MappedTextReplacement(" Write(Model[1]);\r\n", " Write(Model[1] /* Test! */);\r\n", GetFullPath("RazorSourceUpdaterStepViews\\TestViews\\View.cshtml"), 18), + new MappedTextReplacement("using Microsoft.AspNetCore.Mvc;\r\n".ReplaceLineEndings(), "using Microsoft.AspNetCore.Mvc; /* Test! */\r\n".ReplaceLineEndings(), GetFullPath("RazorSourceUpdaterStepViews\\_ViewImports.cshtml"), 1), + new MappedTextReplacement(" Write(DateTime.Now.ToString());\r\n".ReplaceLineEndings(), " Write(DateTime.Now.ToString() /* Test! */);\r\n".ReplaceLineEndings(), GetFullPath("RazorSourceUpdaterStepViews\\TestViews\\Simple.cshtml"), 1), + new MappedTextReplacement(" Write(Model[0]);\r\n".ReplaceLineEndings(), " Write(Model[0] /* Test! */);\r\n".ReplaceLineEndings(), GetFullPath("RazorSourceUpdaterStepViews\\TestViews\\View.cshtml"), 6), + new MappedTextReplacement(" Write(Model[1]);\r\n".ReplaceLineEndings(), " Write(Model[1] /* Test! */);\r\n".ReplaceLineEndings(), GetFullPath("RazorSourceUpdaterStepViews\\TestViews\\View.cshtml"), 18), } } }; @@ -380,9 +380,11 @@ private static async Task> GetRazorCodeDocumentsA return updaterStep.RazorDocuments; } - private static string GetFullPath(string path) => - Path.IsPathFullyQualified(path) - ? path - : Path.Combine(AppContext.BaseDirectory, path); + private static string GetFullPath(string path) + { + path = path.Replace('\\', Path.DirectorySeparatorChar); + + return Path.IsPathFullyQualified(path) ? path : Path.Combine(AppContext.BaseDirectory, path); + } } } diff --git a/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/RazorUpdaterStepTests.cs b/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/RazorUpdaterStepTests.cs index e1e551806..4406c1fef 100644 --- a/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/RazorUpdaterStepTests.cs +++ b/tests/extensions/web/Microsoft.DotNet.UpgradeAssistant.Steps.Razor.Tests/RazorUpdaterStepTests.cs @@ -5,6 +5,7 @@ using System.Collections.Immutable; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -67,6 +68,11 @@ public void NegativeCtorTests() [InlineData("RazorSourceUpdaterStepViews/Test.csproj", 1, 0, true, true)] // Applicable even with only complete updaters (updater status is not checked) public async Task IsApplicableTests(string projectPath, int completeUpdaterCount, int incompleteUpdaterCount, bool projectLoaded, bool expected) { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + expected = false; + } + // Arrange using var mock = GetMock(projectLoaded, projectPath, completeUpdaterCount, incompleteUpdaterCount); var step = mock.Create(); diff --git a/tests/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.Tests/ExpectedDiagnostic.cs b/tests/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.Tests/ExpectedDiagnostic.cs index df70b9e0e..eb7b82fe9 100644 --- a/tests/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.Tests/ExpectedDiagnostic.cs +++ b/tests/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.Tests/ExpectedDiagnostic.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; @@ -15,11 +16,19 @@ public class ExpectedDiagnostic public Language Language { get; } - public ExpectedDiagnostic(string id, TextSpan sourceSpan, Language lang = Language.CSharp) + public ExpectedDiagnostic(string id, TextSpan windowsSourceSpan, TextSpan unixSourceSpan, Language lang = Language.CSharp) { Id = id; - SourceSpan = sourceSpan; Language = lang; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + SourceSpan = windowsSourceSpan; + } + else + { + SourceSpan = unixSourceSpan; + } } public bool Matches(Diagnostic diagnostic) => (diagnostic?.Id.Equals(Id, StringComparison.Ordinal) ?? false) && diagnostic.Location.SourceSpan.Equals(SourceSpan); diff --git a/tests/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.Tests/UWPToWinAppSDKTests.cs b/tests/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.Tests/UWPToWinAppSDKTests.cs index 830482a7b..5af4c9e79 100644 --- a/tests/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.Tests/UWPToWinAppSDKTests.cs +++ b/tests/extensions/windows/Microsoft.DotNet.UpgradeAssistant.Extensions.Windows.Tests/UWPToWinAppSDKTests.cs @@ -24,69 +24,69 @@ public class UWPToWinAppSDKTests "ContentDialogCaller", new[] { - new ExpectedDiagnostic(WinUIContentDialogAnalyzer.DiagnosticId, new TextSpan(729, 20)) + new ExpectedDiagnostic(WinUIContentDialogAnalyzer.DiagnosticId, new TextSpan(729, 20), new TextSpan(707, 20)) } }, { "InitializeWithWindow", new[] { - new ExpectedDiagnostic(WinUIInitializeWindowAnalyzer.DiagnosticId, new TextSpan(415, 20)), - new ExpectedDiagnostic(WinUIInitializeWindowAnalyzer.DiagnosticId, new TextSpan(469, 18)) + new ExpectedDiagnostic(WinUIInitializeWindowAnalyzer.DiagnosticId, new TextSpan(415, 20), new TextSpan(399, 20)), + new ExpectedDiagnostic(WinUIInitializeWindowAnalyzer.DiagnosticId, new TextSpan(469, 18), new TextSpan(452, 18)) } }, { "DataTransferManagerCaller", new[] { - new ExpectedDiagnostic(WinUIDataTransferManagerAnalyzer.DiagnosticId, new TextSpan(413, 33)), - new ExpectedDiagnostic(WinUIDataTransferManagerAnalyzer.DiagnosticId, new TextSpan(461, 53)), - new ExpectedDiagnostic(WinUIDataTransferManagerAnalyzer.DiagnosticId, new TextSpan(529, 71)) + new ExpectedDiagnostic(WinUIDataTransferManagerAnalyzer.DiagnosticId, new TextSpan(413, 33), new TextSpan(397, 33)), + new ExpectedDiagnostic(WinUIDataTransferManagerAnalyzer.DiagnosticId, new TextSpan(461, 53), new TextSpan(444, 53)), + new ExpectedDiagnostic(WinUIDataTransferManagerAnalyzer.DiagnosticId, new TextSpan(529, 71), new TextSpan(511, 71)) } }, { "InteropsCaller", new[] { - new ExpectedDiagnostic(WinUIInteropAnalyzer.DiagnosticId, new TextSpan(481, 60)), - new ExpectedDiagnostic(WinUIInteropAnalyzer.DiagnosticId, new TextSpan(570, 92)), - new ExpectedDiagnostic(WinUIInteropAnalyzer.DiagnosticId, new TextSpan(746, 39)), - new ExpectedDiagnostic(WinUIInteropAnalyzer.DiagnosticId, new TextSpan(808, 91)) + new ExpectedDiagnostic(WinUIInteropAnalyzer.DiagnosticId, new TextSpan(481, 60), new TextSpan(464, 60)), + new ExpectedDiagnostic(WinUIInteropAnalyzer.DiagnosticId, new TextSpan(570, 92), new TextSpan(552, 92)), + new ExpectedDiagnostic(WinUIInteropAnalyzer.DiagnosticId, new TextSpan(746, 39), new TextSpan(723, 39)), + new ExpectedDiagnostic(WinUIInteropAnalyzer.DiagnosticId, new TextSpan(808, 91), new TextSpan(784, 91)) } }, { "MRTResourceManagerCaller", new[] { - new ExpectedDiagnostic(WinUIMRTResourceManagerAnalyzer.ResourceManagerAPIDiagnosticId, new TextSpan(452, 23)), - new ExpectedDiagnostic(WinUIMRTResourceManagerAnalyzer.ResourceManagerAPIDiagnosticId, new TextSpan(520, 68)), - new ExpectedDiagnostic(WinUIMRTResourceManagerAnalyzer.ResourceManagerAPIDiagnosticId, new TextSpan(708, 23)), - new ExpectedDiagnostic(WinUIMRTResourceManagerAnalyzer.ResourceContextAPIDiagnosticId, new TextSpan(769, 40)), - new ExpectedDiagnostic(WinUIMRTResourceManagerAnalyzer.ResourceContextAPIDiagnosticId, new TextSpan(849, 33)) + new ExpectedDiagnostic(WinUIMRTResourceManagerAnalyzer.ResourceManagerAPIDiagnosticId, new TextSpan(452, 23), new TextSpan(436, 23)), + new ExpectedDiagnostic(WinUIMRTResourceManagerAnalyzer.ResourceManagerAPIDiagnosticId, new TextSpan(520, 68), new TextSpan(503, 68)), + new ExpectedDiagnostic(WinUIMRTResourceManagerAnalyzer.ResourceManagerAPIDiagnosticId, new TextSpan(708, 23), new TextSpan(686, 23)), + new ExpectedDiagnostic(WinUIMRTResourceManagerAnalyzer.ResourceContextAPIDiagnosticId, new TextSpan(769, 40), new TextSpan(746, 40)), + new ExpectedDiagnostic(WinUIMRTResourceManagerAnalyzer.ResourceContextAPIDiagnosticId, new TextSpan(849, 33), new TextSpan(825, 33)) } }, { "BackButtonCaller", new[] { - new ExpectedDiagnostic(WinUIBackButtonAnalyzer.DiagnosticId, new TextSpan(360, 78)), - new ExpectedDiagnostic(WinUIBackButtonAnalyzer.DiagnosticId, new TextSpan(531, 111)), - new ExpectedDiagnostic(WinUIBackButtonAnalyzer.DiagnosticId, new TextSpan(657, 111)), - new ExpectedDiagnostic(WinUIBackButtonAnalyzer.DiagnosticId, new TextSpan(928, 69)), - new ExpectedDiagnostic(WinUIBackButtonAnalyzer.DiagnosticId, new TextSpan(1157, 39)) + new ExpectedDiagnostic(WinUIBackButtonAnalyzer.DiagnosticId, new TextSpan(360, 78), new TextSpan(345, 78)), + new ExpectedDiagnostic(WinUIBackButtonAnalyzer.DiagnosticId, new TextSpan(531, 111), new TextSpan(511, 111)), + new ExpectedDiagnostic(WinUIBackButtonAnalyzer.DiagnosticId, new TextSpan(657, 111), new TextSpan(636, 111)), + new ExpectedDiagnostic(WinUIBackButtonAnalyzer.DiagnosticId, new TextSpan(928, 69), new TextSpan(901, 69)), + new ExpectedDiagnostic(WinUIBackButtonAnalyzer.DiagnosticId, new TextSpan(1157, 39), new TextSpan(1124, 39)) } }, { "AppWindowCaller", new[] { - new ExpectedDiagnostic(WinUIAppWindowAnalyzer.DiagnosticIdAppWindowType, new TextSpan(452, 37)), - new ExpectedDiagnostic(WinUIAppWindowAnalyzer.DiagnosticIdAppWindowType, new TextSpan(696, 37)), - new ExpectedDiagnostic(WinUIAppWindowAnalyzer.DiagnosticIdAppWindowType, new TextSpan(819, 9)), - new ExpectedDiagnostic(WinUIAppWindowAnalyzer.DiagnosticIdAppWindowMember, new TextSpan(935, 46)), - new ExpectedDiagnostic(WinUIAppWindowAnalyzer.DiagnosticIdAppWindowType, new TextSpan(1015, 15)), - new ExpectedDiagnostic(WinUIAppWindowAnalyzer.DiagnosticIdAppWindowType, new TextSpan(1090, 15)), - new ExpectedDiagnostic(WinUIAppWindowAnalyzer.DiagnosticIdAppWindowType, new TextSpan(1122, 15)) + new ExpectedDiagnostic(WinUIAppWindowAnalyzer.DiagnosticIdAppWindowType, new TextSpan(452, 37), new TextSpan(435, 37)), + new ExpectedDiagnostic(WinUIAppWindowAnalyzer.DiagnosticIdAppWindowType, new TextSpan(696, 37), new TextSpan(671, 37)), + new ExpectedDiagnostic(WinUIAppWindowAnalyzer.DiagnosticIdAppWindowType, new TextSpan(819, 9), new TextSpan(791, 9)), + new ExpectedDiagnostic(WinUIAppWindowAnalyzer.DiagnosticIdAppWindowMember, new TextSpan(935, 46), new TextSpan(902, 46)), + new ExpectedDiagnostic(WinUIAppWindowAnalyzer.DiagnosticIdAppWindowType, new TextSpan(1015, 15), new TextSpan(979, 15)), + new ExpectedDiagnostic(WinUIAppWindowAnalyzer.DiagnosticIdAppWindowType, new TextSpan(1090, 15), new TextSpan(1052, 15)), + new ExpectedDiagnostic(WinUIAppWindowAnalyzer.DiagnosticIdAppWindowType, new TextSpan(1122, 15), new TextSpan(1083, 15)) } } }; @@ -235,9 +235,10 @@ private static void AssertDiagnosticsCorrect(IEnumerable diagnostics var count = 0; foreach (var d in diagnostics.OrderBy(d => d.Location.SourceSpan.Start)) { - var expected = $"{expectedDiagnostics.ElementAt(count).SourceSpan}"; + var e = expectedDiagnostics.ElementAt(count); + var expected = $"{e.SourceSpan}"; var actual = $"{d.Location.SourceSpan}"; - Assert.True(expectedDiagnostics.ElementAt(count).Matches(d), $"Expected {expectedDiagnostics.ElementAt(count).Language} diagnostic {count} to be at {expectedDiagnostics.ElementAt(count).SourceSpan}" + + Assert.True(e.Matches(d), $"Expected {e.Language} diagnostic {count} to be at {e.SourceSpan}" + $" ; actually at {d.Location.SourceSpan} {d.Location.SourceSpan.End - d.Location.SourceSpan.Start}"); count++; } diff --git a/tests/tool/Integration.Tests/E2ETest.cs b/tests/tool/Integration.Tests/E2ETest.cs index 6010b7def..61266efbb 100644 --- a/tests/tool/Integration.Tests/E2ETest.cs +++ b/tests/tool/Integration.Tests/E2ETest.cs @@ -3,8 +3,10 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -38,21 +40,26 @@ public E2ETest(ITestOutputHelper output) _output = output; } - [InlineData("PCL", "SamplePCL.csproj", "")] - [InlineData("WpfSample/csharp", "BeanTrader.sln", "BeanTraderClient.csproj")] + [InlineData("PCL", "SamplePCL.csproj", "", true)] + [InlineData("WpfSample/csharp", "BeanTrader.sln", "BeanTraderClient.csproj", true)] /* - [InlineData("WebLibrary/csharp", "WebLibrary.csproj", "")] - [InlineData("AspNetSample/csharp", "TemplateMvc.csproj", "")] + [InlineData("WebLibrary/csharp", "WebLibrary.csproj", "", true)] + [InlineData("AspNetSample/csharp", "TemplateMvc.csproj", "", true)] */ - [InlineData("WpfSample/vb", "WpfApp1.sln", "")] - [InlineData("WCFSample", "ConsoleApp.csproj", "")] + [InlineData("WpfSample/vb", "WpfApp1.sln", "", true)] + [InlineData("WCFSample", "ConsoleApp.csproj", "", true)] // TODO: [mgoertz] Re-enable after MAUI workloads are installed on test machines - // [InlineData("MauiSample/droid", "EwDavidForms.sln", "EwDavidForms.Android.csproj")] - // [InlineData("MauiSample/ios", "EwDavidForms.sln", "EwDavidForms.iOS.csproj")] + // [InlineData("MauiSample/droid", "EwDavidForms.sln", "EwDavidForms.Android.csproj", false)] + // [InlineData("MauiSample/ios", "EwDavidForms.sln", "EwDavidForms.iOS.csproj", false)] [Theory] - public async Task UpgradeTest(string scenarioPath, string inputFileName, string entrypoint) + public async Task UpgradeTest(string scenarioPath, string inputFileName, string entrypoint, bool windowsOnly) { + if (windowsOnly && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + // Create a temporary working directory var workingDir = Path.Combine(Path.GetTempPath(), TempDirectoryName, Guid.NewGuid().ToString()); var dir = Directory.CreateDirectory(workingDir); @@ -137,14 +144,22 @@ private void AssertDirectoriesEqual(string expectedDir, string actualDir) foreach (var file in expectedFiles) { - var expectedText = ReadFile(expectedDir, file); - var actualText = ReadFile(actualDir, file); + var expectedText = ReadFile(expectedDir, file).ReplaceLineEndings(); + var actualText = ReadFile(actualDir, file).ReplaceLineEndings(); if (file.StartsWith("UpgradeReport.", StringComparison.Ordinal)) { - actualText = actualText.Replace(actualDir.Replace("\\", "\\\\", StringComparison.Ordinal), "[ACTUAL_PROJECT_ROOT]", StringComparison.Ordinal) - .Replace(actualDir.Replace("\\", "/", StringComparison.Ordinal), "[ACTUAL_PROJECT_ROOT]", StringComparison.Ordinal) - .Replace(Directory.GetCurrentDirectory().Replace("\\", "/", StringComparison.Ordinal), "[UA_PROJECT_BIN]", StringComparison.Ordinal); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + actualText = actualText.Replace(actualDir.Replace("\\", "\\\\", StringComparison.Ordinal), "[ACTUAL_PROJECT_ROOT]", StringComparison.Ordinal) + .Replace(actualDir.Replace("\\", "/", StringComparison.Ordinal), "[ACTUAL_PROJECT_ROOT]", StringComparison.Ordinal) + .Replace(Directory.GetCurrentDirectory().Replace("\\", "/", StringComparison.Ordinal), "[UA_PROJECT_BIN]", StringComparison.Ordinal); + } + else + { + actualText = actualText.Replace(actualDir.TrimStart('/'), "[ACTUAL_PROJECT_ROOT]", StringComparison.Ordinal) + .Replace(Directory.GetCurrentDirectory().TrimStart('/'), "[UA_PROJECT_BIN]", StringComparison.Ordinal); + } actualText = ReplaceVersionStrings(actualText); } @@ -184,14 +199,27 @@ private static string ReplaceVersionStrings(string actualText) private static string FindFileDiff(string file1, string file2) { - using var process = new System.Diagnostics.Process(); - process.StartInfo = new System.Diagnostics.ProcessStartInfo() + string command, arguments; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + command = "cmd.exe"; + arguments = $"/C fc {file1} {file2} /c"; + } + else + { + command = "diff"; + arguments = $"-u --strip-trailing-cr {file1} {file2}"; + } + + using var process = new Process(); + process.StartInfo = new ProcessStartInfo() { UseShellExecute = false, CreateNoWindow = true, WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden, - FileName = "cmd.exe", - Arguments = $"/C fc {file1} {file2} /c", + FileName = command, + Arguments = arguments, RedirectStandardError = true, RedirectStandardOutput = true };