From 8aea15e10fbd30249944336145f514824a847be2 Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Mon, 21 Sep 2026 17:34:09 +0200 Subject: [PATCH 1/6] fix: copy .NET runtime support files to output so obfuscated apps are runnable (#103) --- Confuser.Core/ConfuserEngine.cs | 43 ++++++++++++++++++++++++ Confuser.Core/Project/ConfuserPrj.xsd | 1 + Confuser.Core/Project/ConfuserProject.cs | 21 ++++++++++++ 3 files changed, 65 insertions(+) diff --git a/Confuser.Core/ConfuserEngine.cs b/Confuser.Core/ConfuserEngine.cs index 91594f9cf..51933f503 100644 --- a/Confuser.Core/ConfuserEngine.cs +++ b/Confuser.Core/ConfuserEngine.cs @@ -462,6 +462,49 @@ static void SaveModules(ConfuserContext context) { context.Logger.LogDebug("Saving to '{0}'...", path); File.WriteAllBytes(path, context.OutputModules[i]); } + + if (context.Project.CopyRuntimeFiles) + CopyRuntimeFiles(context); + } + + static void CopyRuntimeFiles(ConfuserContext context) { + for (int i = 0; i < context.OutputModules.Count; i++) { + string sourcePath = context.Modules[i].Location; + if (string.IsNullOrEmpty(sourcePath)) + continue; + if (!Path.IsPathRooted(sourcePath)) + sourcePath = Path.Combine(context.BaseDirectory, sourcePath); + + string sourceDir = Path.GetDirectoryName(sourcePath); + string baseName = Path.GetFileNameWithoutExtension(sourcePath); + if (sourceDir is null || string.IsNullOrEmpty(baseName)) + continue; + + // A .runtimeconfig.json sibling marks a .NET (Core) app, whose native launcher + // (apphost .exe) and dependency manifest live beside the module. .NET Framework + // assemblies have none, so the loop below simply finds nothing to copy. + string runtimeConfig = baseName + ".runtimeconfig.json"; + if (!File.Exists(Path.Combine(sourceDir, runtimeConfig))) + continue; + + string outputDir = Path.GetDirectoryName( + Path.GetFullPath(Path.Combine(context.OutputDirectory, context.OutputPaths[i]))); + if (outputDir is null) + continue; + + foreach (string supportFile in new[] { runtimeConfig, baseName + ".deps.json", baseName + ".exe" }) { + string from = Path.Combine(sourceDir, supportFile); + if (!File.Exists(from)) + continue; + string to = Path.Combine(outputDir, supportFile); + if (string.Equals(Path.GetFullPath(from), Path.GetFullPath(to), StringComparison.OrdinalIgnoreCase)) + continue; + if (!Directory.Exists(outputDir)) + Directory.CreateDirectory(outputDir); + context.Logger.LogDebug("Copying runtime support file '{0}'...", supportFile); + File.Copy(from, to, true); + } + } } /// diff --git a/Confuser.Core/Project/ConfuserPrj.xsd b/Confuser.Core/Project/ConfuserPrj.xsd index e99703b12..b85810939 100644 --- a/Confuser.Core/Project/ConfuserPrj.xsd +++ b/Confuser.Core/Project/ConfuserPrj.xsd @@ -75,6 +75,7 @@ + diff --git a/Confuser.Core/Project/ConfuserProject.cs b/Confuser.Core/Project/ConfuserProject.cs index 75d2f5361..43a362ec7 100644 --- a/Confuser.Core/Project/ConfuserProject.cs +++ b/Confuser.Core/Project/ConfuserProject.cs @@ -528,6 +528,15 @@ public ConfuserProject() { /// true if debug symbols are generated; otherwise, false. public bool Debug { get; set; } + /// + /// Gets or sets a value indicating whether the runtime support files of a .NET (Core) + /// application (its .runtimeconfig.json, .deps.json and native apphost + /// executable) are copied next to the obfuscated modules, so the output directory is + /// directly runnable. Has no effect on .NET Framework assemblies, which have none. + /// + /// true to copy the runtime support files; otherwise, false. Defaults to true. + public bool CopyRuntimeFiles { get; set; } = true; + /// /// Gets or sets the path to a symbol map from a previous obfuscation run. When set, the /// renamer reuses the obfuscated names recorded in that map, so re-obfuscation produces @@ -602,6 +611,12 @@ public XmlDocument Save() { elem.Attributes.Append(debugAttr); } + if (!CopyRuntimeFiles) { + XmlAttribute copyRuntimeAttr = xmlDoc.CreateAttribute("copyRuntimeFiles"); + copyRuntimeAttr.Value = CopyRuntimeFiles.ToString().ToLower(); + elem.Attributes.Append(copyRuntimeAttr); + } + if (InputSymbolMap != null) { XmlAttribute mapAttr = xmlDoc.CreateAttribute("inputSymbolMap"); mapAttr.Value = InputSymbolMap; @@ -669,6 +684,11 @@ public void Load(XmlDocument doc, string baseDirRoot = null) { else Debug = false; + if (docElem.Attributes["copyRuntimeFiles"] != null) + CopyRuntimeFiles = bool.Parse(docElem.Attributes["copyRuntimeFiles"].Value); + else + CopyRuntimeFiles = true; + if (docElem.Attributes["inputSymbolMap"] != null) InputSymbolMap = docElem.Attributes["inputSymbolMap"].Value.NullIfEmpty(); else @@ -743,6 +763,7 @@ public ConfuserProject Clone() { var ret = new ConfuserProject(); ret.Seed = Seed; ret.Debug = Debug; + ret.CopyRuntimeFiles = CopyRuntimeFiles; ret.InputSymbolMap = InputSymbolMap; ret.OutputDirectory = OutputDirectory; ret.BaseDirectory = BaseDirectory; From f428dba53339152a684653cb809010f3d7fdc618 Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Mon, 21 Sep 2026 17:34:09 +0200 Subject: [PATCH 2/6] test: verify net10 obfuscated output is runnable standalone (#103) --- Tests/Confuser.UnitTest/TestBase.cs | 7 ++-- .../ConsoleFrameworkTest.cs | 38 +++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/Tests/Confuser.UnitTest/TestBase.cs b/Tests/Confuser.UnitTest/TestBase.cs index c88b0403d..f659ec4f9 100644 --- a/Tests/Confuser.UnitTest/TestBase.cs +++ b/Tests/Confuser.UnitTest/TestBase.cs @@ -104,12 +104,13 @@ protected async Task Run(string[] inputFileNames, string[] expectedOutput, IEnum exists = File.Exists(outputName); } - if (exists) { - // Check if output assemblies is obfuscated + if (exists && IsAssembly(inputFileNames[index])) { + // Check if output assemblies is obfuscated. Non-assembly support files + // (e.g. runtimeconfig.json copied next to the module) are verbatim copies. Assert.NotEqual(FileUtilities.ComputeFileChecksum(Path.Combine(baseDir, name)), FileUtilities.ComputeFileChecksum(outputName)); } - else if (IsExternal(inputFileNames[index])) { + else if (!exists && IsExternal(inputFileNames[index])) { File.Copy( Path.Combine(baseDir, GetFileName(inputFileNames[index])), Path.Combine(outputDir, GetFileName(inputFileNames[index]))); diff --git a/Tests/CrossFramework.Test/ConsoleFrameworkTest.cs b/Tests/CrossFramework.Test/ConsoleFrameworkTest.cs index bea1b7119..2d7fdf6b5 100644 --- a/Tests/CrossFramework.Test/ConsoleFrameworkTest.cs +++ b/Tests/CrossFramework.Test/ConsoleFrameworkTest.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.IO; using System.Threading.Tasks; using Confuser.Core; using Confuser.Core.Project; @@ -117,5 +118,42 @@ public Task Console_Net10_ResourceProtection() => output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)); return Task.CompletedTask; }); + + [Fact] + [Trait("Category", "CrossFramework")] + [Trait("AppType", "Console")] + [Trait("TFM", "net10.0")] + [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] + public Task Console_Net10_EmitsRunnableOutput() => + Run("CrossFramework.Console.Net10.dll", + null, + new SettingItem("rename"), + outputDirSuffix: "-console-net10-runnable", + checkOutput: false, + postProcessAction: outputPath => { + // The obfuscated output must be directly runnable: the copyRuntimeFiles feature + // copies the .NET runtime support files next to the obfuscated module. + Assert.True(File.Exists(Path.Combine(outputPath, "CrossFramework.Console.Net10.runtimeconfig.json")), + "runtimeconfig.json was not copied into the obfuscated output"); + Assert.True(File.Exists(Path.Combine(outputPath, "CrossFramework.Console.Net10.deps.json")), + "deps.json was not copied into the obfuscated output"); + Assert.True(File.Exists(Path.Combine(outputPath, "CrossFramework.Console.Net10.exe")), + "apphost .exe was not copied into the obfuscated output"); + + var startInfo = new ProcessStartInfo("dotnet", "CrossFramework.Console.Net10.dll") { + WorkingDirectory = outputPath, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false + }; + using var process = Process.Start(startInfo); + var output = process.StandardOutput.ReadToEnd(); + var error = process.StandardError.ReadToEnd(); + process.WaitForExit(); + Assert.Equal(42, process.ExitCode); + Assert.Empty(error); + Assert.Contains("Hello from net10.0", output); + return Task.CompletedTask; + }); } } From 7d2ac4cb483efb20a2a9f80bb97603c3ae031887 Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Mon, 21 Sep 2026 17:44:50 +0200 Subject: [PATCH 3/6] test: run obfuscated net10 WinForms/WPF apps off-screen with crash reporting (#103) --- Tests/Confuser.UnitTest/TestBase.cs | 20 +++++++ .../WinFormsFrameworkTest.cs | 41 +++++++++++++ Tests/CrossFramework.Test/WpfFrameworkTest.cs | 41 +++++++++++++ Tests/CrossFramework.WPF.Net10/App.xaml | 3 +- Tests/CrossFramework.WPF.Net10/App.xaml.cs | 9 +++ .../CrossFramework.WPF.Net10.csproj | 3 + .../CrossFramework.WinForms.Net10.csproj | 3 + .../CrossFramework.WinForms.Net10/Program.cs | 4 ++ Tests/Shared/WinFormsSelfTestHost.cs | 52 +++++++++++++++++ Tests/Shared/WpfSelfTestHost.cs | 57 +++++++++++++++++++ 10 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 Tests/Shared/WinFormsSelfTestHost.cs create mode 100644 Tests/Shared/WpfSelfTestHost.cs diff --git a/Tests/Confuser.UnitTest/TestBase.cs b/Tests/Confuser.UnitTest/TestBase.cs index f659ec4f9..f976f38c2 100644 --- a/Tests/Confuser.UnitTest/TestBase.cs +++ b/Tests/Confuser.UnitTest/TestBase.cs @@ -186,5 +186,25 @@ private static bool IsAssembly(string name) { return extension.Equals(".dll", StringComparison.OrdinalIgnoreCase) || extension.Equals(".exe", StringComparison.OrdinalIgnoreCase); } + + /// + /// Runs an obfuscated .NET (Core) application via dotnet <dll> from its output + /// directory and returns the exit code together with the captured output streams. + /// + protected static (int ExitCode, string StdOut, string StdErr) RunDotnetApp( + string workingDirectory, string dllName, string arguments = null) { + var startInfo = new ProcessStartInfo("dotnet", + string.IsNullOrEmpty(arguments) ? dllName : dllName + " " + arguments) { + WorkingDirectory = workingDirectory, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false + }; + using var process = Process.Start(startInfo); + string stdout = process.StandardOutput.ReadToEnd(); + string stderr = process.StandardError.ReadToEnd(); + process.WaitForExit(); + return (process.ExitCode, stdout, stderr); + } } } diff --git a/Tests/CrossFramework.Test/WinFormsFrameworkTest.cs b/Tests/CrossFramework.Test/WinFormsFrameworkTest.cs index b6fecf658..45723bd25 100644 --- a/Tests/CrossFramework.Test/WinFormsFrameworkTest.cs +++ b/Tests/CrossFramework.Test/WinFormsFrameworkTest.cs @@ -74,5 +74,46 @@ public Task WinForms_Net10_RenameProtection() => new SettingItem("rename"), outputDirSuffix: "-winforms-net10", checkOutput: false); + + // --- Modern .NET: actually run the obfuscated UI app (shows a real window off-screen, + // pumps the message loop, self-closes after ~2s) and assert it starts without crashing. --- + + [Fact] + [Trait("Category", "CrossFramework")] + [Trait("AppType", "WinForms")] + [Trait("TFM", "net10.0")] + [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] + public Task WinForms_Net10_SelfTest_RunsWithoutCrashing() => + Run("CrossFramework.WinForms.Net10.dll", + null, + new SettingItem("rename"), + outputDirSuffix: "-winforms-net10-selftest", + checkOutput: false, + postProcessAction: outputPath => { + var (exit, stdout, stderr) = RunDotnetApp(outputPath, "CrossFramework.WinForms.Net10.dll", "--selftest"); + Assert.Equal(42, exit); + Assert.Contains("SHOWN:", stdout); + Assert.DoesNotContain("CRASH", stdout); + Assert.Empty(stderr); + return Task.CompletedTask; + }); + + [Fact] + [Trait("Category", "CrossFramework")] + [Trait("AppType", "WinForms")] + [Trait("TFM", "net10.0")] + [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] + public Task WinForms_Net10_SelfTest_ReportsCrash() => + Run("CrossFramework.WinForms.Net10.dll", + null, + new SettingItem("rename"), + outputDirSuffix: "-winforms-net10-crash", + checkOutput: false, + postProcessAction: outputPath => { + var (exit, stdout, _) = RunDotnetApp(outputPath, "CrossFramework.WinForms.Net10.dll", "--selftest-crash"); + Assert.NotEqual(42, exit); + Assert.Contains("CRASH", stdout); + return Task.CompletedTask; + }); } } diff --git a/Tests/CrossFramework.Test/WpfFrameworkTest.cs b/Tests/CrossFramework.Test/WpfFrameworkTest.cs index b60030a41..0058d78dc 100644 --- a/Tests/CrossFramework.Test/WpfFrameworkTest.cs +++ b/Tests/CrossFramework.Test/WpfFrameworkTest.cs @@ -74,5 +74,46 @@ public Task WPF_Net10_RenameProtection() => new SettingItem("rename"), outputDirSuffix: "-wpf-net10", checkOutput: false); + + // --- Modern .NET: actually run the obfuscated WPF app (shows a real window off-screen, + // pumps the dispatcher, self-closes after ~2s) and assert it starts without crashing. --- + + [Fact] + [Trait("Category", "CrossFramework")] + [Trait("AppType", "WPF")] + [Trait("TFM", "net10.0-windows")] + [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] + public Task WPF_Net10_SelfTest_RunsWithoutCrashing() => + Run("CrossFramework.WPF.Net10.dll", + null, + new SettingItem("rename"), + outputDirSuffix: "-wpf-net10-selftest", + checkOutput: false, + postProcessAction: outputPath => { + var (exit, stdout, stderr) = RunDotnetApp(outputPath, "CrossFramework.WPF.Net10.dll", "--selftest"); + Assert.Equal(42, exit); + Assert.Contains("SHOWN:", stdout); + Assert.DoesNotContain("CRASH", stdout); + Assert.Empty(stderr); + return Task.CompletedTask; + }); + + [Fact] + [Trait("Category", "CrossFramework")] + [Trait("AppType", "WPF")] + [Trait("TFM", "net10.0-windows")] + [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] + public Task WPF_Net10_SelfTest_ReportsCrash() => + Run("CrossFramework.WPF.Net10.dll", + null, + new SettingItem("rename"), + outputDirSuffix: "-wpf-net10-crash", + checkOutput: false, + postProcessAction: outputPath => { + var (exit, stdout, _) = RunDotnetApp(outputPath, "CrossFramework.WPF.Net10.dll", "--selftest-crash"); + Assert.NotEqual(42, exit); + Assert.Contains("CRASH", stdout); + return Task.CompletedTask; + }); } } diff --git a/Tests/CrossFramework.WPF.Net10/App.xaml b/Tests/CrossFramework.WPF.Net10/App.xaml index a13360fb3..26cf87f1d 100644 --- a/Tests/CrossFramework.WPF.Net10/App.xaml +++ b/Tests/CrossFramework.WPF.Net10/App.xaml @@ -1,5 +1,4 @@ + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> diff --git a/Tests/CrossFramework.WPF.Net10/App.xaml.cs b/Tests/CrossFramework.WPF.Net10/App.xaml.cs index 8ca54a6ce..946904461 100644 --- a/Tests/CrossFramework.WPF.Net10/App.xaml.cs +++ b/Tests/CrossFramework.WPF.Net10/App.xaml.cs @@ -1,5 +1,6 @@ using System; using System.Windows; +using CrossFramework.SelfTest; namespace CrossFramework.WPF { public partial class App : Application { @@ -12,7 +13,15 @@ protected override void OnStartup(StartupEventArgs e) { Console.WriteLine("Content: " + window.GetStatusText()); Console.WriteLine("END"); Shutdown(42); + return; } + + if (e.Args.Length > 0 && (e.Args[0] == "--selftest" || e.Args[0] == "--selftest-crash")) { + WpfSelfTestHost.Run(this, new MainWindow(), induceCrash: e.Args[0] == "--selftest-crash"); + return; + } + + new MainWindow().Show(); } } } diff --git a/Tests/CrossFramework.WPF.Net10/CrossFramework.WPF.Net10.csproj b/Tests/CrossFramework.WPF.Net10/CrossFramework.WPF.Net10.csproj index 37a5b6c03..a6e9fd78b 100644 --- a/Tests/CrossFramework.WPF.Net10/CrossFramework.WPF.Net10.csproj +++ b/Tests/CrossFramework.WPF.Net10/CrossFramework.WPF.Net10.csproj @@ -5,4 +5,7 @@ true disable + + + diff --git a/Tests/CrossFramework.WinForms.Net10/CrossFramework.WinForms.Net10.csproj b/Tests/CrossFramework.WinForms.Net10/CrossFramework.WinForms.Net10.csproj index 13bb1cf3d..3bac47f2e 100644 --- a/Tests/CrossFramework.WinForms.Net10/CrossFramework.WinForms.Net10.csproj +++ b/Tests/CrossFramework.WinForms.Net10/CrossFramework.WinForms.Net10.csproj @@ -5,4 +5,7 @@ true disable + + + diff --git a/Tests/CrossFramework.WinForms.Net10/Program.cs b/Tests/CrossFramework.WinForms.Net10/Program.cs index 143e430ee..873eeba5a 100644 --- a/Tests/CrossFramework.WinForms.Net10/Program.cs +++ b/Tests/CrossFramework.WinForms.Net10/Program.cs @@ -1,5 +1,6 @@ using System; using System.Windows.Forms; +using CrossFramework.SelfTest; namespace CrossFramework.WinForms { static class Program { @@ -15,6 +16,9 @@ static int Main(string[] args) { return 42; } + if (args.Length > 0 && (args[0] == "--selftest" || args[0] == "--selftest-crash")) + return WinFormsSelfTestHost.Run(() => new MainForm(), induceCrash: args[0] == "--selftest-crash"); + Application.SetHighDpiMode(HighDpiMode.SystemAware); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); diff --git a/Tests/Shared/WinFormsSelfTestHost.cs b/Tests/Shared/WinFormsSelfTestHost.cs new file mode 100644 index 000000000..0adb5f237 --- /dev/null +++ b/Tests/Shared/WinFormsSelfTestHost.cs @@ -0,0 +1,52 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace CrossFramework.SelfTest { + // Shared across the WinForms subject apps (linked, not duplicated). Actually runs the app: + // shows the real main window off-screen, lets the message loop pump briefly, then closes it. + // Any unhandled exception (UI thread or background) makes the process report failure. + // + // Contract: prints START / SHOWN / END and exits 42 on a clean run; on a crash prints + // "CRASH: ..." and exits 1. The window is placed off-screen with no taskbar entry so it is + // non-intrusive on a developer machine while still exercising a genuine window + message loop. + internal static class WinFormsSelfTestHost { + public static int Run(Func
formFactory, bool induceCrash = false) { + AppDomain.CurrentDomain.UnhandledException += (sender, e) => + Fail("AppDomain", e.ExceptionObject as Exception); + Application.ThreadException += (sender, e) => + Fail("ThreadException", e.Exception); + Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); + + Console.WriteLine("START"); + + var form = formFactory(); + form.StartPosition = FormStartPosition.Manual; + form.Location = new Point(-32000, -32000); + form.ShowInTaskbar = false; + + form.Shown += (sender, e) => { + Console.WriteLine("SHOWN: " + form.Text); + if (induceCrash) + throw new InvalidOperationException("Induced self-test crash"); + + var timer = new Timer { Interval = 2000 }; + timer.Tick += (s, args) => { + timer.Stop(); + form.Close(); + }; + timer.Start(); + }; + + Application.Run(form); + Console.WriteLine("END"); + return 42; + } + + static void Fail(string source, Exception ex) { + Console.WriteLine("CRASH: [" + source + "] " + (ex != null ? ex.Message : "unknown")); + Console.Out.Flush(); + Environment.Exit(1); + } + } +} diff --git a/Tests/Shared/WpfSelfTestHost.cs b/Tests/Shared/WpfSelfTestHost.cs new file mode 100644 index 000000000..b7d7025e7 --- /dev/null +++ b/Tests/Shared/WpfSelfTestHost.cs @@ -0,0 +1,57 @@ +using System; +using System.Windows; +using System.Windows.Threading; + +namespace CrossFramework.SelfTest { + // Shared across the WPF subject apps (linked, not duplicated). Shows the real main window + // off-screen inside the already-running Application, lets it live briefly, then closes it. + // Any unhandled exception (dispatcher or background) makes the process report failure. + // + // Contract: prints START / SHOWN / END and exits 42 on a clean run; on a crash prints + // "CRASH: ..." and exits 1. Call from Application.OnStartup; the app's own Run loop keeps + // pumping, and closing the window shuts the app down with exit code 42. + internal static class WpfSelfTestHost { + public static void Run(Application app, Window window, bool induceCrash = false) { + AppDomain.CurrentDomain.UnhandledException += (sender, e) => + Fail("AppDomain", e.ExceptionObject as Exception); + app.DispatcherUnhandledException += (sender, e) => + Fail("Dispatcher", e.Exception); + + Console.WriteLine("START"); + + window.ShowInTaskbar = false; + window.WindowStartupLocation = WindowStartupLocation.Manual; + window.Left = -32000; + window.Top = -32000; + + window.Loaded += (sender, e) => { + Console.WriteLine("SHOWN: " + window.Title); + if (induceCrash) + throw new InvalidOperationException("Induced self-test crash"); + + var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) }; + timer.Tick += (s, args) => { + timer.Stop(); + window.Close(); + }; + timer.Start(); + }; + + window.Closed += (sender, e) => { + Console.WriteLine("END"); + // WPF's generated Main is void and ignores Run()'s return value, so set the + // process exit code explicitly rather than relying on Shutdown's argument. + Environment.ExitCode = 42; + app.Shutdown(); + }; + + window.Show(); + } + + static void Fail(string source, Exception ex) { + Console.WriteLine("CRASH: [" + source + "] " + (ex != null ? ex.Message : "unknown")); + Console.Out.Flush(); + Environment.Exit(1); + } + } +} From 7a70ccac12e63334359d8d2e9e5ea707037a80a7 Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Mon, 21 Sep 2026 17:47:26 +0200 Subject: [PATCH 4/6] test: extend off-screen self-run UI coverage to net6 and net8 (#103) --- .../WinFormsFrameworkTest.cs | 40 +++++++++++++++++++ Tests/CrossFramework.Test/WpfFrameworkTest.cs | 40 +++++++++++++++++++ Tests/CrossFramework.WPF.Net6/App.xaml | 3 +- Tests/CrossFramework.WPF.Net6/App.xaml.cs | 8 ++++ .../CrossFramework.WPF.Net6.csproj | 3 ++ Tests/CrossFramework.WPF.Net8/App.xaml | 3 +- Tests/CrossFramework.WPF.Net8/App.xaml.cs | 8 ++++ .../CrossFramework.WPF.Net8.csproj | 3 ++ .../CrossFramework.WinForms.Net6.csproj | 3 ++ Tests/CrossFramework.WinForms.Net6/Program.cs | 3 ++ .../CrossFramework.WinForms.Net8.csproj | 3 ++ Tests/CrossFramework.WinForms.Net8/Program.cs | 3 ++ 12 files changed, 116 insertions(+), 4 deletions(-) diff --git a/Tests/CrossFramework.Test/WinFormsFrameworkTest.cs b/Tests/CrossFramework.Test/WinFormsFrameworkTest.cs index 45723bd25..7bbcc2db1 100644 --- a/Tests/CrossFramework.Test/WinFormsFrameworkTest.cs +++ b/Tests/CrossFramework.Test/WinFormsFrameworkTest.cs @@ -115,5 +115,45 @@ public Task WinForms_Net10_SelfTest_ReportsCrash() => Assert.Contains("CRASH", stdout); return Task.CompletedTask; }); + + [Fact] + [Trait("Category", "CrossFramework")] + [Trait("AppType", "WinForms")] + [Trait("TFM", "net6.0")] + [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] + public Task WinForms_Net6_SelfTest_RunsWithoutCrashing() => + Run("CrossFramework.WinForms.Net6.dll", + null, + new SettingItem("rename"), + outputDirSuffix: "-winforms-net6-selftest", + checkOutput: false, + postProcessAction: outputPath => { + var (exit, stdout, stderr) = RunDotnetApp(outputPath, "CrossFramework.WinForms.Net6.dll", "--selftest"); + Assert.Equal(42, exit); + Assert.Contains("SHOWN:", stdout); + Assert.DoesNotContain("CRASH", stdout); + Assert.Empty(stderr); + return Task.CompletedTask; + }); + + [Fact] + [Trait("Category", "CrossFramework")] + [Trait("AppType", "WinForms")] + [Trait("TFM", "net8.0")] + [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] + public Task WinForms_Net8_SelfTest_RunsWithoutCrashing() => + Run("CrossFramework.WinForms.Net8.dll", + null, + new SettingItem("rename"), + outputDirSuffix: "-winforms-net8-selftest", + checkOutput: false, + postProcessAction: outputPath => { + var (exit, stdout, stderr) = RunDotnetApp(outputPath, "CrossFramework.WinForms.Net8.dll", "--selftest"); + Assert.Equal(42, exit); + Assert.Contains("SHOWN:", stdout); + Assert.DoesNotContain("CRASH", stdout); + Assert.Empty(stderr); + return Task.CompletedTask; + }); } } diff --git a/Tests/CrossFramework.Test/WpfFrameworkTest.cs b/Tests/CrossFramework.Test/WpfFrameworkTest.cs index 0058d78dc..46aab952e 100644 --- a/Tests/CrossFramework.Test/WpfFrameworkTest.cs +++ b/Tests/CrossFramework.Test/WpfFrameworkTest.cs @@ -115,5 +115,45 @@ public Task WPF_Net10_SelfTest_ReportsCrash() => Assert.Contains("CRASH", stdout); return Task.CompletedTask; }); + + [Fact] + [Trait("Category", "CrossFramework")] + [Trait("AppType", "WPF")] + [Trait("TFM", "net6.0-windows")] + [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] + public Task WPF_Net6_SelfTest_RunsWithoutCrashing() => + Run("CrossFramework.WPF.Net6.dll", + null, + new SettingItem("rename"), + outputDirSuffix: "-wpf-net6-selftest", + checkOutput: false, + postProcessAction: outputPath => { + var (exit, stdout, stderr) = RunDotnetApp(outputPath, "CrossFramework.WPF.Net6.dll", "--selftest"); + Assert.Equal(42, exit); + Assert.Contains("SHOWN:", stdout); + Assert.DoesNotContain("CRASH", stdout); + Assert.Empty(stderr); + return Task.CompletedTask; + }); + + [Fact] + [Trait("Category", "CrossFramework")] + [Trait("AppType", "WPF")] + [Trait("TFM", "net8.0-windows")] + [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] + public Task WPF_Net8_SelfTest_RunsWithoutCrashing() => + Run("CrossFramework.WPF.Net8.dll", + null, + new SettingItem("rename"), + outputDirSuffix: "-wpf-net8-selftest", + checkOutput: false, + postProcessAction: outputPath => { + var (exit, stdout, stderr) = RunDotnetApp(outputPath, "CrossFramework.WPF.Net8.dll", "--selftest"); + Assert.Equal(42, exit); + Assert.Contains("SHOWN:", stdout); + Assert.DoesNotContain("CRASH", stdout); + Assert.Empty(stderr); + return Task.CompletedTask; + }); } } diff --git a/Tests/CrossFramework.WPF.Net6/App.xaml b/Tests/CrossFramework.WPF.Net6/App.xaml index a13360fb3..26cf87f1d 100644 --- a/Tests/CrossFramework.WPF.Net6/App.xaml +++ b/Tests/CrossFramework.WPF.Net6/App.xaml @@ -1,5 +1,4 @@ + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> diff --git a/Tests/CrossFramework.WPF.Net6/App.xaml.cs b/Tests/CrossFramework.WPF.Net6/App.xaml.cs index 8ca54a6ce..367a0aa23 100644 --- a/Tests/CrossFramework.WPF.Net6/App.xaml.cs +++ b/Tests/CrossFramework.WPF.Net6/App.xaml.cs @@ -12,7 +12,15 @@ protected override void OnStartup(StartupEventArgs e) { Console.WriteLine("Content: " + window.GetStatusText()); Console.WriteLine("END"); Shutdown(42); + return; } + + if (e.Args.Length > 0 && (e.Args[0] == "--selftest" || e.Args[0] == "--selftest-crash")) { + CrossFramework.SelfTest.WpfSelfTestHost.Run(this, new MainWindow(), induceCrash: e.Args[0] == "--selftest-crash"); + return; + } + + new MainWindow().Show(); } } } diff --git a/Tests/CrossFramework.WPF.Net6/CrossFramework.WPF.Net6.csproj b/Tests/CrossFramework.WPF.Net6/CrossFramework.WPF.Net6.csproj index f4429b158..28e790533 100644 --- a/Tests/CrossFramework.WPF.Net6/CrossFramework.WPF.Net6.csproj +++ b/Tests/CrossFramework.WPF.Net6/CrossFramework.WPF.Net6.csproj @@ -5,4 +5,7 @@ true disable + + + diff --git a/Tests/CrossFramework.WPF.Net8/App.xaml b/Tests/CrossFramework.WPF.Net8/App.xaml index a13360fb3..26cf87f1d 100644 --- a/Tests/CrossFramework.WPF.Net8/App.xaml +++ b/Tests/CrossFramework.WPF.Net8/App.xaml @@ -1,5 +1,4 @@ + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> diff --git a/Tests/CrossFramework.WPF.Net8/App.xaml.cs b/Tests/CrossFramework.WPF.Net8/App.xaml.cs index 8ca54a6ce..367a0aa23 100644 --- a/Tests/CrossFramework.WPF.Net8/App.xaml.cs +++ b/Tests/CrossFramework.WPF.Net8/App.xaml.cs @@ -12,7 +12,15 @@ protected override void OnStartup(StartupEventArgs e) { Console.WriteLine("Content: " + window.GetStatusText()); Console.WriteLine("END"); Shutdown(42); + return; } + + if (e.Args.Length > 0 && (e.Args[0] == "--selftest" || e.Args[0] == "--selftest-crash")) { + CrossFramework.SelfTest.WpfSelfTestHost.Run(this, new MainWindow(), induceCrash: e.Args[0] == "--selftest-crash"); + return; + } + + new MainWindow().Show(); } } } diff --git a/Tests/CrossFramework.WPF.Net8/CrossFramework.WPF.Net8.csproj b/Tests/CrossFramework.WPF.Net8/CrossFramework.WPF.Net8.csproj index 8e794faa1..791db65ff 100644 --- a/Tests/CrossFramework.WPF.Net8/CrossFramework.WPF.Net8.csproj +++ b/Tests/CrossFramework.WPF.Net8/CrossFramework.WPF.Net8.csproj @@ -5,4 +5,7 @@ true disable + + + diff --git a/Tests/CrossFramework.WinForms.Net6/CrossFramework.WinForms.Net6.csproj b/Tests/CrossFramework.WinForms.Net6/CrossFramework.WinForms.Net6.csproj index 43d1defe0..661aab39c 100644 --- a/Tests/CrossFramework.WinForms.Net6/CrossFramework.WinForms.Net6.csproj +++ b/Tests/CrossFramework.WinForms.Net6/CrossFramework.WinForms.Net6.csproj @@ -5,4 +5,7 @@ true disable + + + diff --git a/Tests/CrossFramework.WinForms.Net6/Program.cs b/Tests/CrossFramework.WinForms.Net6/Program.cs index e438e143c..41ebbbf16 100644 --- a/Tests/CrossFramework.WinForms.Net6/Program.cs +++ b/Tests/CrossFramework.WinForms.Net6/Program.cs @@ -16,6 +16,9 @@ static int Main(string[] args) { return 42; } + if (args.Length > 0 && (args[0] == "--selftest" || args[0] == "--selftest-crash")) + return CrossFramework.SelfTest.WinFormsSelfTestHost.Run(() => new MainForm(), induceCrash: args[0] == "--selftest-crash"); + Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); diff --git a/Tests/CrossFramework.WinForms.Net8/CrossFramework.WinForms.Net8.csproj b/Tests/CrossFramework.WinForms.Net8/CrossFramework.WinForms.Net8.csproj index b6d4633b9..451dca704 100644 --- a/Tests/CrossFramework.WinForms.Net8/CrossFramework.WinForms.Net8.csproj +++ b/Tests/CrossFramework.WinForms.Net8/CrossFramework.WinForms.Net8.csproj @@ -5,4 +5,7 @@ true disable + + + diff --git a/Tests/CrossFramework.WinForms.Net8/Program.cs b/Tests/CrossFramework.WinForms.Net8/Program.cs index e438e143c..41ebbbf16 100644 --- a/Tests/CrossFramework.WinForms.Net8/Program.cs +++ b/Tests/CrossFramework.WinForms.Net8/Program.cs @@ -16,6 +16,9 @@ static int Main(string[] args) { return 42; } + if (args.Length > 0 && (args[0] == "--selftest" || args[0] == "--selftest-crash")) + return CrossFramework.SelfTest.WinFormsSelfTestHost.Run(() => new MainForm(), induceCrash: args[0] == "--selftest-crash"); + Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); From 39c5394d6dab6be8921f9172a6b744be546f4635 Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Mon, 21 Sep 2026 18:24:04 +0200 Subject: [PATCH 5/6] test: run every obfuscated app across net35-net10 with off-screen UI + crash reporting (#103) --- Tests/Confuser.UnitTest/TestBase.cs | 93 +++++++++++++--- .../CrossFramework.Console.Net6.csproj | 3 + Tests/CrossFramework.Console.Net6/Program.cs | 5 +- .../CrossFramework.Console.Net8.csproj | 3 + Tests/CrossFramework.Console.Net8/Program.cs | 5 +- .../ConsoleFrameworkTest.cs | 13 +++ .../WinFormsFrameworkTest.cs | 105 +++++------------- Tests/CrossFramework.Test/WpfFrameworkTest.cs | 105 +++++------------- .../CrossFramework.WPF.Net35.csproj | 1 + Tests/CrossFramework.WPF.Net35/Program.cs | 6 + .../CrossFramework.WPF.Net40.csproj | 1 + Tests/CrossFramework.WPF.Net40/Program.cs | 6 + Tests/CrossFramework.WPF.Net48/App.xaml | 3 +- Tests/CrossFramework.WPF.Net48/App.xaml.cs | 8 ++ .../CrossFramework.WPF.Net48.csproj | 3 + .../CrossFramework.WinForms.Net35.csproj | 1 + .../CrossFramework.WinForms.Net35/Program.cs | 3 + .../CrossFramework.WinForms.Net40.csproj | 1 + .../CrossFramework.WinForms.Net40/Program.cs | 3 + .../CrossFramework.WinForms.Net48.csproj | 3 + .../CrossFramework.WinForms.Net48/Program.cs | 3 + Tests/Shared/ConsoleSelfTestHost.cs | 28 +++++ Tests/Shared/WinFormsSelfTestHost.cs | 33 ++++-- Tests/Shared/WpfSelfTestHost.cs | 38 +++---- 24 files changed, 269 insertions(+), 204 deletions(-) create mode 100644 Tests/Shared/ConsoleSelfTestHost.cs diff --git a/Tests/Confuser.UnitTest/TestBase.cs b/Tests/Confuser.UnitTest/TestBase.cs index f976f38c2..792471e0d 100644 --- a/Tests/Confuser.UnitTest/TestBase.cs +++ b/Tests/Confuser.UnitTest/TestBase.cs @@ -24,10 +24,10 @@ protected TestBase(ITestOutputHelper outputHelper) => protected Task Run(string inputFileName, string[] expectedOutput, SettingItem protection, string outputDirSuffix = "", Action outputAction = null, SettingItem packer = null, Action projectModuleAction = null, Func postProcessAction = null, - string seed = null, bool checkOutput = true, string processArguments = null) => + string seed = null, bool checkOutput = true, string processArguments = null, bool runEntryProcess = true) => Run(new[] { inputFileName }, expectedOutput, protection, outputDirSuffix, outputAction, packer, - projectModuleAction, postProcessAction, seed, checkOutput, processArguments); + projectModuleAction, postProcessAction, seed, checkOutput, processArguments, runEntryProcess); protected Task Run(string inputFileName, string[] expectedOutput, IEnumerable> protections, string outputDirSuffix = "", Action outputAction = null, SettingItem packer = null, @@ -40,15 +40,15 @@ protected Task Run(string inputFileName, string[] expectedOutput, IEnumerable protection, string outputDirSuffix = "", Action outputAction = null, SettingItem packer = null, Action projectModuleAction = null, Func postProcessAction = null, - string seed = null, bool checkOutput = true, string processArguments = null) { + string seed = null, bool checkOutput = true, string processArguments = null, bool runEntryProcess = true) { var protections = (protection is null) ? Enumerable.Empty>() : new[] { protection }; - return Run(inputFileNames, expectedOutput, protections, outputDirSuffix, outputAction, packer, projectModuleAction, postProcessAction, seed, checkOutput, processArguments); + return Run(inputFileNames, expectedOutput, protections, outputDirSuffix, outputAction, packer, projectModuleAction, postProcessAction, seed, checkOutput, processArguments, runEntryProcess); } protected async Task Run(string[] inputFileNames, string[] expectedOutput, IEnumerable> protections, string outputDirSuffix = "", Action outputAction = null, SettingItem packer = null, Action projectModuleAction = null, Func postProcessAction = null, - string seed = null, bool checkOutput = true, string processArguments = null) { + string seed = null, bool checkOutput = true, string processArguments = null, bool runEntryProcess = true) { var baseDir = Environment.CurrentDirectory; var outputDir = Path.Combine(baseDir, "obfuscated" + outputDirSuffix); @@ -117,7 +117,7 @@ protected async Task Run(string[] inputFileNames, string[] expectedOutput, IEnum } } - if (Path.GetExtension(entryInputFileName) == ".exe") { + if (runEntryProcess && Path.GetExtension(entryInputFileName) == ".exe") { var info = new ProcessStartInfo(entryOutputFileName) { RedirectStandardOutput = true, RedirectStandardError = true, @@ -188,23 +188,82 @@ private static bool IsAssembly(string name) { } /// - /// Runs an obfuscated .NET (Core) application via dotnet <dll> from its output - /// directory and returns the exit code together with the captured output streams. + /// Runs an obfuscated application from its output directory and returns the exit code + /// together with the captured output streams. A .exe (.NET Framework) is launched + /// directly; a .dll (.NET Core) is launched via dotnet <dll>. /// - protected static (int ExitCode, string StdOut, string StdErr) RunDotnetApp( - string workingDirectory, string dllName, string arguments = null) { - var startInfo = new ProcessStartInfo("dotnet", - string.IsNullOrEmpty(arguments) ? dllName : dllName + " " + arguments) { - WorkingDirectory = workingDirectory, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false - }; + protected static (int ExitCode, string StdOut, string StdErr) RunApp( + string workingDirectory, string appFileName, string arguments = null) { + ProcessStartInfo startInfo; + if (appFileName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) { + startInfo = new ProcessStartInfo(Path.Combine(workingDirectory, appFileName)) { + Arguments = arguments ?? "", + WorkingDirectory = workingDirectory + }; + } + else { + startInfo = new ProcessStartInfo("dotnet", + string.IsNullOrEmpty(arguments) ? appFileName : appFileName + " " + arguments) { + WorkingDirectory = workingDirectory + }; + } + startInfo.RedirectStandardOutput = true; + startInfo.RedirectStandardError = true; + startInfo.UseShellExecute = false; + using var process = Process.Start(startInfo); string stdout = process.StandardOutput.ReadToEnd(); string stderr = process.StandardError.ReadToEnd(); process.WaitForExit(); return (process.ExitCode, stdout, stderr); } + + /// + /// Obfuscates the given UI application, then launches the obfuscated build with + /// --selftest: it shows its real window off-screen, pumps its message loop and + /// self-closes. Asserts the app actually started and exited cleanly (42) without crashing. + /// + protected Task SelfTestRunsWithoutCrashing(string appFile, string outputDirSuffix) => + Run(appFile, null, new SettingItem("rename"), + outputDirSuffix: outputDirSuffix, checkOutput: false, runEntryProcess: false, + postProcessAction: outputPath => { + var (exit, stdout, stderr) = RunApp(outputPath, appFile, "--selftest"); + Assert.Equal(42, exit); + Assert.Contains("SHOWN:", stdout); + Assert.DoesNotContain("CRASH", stdout); + Assert.Empty(stderr); + return Task.CompletedTask; + }); + + /// + /// Obfuscates the given UI application, then launches it with --selftest-crash, + /// which throws once the window is shown. Asserts the app's unhandled-exception handler + /// caught the crash and reported failure (non-42 exit, CRASH on stdout). + /// + /// + /// Obfuscates the given console application, then runs the obfuscated build with + /// --selftest and asserts it executed to completion and exited cleanly (42). + /// + protected Task SelfTestConsoleRuns(string appFile, string outputDirSuffix) => + Run(appFile, null, new SettingItem("rename"), + outputDirSuffix: outputDirSuffix, checkOutput: false, runEntryProcess: false, + postProcessAction: outputPath => { + var (exit, stdout, stderr) = RunApp(outputPath, appFile, "--selftest"); + Assert.Equal(42, exit); + Assert.Contains("RUN", stdout); + Assert.DoesNotContain("CRASH", stdout); + Assert.Empty(stderr); + return Task.CompletedTask; + }); + + protected Task SelfTestReportsCrash(string appFile, string outputDirSuffix) => + Run(appFile, null, new SettingItem("rename"), + outputDirSuffix: outputDirSuffix, checkOutput: false, runEntryProcess: false, + postProcessAction: outputPath => { + var (exit, stdout, _) = RunApp(outputPath, appFile, "--selftest-crash"); + Assert.NotEqual(42, exit); + Assert.Contains("CRASH", stdout); + return Task.CompletedTask; + }); } } diff --git a/Tests/CrossFramework.Console.Net6/CrossFramework.Console.Net6.csproj b/Tests/CrossFramework.Console.Net6/CrossFramework.Console.Net6.csproj index b87b9174f..85048abf7 100644 --- a/Tests/CrossFramework.Console.Net6/CrossFramework.Console.Net6.csproj +++ b/Tests/CrossFramework.Console.Net6/CrossFramework.Console.Net6.csproj @@ -4,4 +4,7 @@ net6.0 disable + + + diff --git a/Tests/CrossFramework.Console.Net6/Program.cs b/Tests/CrossFramework.Console.Net6/Program.cs index adf77bd1d..df86447f4 100644 --- a/Tests/CrossFramework.Console.Net6/Program.cs +++ b/Tests/CrossFramework.Console.Net6/Program.cs @@ -2,7 +2,10 @@ namespace CrossFramework.Console { class Program { - static int Main() { + static int Main(string[] args) { + if (args.Length > 0 && (args[0] == "--selftest" || args[0] == "--selftest-crash")) + return CrossFramework.SelfTest.ConsoleSelfTestHost.Run(args[0] == "--selftest-crash"); + System.Console.WriteLine("START"); System.Console.WriteLine("Hello from net6.0"); System.Console.WriteLine("END"); diff --git a/Tests/CrossFramework.Console.Net8/CrossFramework.Console.Net8.csproj b/Tests/CrossFramework.Console.Net8/CrossFramework.Console.Net8.csproj index d0ab6f9a4..fdb01141b 100644 --- a/Tests/CrossFramework.Console.Net8/CrossFramework.Console.Net8.csproj +++ b/Tests/CrossFramework.Console.Net8/CrossFramework.Console.Net8.csproj @@ -4,4 +4,7 @@ net8.0 disable + + + diff --git a/Tests/CrossFramework.Console.Net8/Program.cs b/Tests/CrossFramework.Console.Net8/Program.cs index 779a97411..c10659acb 100644 --- a/Tests/CrossFramework.Console.Net8/Program.cs +++ b/Tests/CrossFramework.Console.Net8/Program.cs @@ -2,7 +2,10 @@ namespace CrossFramework.Console { class Program { - static int Main() { + static int Main(string[] args) { + if (args.Length > 0 && (args[0] == "--selftest" || args[0] == "--selftest-crash")) + return CrossFramework.SelfTest.ConsoleSelfTestHost.Run(args[0] == "--selftest-crash"); + System.Console.WriteLine("START"); System.Console.WriteLine("Hello from net8.0"); System.Console.WriteLine("END"); diff --git a/Tests/CrossFramework.Test/ConsoleFrameworkTest.cs b/Tests/CrossFramework.Test/ConsoleFrameworkTest.cs index 2d7fdf6b5..9fd6146ed 100644 --- a/Tests/CrossFramework.Test/ConsoleFrameworkTest.cs +++ b/Tests/CrossFramework.Test/ConsoleFrameworkTest.cs @@ -155,5 +155,18 @@ public Task Console_Net10_EmitsRunnableOutput() => Assert.Contains("Hello from net10.0", output); return Task.CompletedTask; }); + + // --- Run the obfuscated console apps that were previously obfuscated-but-never-executed, + // with crash reporting via the unhandled-exception handler. --- + + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "Console"), Trait("TFM", "net6.0"), Trait("Issue", "103")] + public Task Console_Net6_SelfTest_Runs() => SelfTestConsoleRuns("CrossFramework.Console.Net6.dll", "-console-net6-selftest"); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "Console"), Trait("TFM", "net6.0"), Trait("Issue", "103")] + public Task Console_Net6_SelfTest_Crash() => SelfTestReportsCrash("CrossFramework.Console.Net6.dll", "-console-net6-crash"); + + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "Console"), Trait("TFM", "net8.0"), Trait("Issue", "103")] + public Task Console_Net8_SelfTest_Runs() => SelfTestConsoleRuns("CrossFramework.Console.Net8.dll", "-console-net8-selftest"); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "Console"), Trait("TFM", "net8.0"), Trait("Issue", "103")] + public Task Console_Net8_SelfTest_Crash() => SelfTestReportsCrash("CrossFramework.Console.Net8.dll", "-console-net8-crash"); } } diff --git a/Tests/CrossFramework.Test/WinFormsFrameworkTest.cs b/Tests/CrossFramework.Test/WinFormsFrameworkTest.cs index 7bbcc2db1..37ecacb07 100644 --- a/Tests/CrossFramework.Test/WinFormsFrameworkTest.cs +++ b/Tests/CrossFramework.Test/WinFormsFrameworkTest.cs @@ -75,85 +75,38 @@ public Task WinForms_Net10_RenameProtection() => outputDirSuffix: "-winforms-net10", checkOutput: false); - // --- Modern .NET: actually run the obfuscated UI app (shows a real window off-screen, - // pumps the message loop, self-closes after ~2s) and assert it starts without crashing. --- + // --- Actually run the obfuscated UI app on every framework: it shows its real window + // off-screen, pumps the message loop, self-closes after ~2s (exit 42), and an induced + // unhandled exception is caught by the crash handlers and reported as failure. --- - [Fact] - [Trait("Category", "CrossFramework")] - [Trait("AppType", "WinForms")] - [Trait("TFM", "net10.0")] - [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] - public Task WinForms_Net10_SelfTest_RunsWithoutCrashing() => - Run("CrossFramework.WinForms.Net10.dll", - null, - new SettingItem("rename"), - outputDirSuffix: "-winforms-net10-selftest", - checkOutput: false, - postProcessAction: outputPath => { - var (exit, stdout, stderr) = RunDotnetApp(outputPath, "CrossFramework.WinForms.Net10.dll", "--selftest"); - Assert.Equal(42, exit); - Assert.Contains("SHOWN:", stdout); - Assert.DoesNotContain("CRASH", stdout); - Assert.Empty(stderr); - return Task.CompletedTask; - }); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WinForms"), Trait("TFM", "net35"), Trait("Issue", "103")] + public Task WinForms_Net35_SelfTest_Runs() => SelfTestRunsWithoutCrashing("CrossFramework.WinForms.Net35.exe", "-winforms-net35-selftest"); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WinForms"), Trait("TFM", "net35"), Trait("Issue", "103")] + public Task WinForms_Net35_SelfTest_Crash() => SelfTestReportsCrash("CrossFramework.WinForms.Net35.exe", "-winforms-net35-crash"); - [Fact] - [Trait("Category", "CrossFramework")] - [Trait("AppType", "WinForms")] - [Trait("TFM", "net10.0")] - [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] - public Task WinForms_Net10_SelfTest_ReportsCrash() => - Run("CrossFramework.WinForms.Net10.dll", - null, - new SettingItem("rename"), - outputDirSuffix: "-winforms-net10-crash", - checkOutput: false, - postProcessAction: outputPath => { - var (exit, stdout, _) = RunDotnetApp(outputPath, "CrossFramework.WinForms.Net10.dll", "--selftest-crash"); - Assert.NotEqual(42, exit); - Assert.Contains("CRASH", stdout); - return Task.CompletedTask; - }); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WinForms"), Trait("TFM", "net40"), Trait("Issue", "103")] + public Task WinForms_Net40_SelfTest_Runs() => SelfTestRunsWithoutCrashing("CrossFramework.WinForms.Net40.exe", "-winforms-net40-selftest"); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WinForms"), Trait("TFM", "net40"), Trait("Issue", "103")] + public Task WinForms_Net40_SelfTest_Crash() => SelfTestReportsCrash("CrossFramework.WinForms.Net40.exe", "-winforms-net40-crash"); - [Fact] - [Trait("Category", "CrossFramework")] - [Trait("AppType", "WinForms")] - [Trait("TFM", "net6.0")] - [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] - public Task WinForms_Net6_SelfTest_RunsWithoutCrashing() => - Run("CrossFramework.WinForms.Net6.dll", - null, - new SettingItem("rename"), - outputDirSuffix: "-winforms-net6-selftest", - checkOutput: false, - postProcessAction: outputPath => { - var (exit, stdout, stderr) = RunDotnetApp(outputPath, "CrossFramework.WinForms.Net6.dll", "--selftest"); - Assert.Equal(42, exit); - Assert.Contains("SHOWN:", stdout); - Assert.DoesNotContain("CRASH", stdout); - Assert.Empty(stderr); - return Task.CompletedTask; - }); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WinForms"), Trait("TFM", "net48"), Trait("Issue", "103")] + public Task WinForms_Net48_SelfTest_Runs() => SelfTestRunsWithoutCrashing("CrossFramework.WinForms.Net48.exe", "-winforms-net48-selftest"); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WinForms"), Trait("TFM", "net48"), Trait("Issue", "103")] + public Task WinForms_Net48_SelfTest_Crash() => SelfTestReportsCrash("CrossFramework.WinForms.Net48.exe", "-winforms-net48-crash"); - [Fact] - [Trait("Category", "CrossFramework")] - [Trait("AppType", "WinForms")] - [Trait("TFM", "net8.0")] - [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] - public Task WinForms_Net8_SelfTest_RunsWithoutCrashing() => - Run("CrossFramework.WinForms.Net8.dll", - null, - new SettingItem("rename"), - outputDirSuffix: "-winforms-net8-selftest", - checkOutput: false, - postProcessAction: outputPath => { - var (exit, stdout, stderr) = RunDotnetApp(outputPath, "CrossFramework.WinForms.Net8.dll", "--selftest"); - Assert.Equal(42, exit); - Assert.Contains("SHOWN:", stdout); - Assert.DoesNotContain("CRASH", stdout); - Assert.Empty(stderr); - return Task.CompletedTask; - }); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WinForms"), Trait("TFM", "net6.0"), Trait("Issue", "103")] + public Task WinForms_Net6_SelfTest_Runs() => SelfTestRunsWithoutCrashing("CrossFramework.WinForms.Net6.dll", "-winforms-net6-selftest"); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WinForms"), Trait("TFM", "net6.0"), Trait("Issue", "103")] + public Task WinForms_Net6_SelfTest_Crash() => SelfTestReportsCrash("CrossFramework.WinForms.Net6.dll", "-winforms-net6-crash"); + + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WinForms"), Trait("TFM", "net8.0"), Trait("Issue", "103")] + public Task WinForms_Net8_SelfTest_Runs() => SelfTestRunsWithoutCrashing("CrossFramework.WinForms.Net8.dll", "-winforms-net8-selftest"); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WinForms"), Trait("TFM", "net8.0"), Trait("Issue", "103")] + public Task WinForms_Net8_SelfTest_Crash() => SelfTestReportsCrash("CrossFramework.WinForms.Net8.dll", "-winforms-net8-crash"); + + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WinForms"), Trait("TFM", "net10.0"), Trait("Issue", "103")] + public Task WinForms_Net10_SelfTest_Runs() => SelfTestRunsWithoutCrashing("CrossFramework.WinForms.Net10.dll", "-winforms-net10-selftest"); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WinForms"), Trait("TFM", "net10.0"), Trait("Issue", "103")] + public Task WinForms_Net10_SelfTest_Crash() => SelfTestReportsCrash("CrossFramework.WinForms.Net10.dll", "-winforms-net10-crash"); } } diff --git a/Tests/CrossFramework.Test/WpfFrameworkTest.cs b/Tests/CrossFramework.Test/WpfFrameworkTest.cs index 46aab952e..ec7bc7b0b 100644 --- a/Tests/CrossFramework.Test/WpfFrameworkTest.cs +++ b/Tests/CrossFramework.Test/WpfFrameworkTest.cs @@ -75,85 +75,38 @@ public Task WPF_Net10_RenameProtection() => outputDirSuffix: "-wpf-net10", checkOutput: false); - // --- Modern .NET: actually run the obfuscated WPF app (shows a real window off-screen, - // pumps the dispatcher, self-closes after ~2s) and assert it starts without crashing. --- + // --- Actually run the obfuscated WPF app on every framework: it shows its real window + // off-screen/invisible, pumps the dispatcher, self-terminates (exit 42), and an induced + // unhandled exception is caught by the crash handlers and reported as failure. --- - [Fact] - [Trait("Category", "CrossFramework")] - [Trait("AppType", "WPF")] - [Trait("TFM", "net10.0-windows")] - [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] - public Task WPF_Net10_SelfTest_RunsWithoutCrashing() => - Run("CrossFramework.WPF.Net10.dll", - null, - new SettingItem("rename"), - outputDirSuffix: "-wpf-net10-selftest", - checkOutput: false, - postProcessAction: outputPath => { - var (exit, stdout, stderr) = RunDotnetApp(outputPath, "CrossFramework.WPF.Net10.dll", "--selftest"); - Assert.Equal(42, exit); - Assert.Contains("SHOWN:", stdout); - Assert.DoesNotContain("CRASH", stdout); - Assert.Empty(stderr); - return Task.CompletedTask; - }); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WPF"), Trait("TFM", "net35"), Trait("Issue", "103")] + public Task WPF_Net35_SelfTest_Runs() => SelfTestRunsWithoutCrashing("CrossFramework.WPF.Net35.exe", "-wpf-net35-selftest"); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WPF"), Trait("TFM", "net35"), Trait("Issue", "103")] + public Task WPF_Net35_SelfTest_Crash() => SelfTestReportsCrash("CrossFramework.WPF.Net35.exe", "-wpf-net35-crash"); - [Fact] - [Trait("Category", "CrossFramework")] - [Trait("AppType", "WPF")] - [Trait("TFM", "net10.0-windows")] - [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] - public Task WPF_Net10_SelfTest_ReportsCrash() => - Run("CrossFramework.WPF.Net10.dll", - null, - new SettingItem("rename"), - outputDirSuffix: "-wpf-net10-crash", - checkOutput: false, - postProcessAction: outputPath => { - var (exit, stdout, _) = RunDotnetApp(outputPath, "CrossFramework.WPF.Net10.dll", "--selftest-crash"); - Assert.NotEqual(42, exit); - Assert.Contains("CRASH", stdout); - return Task.CompletedTask; - }); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WPF"), Trait("TFM", "net40"), Trait("Issue", "103")] + public Task WPF_Net40_SelfTest_Runs() => SelfTestRunsWithoutCrashing("CrossFramework.WPF.Net40.exe", "-wpf-net40-selftest"); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WPF"), Trait("TFM", "net40"), Trait("Issue", "103")] + public Task WPF_Net40_SelfTest_Crash() => SelfTestReportsCrash("CrossFramework.WPF.Net40.exe", "-wpf-net40-crash"); - [Fact] - [Trait("Category", "CrossFramework")] - [Trait("AppType", "WPF")] - [Trait("TFM", "net6.0-windows")] - [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] - public Task WPF_Net6_SelfTest_RunsWithoutCrashing() => - Run("CrossFramework.WPF.Net6.dll", - null, - new SettingItem("rename"), - outputDirSuffix: "-wpf-net6-selftest", - checkOutput: false, - postProcessAction: outputPath => { - var (exit, stdout, stderr) = RunDotnetApp(outputPath, "CrossFramework.WPF.Net6.dll", "--selftest"); - Assert.Equal(42, exit); - Assert.Contains("SHOWN:", stdout); - Assert.DoesNotContain("CRASH", stdout); - Assert.Empty(stderr); - return Task.CompletedTask; - }); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WPF"), Trait("TFM", "net48"), Trait("Issue", "103")] + public Task WPF_Net48_SelfTest_Runs() => SelfTestRunsWithoutCrashing("CrossFramework.WPF.Net48.exe", "-wpf-net48-selftest"); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WPF"), Trait("TFM", "net48"), Trait("Issue", "103")] + public Task WPF_Net48_SelfTest_Crash() => SelfTestReportsCrash("CrossFramework.WPF.Net48.exe", "-wpf-net48-crash"); - [Fact] - [Trait("Category", "CrossFramework")] - [Trait("AppType", "WPF")] - [Trait("TFM", "net8.0-windows")] - [Trait("Issue", "https://github.com/mcpolo99/ConfuserExx/issues/103")] - public Task WPF_Net8_SelfTest_RunsWithoutCrashing() => - Run("CrossFramework.WPF.Net8.dll", - null, - new SettingItem("rename"), - outputDirSuffix: "-wpf-net8-selftest", - checkOutput: false, - postProcessAction: outputPath => { - var (exit, stdout, stderr) = RunDotnetApp(outputPath, "CrossFramework.WPF.Net8.dll", "--selftest"); - Assert.Equal(42, exit); - Assert.Contains("SHOWN:", stdout); - Assert.DoesNotContain("CRASH", stdout); - Assert.Empty(stderr); - return Task.CompletedTask; - }); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WPF"), Trait("TFM", "net6.0-windows"), Trait("Issue", "103")] + public Task WPF_Net6_SelfTest_Runs() => SelfTestRunsWithoutCrashing("CrossFramework.WPF.Net6.dll", "-wpf-net6-selftest"); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WPF"), Trait("TFM", "net6.0-windows"), Trait("Issue", "103")] + public Task WPF_Net6_SelfTest_Crash() => SelfTestReportsCrash("CrossFramework.WPF.Net6.dll", "-wpf-net6-crash"); + + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WPF"), Trait("TFM", "net8.0-windows"), Trait("Issue", "103")] + public Task WPF_Net8_SelfTest_Runs() => SelfTestRunsWithoutCrashing("CrossFramework.WPF.Net8.dll", "-wpf-net8-selftest"); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WPF"), Trait("TFM", "net8.0-windows"), Trait("Issue", "103")] + public Task WPF_Net8_SelfTest_Crash() => SelfTestReportsCrash("CrossFramework.WPF.Net8.dll", "-wpf-net8-crash"); + + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WPF"), Trait("TFM", "net10.0-windows"), Trait("Issue", "103")] + public Task WPF_Net10_SelfTest_Runs() => SelfTestRunsWithoutCrashing("CrossFramework.WPF.Net10.dll", "-wpf-net10-selftest"); + [Fact, Trait("Category", "CrossFramework"), Trait("AppType", "WPF"), Trait("TFM", "net10.0-windows"), Trait("Issue", "103")] + public Task WPF_Net10_SelfTest_Crash() => SelfTestReportsCrash("CrossFramework.WPF.Net10.dll", "-wpf-net10-crash"); } } diff --git a/Tests/CrossFramework.WPF.Net35/CrossFramework.WPF.Net35.csproj b/Tests/CrossFramework.WPF.Net35/CrossFramework.WPF.Net35.csproj index 8af929669..64c9caf68 100644 --- a/Tests/CrossFramework.WPF.Net35/CrossFramework.WPF.Net35.csproj +++ b/Tests/CrossFramework.WPF.Net35/CrossFramework.WPF.Net35.csproj @@ -10,5 +10,6 @@ + diff --git a/Tests/CrossFramework.WPF.Net35/Program.cs b/Tests/CrossFramework.WPF.Net35/Program.cs index 7f8b185dc..3c344630c 100644 --- a/Tests/CrossFramework.WPF.Net35/Program.cs +++ b/Tests/CrossFramework.WPF.Net35/Program.cs @@ -38,6 +38,12 @@ static int Main(string[] args) { return 42; } + if (args.Length > 0 && (args[0] == "--selftest" || args[0] == "--selftest-crash")) { + var selfTestApp = new Application(); + CrossFramework.SelfTest.WpfSelfTestHost.Run(selfTestApp, new MainWindow(), induceCrash: args[0] == "--selftest-crash"); + return selfTestApp.Run(); + } + var app = new Application(); app.Run(new MainWindow()); return 0; diff --git a/Tests/CrossFramework.WPF.Net40/CrossFramework.WPF.Net40.csproj b/Tests/CrossFramework.WPF.Net40/CrossFramework.WPF.Net40.csproj index 27f404649..4c6ba01a3 100644 --- a/Tests/CrossFramework.WPF.Net40/CrossFramework.WPF.Net40.csproj +++ b/Tests/CrossFramework.WPF.Net40/CrossFramework.WPF.Net40.csproj @@ -10,5 +10,6 @@ + diff --git a/Tests/CrossFramework.WPF.Net40/Program.cs b/Tests/CrossFramework.WPF.Net40/Program.cs index 35a9e55e7..4d81a4378 100644 --- a/Tests/CrossFramework.WPF.Net40/Program.cs +++ b/Tests/CrossFramework.WPF.Net40/Program.cs @@ -38,6 +38,12 @@ static int Main(string[] args) { return 42; } + if (args.Length > 0 && (args[0] == "--selftest" || args[0] == "--selftest-crash")) { + var selfTestApp = new Application(); + CrossFramework.SelfTest.WpfSelfTestHost.Run(selfTestApp, new MainWindow(), induceCrash: args[0] == "--selftest-crash"); + return selfTestApp.Run(); + } + var app = new Application(); app.Run(new MainWindow()); return 0; diff --git a/Tests/CrossFramework.WPF.Net48/App.xaml b/Tests/CrossFramework.WPF.Net48/App.xaml index a13360fb3..26cf87f1d 100644 --- a/Tests/CrossFramework.WPF.Net48/App.xaml +++ b/Tests/CrossFramework.WPF.Net48/App.xaml @@ -1,5 +1,4 @@ + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> diff --git a/Tests/CrossFramework.WPF.Net48/App.xaml.cs b/Tests/CrossFramework.WPF.Net48/App.xaml.cs index 8ca54a6ce..367a0aa23 100644 --- a/Tests/CrossFramework.WPF.Net48/App.xaml.cs +++ b/Tests/CrossFramework.WPF.Net48/App.xaml.cs @@ -12,7 +12,15 @@ protected override void OnStartup(StartupEventArgs e) { Console.WriteLine("Content: " + window.GetStatusText()); Console.WriteLine("END"); Shutdown(42); + return; } + + if (e.Args.Length > 0 && (e.Args[0] == "--selftest" || e.Args[0] == "--selftest-crash")) { + CrossFramework.SelfTest.WpfSelfTestHost.Run(this, new MainWindow(), induceCrash: e.Args[0] == "--selftest-crash"); + return; + } + + new MainWindow().Show(); } } } diff --git a/Tests/CrossFramework.WPF.Net48/CrossFramework.WPF.Net48.csproj b/Tests/CrossFramework.WPF.Net48/CrossFramework.WPF.Net48.csproj index fdeaebcc1..56aa48505 100644 --- a/Tests/CrossFramework.WPF.Net48/CrossFramework.WPF.Net48.csproj +++ b/Tests/CrossFramework.WPF.Net48/CrossFramework.WPF.Net48.csproj @@ -5,4 +5,7 @@ true 7.3 + + + diff --git a/Tests/CrossFramework.WinForms.Net35/CrossFramework.WinForms.Net35.csproj b/Tests/CrossFramework.WinForms.Net35/CrossFramework.WinForms.Net35.csproj index eb16ee086..4b7fa9e07 100644 --- a/Tests/CrossFramework.WinForms.Net35/CrossFramework.WinForms.Net35.csproj +++ b/Tests/CrossFramework.WinForms.Net35/CrossFramework.WinForms.Net35.csproj @@ -8,5 +8,6 @@ + diff --git a/Tests/CrossFramework.WinForms.Net35/Program.cs b/Tests/CrossFramework.WinForms.Net35/Program.cs index ab393762f..2a6dde0c6 100644 --- a/Tests/CrossFramework.WinForms.Net35/Program.cs +++ b/Tests/CrossFramework.WinForms.Net35/Program.cs @@ -15,6 +15,9 @@ static int Main(string[] args) { return 42; } + if (args.Length > 0 && (args[0] == "--selftest" || args[0] == "--selftest-crash")) + return CrossFramework.SelfTest.WinFormsSelfTestHost.Run(() => new MainForm(), induceCrash: args[0] == "--selftest-crash"); + Application.EnableVisualStyles(); Application.Run(new MainForm()); return 0; diff --git a/Tests/CrossFramework.WinForms.Net40/CrossFramework.WinForms.Net40.csproj b/Tests/CrossFramework.WinForms.Net40/CrossFramework.WinForms.Net40.csproj index 91d1159f8..fa63b342a 100644 --- a/Tests/CrossFramework.WinForms.Net40/CrossFramework.WinForms.Net40.csproj +++ b/Tests/CrossFramework.WinForms.Net40/CrossFramework.WinForms.Net40.csproj @@ -8,5 +8,6 @@ + diff --git a/Tests/CrossFramework.WinForms.Net40/Program.cs b/Tests/CrossFramework.WinForms.Net40/Program.cs index ab393762f..2a6dde0c6 100644 --- a/Tests/CrossFramework.WinForms.Net40/Program.cs +++ b/Tests/CrossFramework.WinForms.Net40/Program.cs @@ -15,6 +15,9 @@ static int Main(string[] args) { return 42; } + if (args.Length > 0 && (args[0] == "--selftest" || args[0] == "--selftest-crash")) + return CrossFramework.SelfTest.WinFormsSelfTestHost.Run(() => new MainForm(), induceCrash: args[0] == "--selftest-crash"); + Application.EnableVisualStyles(); Application.Run(new MainForm()); return 0; diff --git a/Tests/CrossFramework.WinForms.Net48/CrossFramework.WinForms.Net48.csproj b/Tests/CrossFramework.WinForms.Net48/CrossFramework.WinForms.Net48.csproj index d47231c13..7406e3c63 100644 --- a/Tests/CrossFramework.WinForms.Net48/CrossFramework.WinForms.Net48.csproj +++ b/Tests/CrossFramework.WinForms.Net48/CrossFramework.WinForms.Net48.csproj @@ -5,4 +5,7 @@ true 7.3 + + + diff --git a/Tests/CrossFramework.WinForms.Net48/Program.cs b/Tests/CrossFramework.WinForms.Net48/Program.cs index e438e143c..41ebbbf16 100644 --- a/Tests/CrossFramework.WinForms.Net48/Program.cs +++ b/Tests/CrossFramework.WinForms.Net48/Program.cs @@ -16,6 +16,9 @@ static int Main(string[] args) { return 42; } + if (args.Length > 0 && (args[0] == "--selftest" || args[0] == "--selftest-crash")) + return CrossFramework.SelfTest.WinFormsSelfTestHost.Run(() => new MainForm(), induceCrash: args[0] == "--selftest-crash"); + Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); diff --git a/Tests/Shared/ConsoleSelfTestHost.cs b/Tests/Shared/ConsoleSelfTestHost.cs new file mode 100644 index 000000000..52795a615 --- /dev/null +++ b/Tests/Shared/ConsoleSelfTestHost.cs @@ -0,0 +1,28 @@ +using System; + +namespace CrossFramework.SelfTest { + // Shared across the console subject apps (linked, not duplicated). Runs the app and reports + // failure if anything goes unhandled. Console apps have no window, so "running" simply means + // executing to completion; the crash path proves the unhandled-exception handler reports it. + // + // Contract: prints START / RUN / END and exits 42 on a clean run; on a crash prints + // "CRASH: ..." and exits 1. System.Console is fully qualified because the subject apps live in + // the CrossFramework.Console namespace, which would otherwise shadow it. + internal static class ConsoleSelfTestHost { + public static int Run(bool induceCrash) { + AppDomain.CurrentDomain.UnhandledException += (sender, e) => { + var ex = e.ExceptionObject as Exception; + System.Console.WriteLine("CRASH: [AppDomain] " + (ex != null ? ex.Message : "unknown")); + System.Console.Out.Flush(); + Environment.Exit(1); + }; + + System.Console.WriteLine("START"); + System.Console.WriteLine("RUN"); + if (induceCrash) + throw new InvalidOperationException("Induced self-test crash"); + System.Console.WriteLine("END"); + return 42; + } + } +} diff --git a/Tests/Shared/WinFormsSelfTestHost.cs b/Tests/Shared/WinFormsSelfTestHost.cs index 0adb5f237..a8f6049b5 100644 --- a/Tests/Shared/WinFormsSelfTestHost.cs +++ b/Tests/Shared/WinFormsSelfTestHost.cs @@ -4,12 +4,13 @@ namespace CrossFramework.SelfTest { // Shared across the WinForms subject apps (linked, not duplicated). Actually runs the app: - // shows the real main window off-screen, lets the message loop pump briefly, then closes it. - // Any unhandled exception (UI thread or background) makes the process report failure. + // shows the real main window (invisible + off-screen), lets the message loop pump briefly, then + // ends the process. Any unhandled exception (UI thread or background) makes it report failure. // // Contract: prints START / SHOWN / END and exits 42 on a clean run; on a crash prints - // "CRASH: ..." and exits 1. The window is placed off-screen with no taskbar entry so it is - // non-intrusive on a developer machine while still exercising a genuine window + message loop. + // "CRASH: ..." and exits 1. Termination is driven by a thread-pool watchdog rather than a UI + // timer, so it always ends even if the message loop never closes the window (e.g. under + // obfuscation, where the WinForms Timer proved unreliable). internal static class WinFormsSelfTestHost { public static int Run(Func formFactory, bool induceCrash = false) { AppDomain.CurrentDomain.UnhandledException += (sender, e) => @@ -24,25 +25,33 @@ public static int Run(Func formFactory, bool induceCrash = false) { form.StartPosition = FormStartPosition.Manual; form.Location = new Point(-32000, -32000); form.ShowInTaskbar = false; + form.WindowState = FormWindowState.Minimized; + form.Opacity = 0d; form.Shown += (sender, e) => { Console.WriteLine("SHOWN: " + form.Text); if (induceCrash) throw new InvalidOperationException("Induced self-test crash"); - - var timer = new Timer { Interval = 2000 }; - timer.Tick += (s, args) => { - timer.Stop(); - form.Close(); - }; - timer.Start(); }; + // Guaranteed terminator, independent of the UI message loop: once the window has had a + // moment to come up (and crash if it is going to), end the process cleanly from a + // thread-pool thread. Rooted across Application.Run so it cannot be collected first. + var watchdog = new System.Threading.Timer( + _ => Succeed(), null, 2000, System.Threading.Timeout.Infinite); + Application.Run(form); - Console.WriteLine("END"); + GC.KeepAlive(watchdog); + Succeed(); return 42; } + static void Succeed() { + Console.WriteLine("END"); + Console.Out.Flush(); + Environment.Exit(42); + } + static void Fail(string source, Exception ex) { Console.WriteLine("CRASH: [" + source + "] " + (ex != null ? ex.Message : "unknown")); Console.Out.Flush(); diff --git a/Tests/Shared/WpfSelfTestHost.cs b/Tests/Shared/WpfSelfTestHost.cs index b7d7025e7..8a02cad41 100644 --- a/Tests/Shared/WpfSelfTestHost.cs +++ b/Tests/Shared/WpfSelfTestHost.cs @@ -1,15 +1,14 @@ using System; using System.Windows; -using System.Windows.Threading; namespace CrossFramework.SelfTest { // Shared across the WPF subject apps (linked, not duplicated). Shows the real main window - // off-screen inside the already-running Application, lets it live briefly, then closes it. - // Any unhandled exception (dispatcher or background) makes the process report failure. + // (invisible + off-screen) inside the already-running Application, lets it live briefly, then + // ends the process. Any unhandled exception (dispatcher or background) makes it report failure. // // Contract: prints START / SHOWN / END and exits 42 on a clean run; on a crash prints - // "CRASH: ..." and exits 1. Call from Application.OnStartup; the app's own Run loop keeps - // pumping, and closing the window shuts the app down with exit code 42. + // "CRASH: ..." and exits 1. Termination is driven by a thread-pool watchdog rather than a UI + // timer, so it always ends even if the dispatcher never closes the window. internal static class WpfSelfTestHost { public static void Run(Application app, Window window, bool induceCrash = false) { AppDomain.CurrentDomain.UnhandledException += (sender, e) => @@ -20,6 +19,9 @@ public static void Run(Application app, Window window, bool induceCrash = false) Console.WriteLine("START"); window.ShowInTaskbar = false; + window.ShowActivated = false; + window.WindowState = WindowState.Minimized; + window.Opacity = 0d; window.WindowStartupLocation = WindowStartupLocation.Manual; window.Left = -32000; window.Top = -32000; @@ -28,26 +30,24 @@ public static void Run(Application app, Window window, bool induceCrash = false) Console.WriteLine("SHOWN: " + window.Title); if (induceCrash) throw new InvalidOperationException("Induced self-test crash"); - - var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) }; - timer.Tick += (s, args) => { - timer.Stop(); - window.Close(); - }; - timer.Start(); }; - window.Closed += (sender, e) => { - Console.WriteLine("END"); - // WPF's generated Main is void and ignores Run()'s return value, so set the - // process exit code explicitly rather than relying on Shutdown's argument. - Environment.ExitCode = 42; - app.Shutdown(); - }; + // Guaranteed terminator, independent of the dispatcher (see WinForms host). Rooted for + // the app's lifetime by the running Application, which holds this host on its stack. + _watchdog = new System.Threading.Timer( + _ => Succeed(), null, 2000, System.Threading.Timeout.Infinite); window.Show(); } + static System.Threading.Timer _watchdog; + + static void Succeed() { + Console.WriteLine("END"); + Console.Out.Flush(); + Environment.Exit(42); + } + static void Fail(string source, Exception ex) { Console.WriteLine("CRASH: [" + source + "] " + (ex != null ? ex.Message : "unknown")); Console.Out.Flush(); From 62cc37c60fb726d7a95b0b1134bd5b2c04b18273 Mon Sep 17 00:00:00 2001 From: RandomCrocodile Date: Mon, 21 Sep 2026 18:47:14 +0200 Subject: [PATCH 6/6] chore: restructure CrossFramework test projects into per-apptype subfolders --- Confuser2.sln | 50 +++++++++---------- .../CrossFramework.Test.csproj | 40 --------------- .../CrossFramework.Console.Net10.csproj | 0 .../Console/Net10}/Program.cs | 0 .../Console/Net10}/Resource.txt | 0 .../CrossFramework.Console.Net20.csproj | 0 .../Console/Net20}/Program.cs | 0 .../CrossFramework.Console.Net35.csproj | 0 .../Console/Net35}/Program.cs | 0 .../CrossFramework.Console.Net40.csproj | 0 .../Console/Net40}/Program.cs | 0 .../CrossFramework.Console.Net48.csproj | 0 .../Console/Net48}/Program.cs | 0 .../Net6}/CrossFramework.Console.Net6.csproj | 2 +- .../Console/Net6}/Program.cs | 0 .../Net8}/CrossFramework.Console.Net8.csproj | 2 +- .../Console/Net8}/Program.cs | 0 .../CrossFramework.Library.Net10.csproj | 0 .../Library/Net10}/SampleService.cs | 0 .../CrossFramework.Library.Net48.csproj | 0 .../Library/Net48}/SampleService.cs | 0 .../Net6}/CrossFramework.Library.Net6.csproj | 0 .../Library/Net6}/SampleService.cs | 0 .../Net8}/CrossFramework.Library.Net8.csproj | 0 .../Library/Net8}/SampleService.cs | 0 .../CrossFramework.Library.NetStd20.csproj | 0 .../Library/NetStd20}/SampleService.cs | 0 .../Test}/ConsoleFrameworkTest.cs | 0 .../Test/CrossFramework.Test.csproj | 40 +++++++++++++++ .../Test}/LibraryFrameworkTest.cs | 0 .../Test}/WinFormsFrameworkTest.cs | 0 .../Test}/WpfFrameworkTest.cs | 0 .../WPF/Net10}/App.xaml | 0 .../WPF/Net10}/App.xaml.cs | 0 .../Net10}/CrossFramework.WPF.Net10.csproj | 2 +- .../WPF/Net10}/MainWindow.xaml | 0 .../WPF/Net10}/MainWindow.xaml.cs | 0 .../Net35}/CrossFramework.WPF.Net35.csproj | 2 +- .../WPF/Net35}/Program.cs | 0 .../Net40}/CrossFramework.WPF.Net40.csproj | 2 +- .../WPF/Net40}/Program.cs | 0 .../WPF/Net48}/App.xaml | 0 .../WPF/Net48}/App.xaml.cs | 0 .../Net48}/CrossFramework.WPF.Net48.csproj | 2 +- .../WPF/Net48}/MainWindow.xaml | 0 .../WPF/Net48}/MainWindow.xaml.cs | 0 .../WPF/Net6}/App.xaml | 0 .../WPF/Net6}/App.xaml.cs | 0 .../WPF/Net6}/CrossFramework.WPF.Net6.csproj | 2 +- .../WPF/Net6}/MainWindow.xaml | 0 .../WPF/Net6}/MainWindow.xaml.cs | 0 .../WPF/Net8}/App.xaml | 0 .../WPF/Net8}/App.xaml.cs | 0 .../WPF/Net8}/CrossFramework.WPF.Net8.csproj | 2 +- .../WPF/Net8}/MainWindow.xaml | 0 .../WPF/Net8}/MainWindow.xaml.cs | 0 .../CrossFramework.WinForms.Net10.csproj | 2 +- .../WinForms/Net10}/MainForm.cs | 0 .../WinForms/Net10}/Program.cs | 0 .../CrossFramework.WinForms.Net35.csproj | 2 +- .../WinForms/Net35}/MainForm.cs | 0 .../WinForms/Net35}/Program.cs | 0 .../CrossFramework.WinForms.Net40.csproj | 2 +- .../WinForms/Net40}/MainForm.cs | 0 .../WinForms/Net40}/Program.cs | 0 .../CrossFramework.WinForms.Net48.csproj | 2 +- .../WinForms/Net48}/MainForm.cs | 0 .../WinForms/Net48}/Program.cs | 0 .../Net6}/CrossFramework.WinForms.Net6.csproj | 2 +- .../WinForms/Net6}/MainForm.cs | 0 .../WinForms/Net6}/Program.cs | 0 .../Net8}/CrossFramework.WinForms.Net8.csproj | 2 +- .../WinForms/Net8}/MainForm.cs | 0 .../WinForms/Net8}/Program.cs | 0 74 files changed, 79 insertions(+), 79 deletions(-) delete mode 100644 Tests/CrossFramework.Test/CrossFramework.Test.csproj rename Tests/{CrossFramework.Console.Net10 => CrossFramework/Console/Net10}/CrossFramework.Console.Net10.csproj (100%) rename Tests/{CrossFramework.Console.Net10 => CrossFramework/Console/Net10}/Program.cs (100%) rename Tests/{CrossFramework.Console.Net10 => CrossFramework/Console/Net10}/Resource.txt (100%) rename Tests/{CrossFramework.Console.Net20 => CrossFramework/Console/Net20}/CrossFramework.Console.Net20.csproj (100%) rename Tests/{CrossFramework.Console.Net20 => CrossFramework/Console/Net20}/Program.cs (100%) rename Tests/{CrossFramework.Console.Net35 => CrossFramework/Console/Net35}/CrossFramework.Console.Net35.csproj (100%) rename Tests/{CrossFramework.Console.Net35 => CrossFramework/Console/Net35}/Program.cs (100%) rename Tests/{CrossFramework.Console.Net40 => CrossFramework/Console/Net40}/CrossFramework.Console.Net40.csproj (100%) rename Tests/{CrossFramework.Console.Net40 => CrossFramework/Console/Net40}/Program.cs (100%) rename Tests/{CrossFramework.Console.Net48 => CrossFramework/Console/Net48}/CrossFramework.Console.Net48.csproj (100%) rename Tests/{CrossFramework.Console.Net48 => CrossFramework/Console/Net48}/Program.cs (100%) rename Tests/{CrossFramework.Console.Net6 => CrossFramework/Console/Net6}/CrossFramework.Console.Net6.csproj (71%) rename Tests/{CrossFramework.Console.Net6 => CrossFramework/Console/Net6}/Program.cs (100%) rename Tests/{CrossFramework.Console.Net8 => CrossFramework/Console/Net8}/CrossFramework.Console.Net8.csproj (71%) rename Tests/{CrossFramework.Console.Net8 => CrossFramework/Console/Net8}/Program.cs (100%) rename Tests/{CrossFramework.Library.Net10 => CrossFramework/Library/Net10}/CrossFramework.Library.Net10.csproj (100%) rename Tests/{CrossFramework.Library.Net10 => CrossFramework/Library/Net10}/SampleService.cs (100%) rename Tests/{CrossFramework.Library.Net48 => CrossFramework/Library/Net48}/CrossFramework.Library.Net48.csproj (100%) rename Tests/{CrossFramework.Library.Net48 => CrossFramework/Library/Net48}/SampleService.cs (100%) rename Tests/{CrossFramework.Library.Net6 => CrossFramework/Library/Net6}/CrossFramework.Library.Net6.csproj (100%) rename Tests/{CrossFramework.Library.Net6 => CrossFramework/Library/Net6}/SampleService.cs (100%) rename Tests/{CrossFramework.Library.Net8 => CrossFramework/Library/Net8}/CrossFramework.Library.Net8.csproj (100%) rename Tests/{CrossFramework.Library.Net8 => CrossFramework/Library/Net8}/SampleService.cs (100%) rename Tests/{CrossFramework.Library.NetStd20 => CrossFramework/Library/NetStd20}/CrossFramework.Library.NetStd20.csproj (100%) rename Tests/{CrossFramework.Library.NetStd20 => CrossFramework/Library/NetStd20}/SampleService.cs (100%) rename Tests/{CrossFramework.Test => CrossFramework/Test}/ConsoleFrameworkTest.cs (100%) create mode 100644 Tests/CrossFramework/Test/CrossFramework.Test.csproj rename Tests/{CrossFramework.Test => CrossFramework/Test}/LibraryFrameworkTest.cs (100%) rename Tests/{CrossFramework.Test => CrossFramework/Test}/WinFormsFrameworkTest.cs (100%) rename Tests/{CrossFramework.Test => CrossFramework/Test}/WpfFrameworkTest.cs (100%) rename Tests/{CrossFramework.WPF.Net10 => CrossFramework/WPF/Net10}/App.xaml (100%) rename Tests/{CrossFramework.WPF.Net10 => CrossFramework/WPF/Net10}/App.xaml.cs (100%) rename Tests/{CrossFramework.WPF.Net10 => CrossFramework/WPF/Net10}/CrossFramework.WPF.Net10.csproj (75%) rename Tests/{CrossFramework.WPF.Net10 => CrossFramework/WPF/Net10}/MainWindow.xaml (100%) rename Tests/{CrossFramework.WPF.Net10 => CrossFramework/WPF/Net10}/MainWindow.xaml.cs (100%) rename Tests/{CrossFramework.WPF.Net35 => CrossFramework/WPF/Net35}/CrossFramework.WPF.Net35.csproj (85%) rename Tests/{CrossFramework.WPF.Net35 => CrossFramework/WPF/Net35}/Program.cs (100%) rename Tests/{CrossFramework.WPF.Net40 => CrossFramework/WPF/Net40}/CrossFramework.WPF.Net40.csproj (85%) rename Tests/{CrossFramework.WPF.Net40 => CrossFramework/WPF/Net40}/Program.cs (100%) rename Tests/{CrossFramework.WPF.Net48 => CrossFramework/WPF/Net48}/App.xaml (100%) rename Tests/{CrossFramework.WPF.Net48 => CrossFramework/WPF/Net48}/App.xaml.cs (100%) rename Tests/{CrossFramework.WPF.Net48 => CrossFramework/WPF/Net48}/CrossFramework.WPF.Net48.csproj (74%) rename Tests/{CrossFramework.WPF.Net48 => CrossFramework/WPF/Net48}/MainWindow.xaml (100%) rename Tests/{CrossFramework.WPF.Net48 => CrossFramework/WPF/Net48}/MainWindow.xaml.cs (100%) rename Tests/{CrossFramework.WPF.Net6 => CrossFramework/WPF/Net6}/App.xaml (100%) rename Tests/{CrossFramework.WPF.Net6 => CrossFramework/WPF/Net6}/App.xaml.cs (100%) rename Tests/{CrossFramework.WPF.Net6 => CrossFramework/WPF/Net6}/CrossFramework.WPF.Net6.csproj (75%) rename Tests/{CrossFramework.WPF.Net6 => CrossFramework/WPF/Net6}/MainWindow.xaml (100%) rename Tests/{CrossFramework.WPF.Net6 => CrossFramework/WPF/Net6}/MainWindow.xaml.cs (100%) rename Tests/{CrossFramework.WPF.Net8 => CrossFramework/WPF/Net8}/App.xaml (100%) rename Tests/{CrossFramework.WPF.Net8 => CrossFramework/WPF/Net8}/App.xaml.cs (100%) rename Tests/{CrossFramework.WPF.Net8 => CrossFramework/WPF/Net8}/CrossFramework.WPF.Net8.csproj (75%) rename Tests/{CrossFramework.WPF.Net8 => CrossFramework/WPF/Net8}/MainWindow.xaml (100%) rename Tests/{CrossFramework.WPF.Net8 => CrossFramework/WPF/Net8}/MainWindow.xaml.cs (100%) rename Tests/{CrossFramework.WinForms.Net10 => CrossFramework/WinForms/Net10}/CrossFramework.WinForms.Net10.csproj (75%) rename Tests/{CrossFramework.WinForms.Net10 => CrossFramework/WinForms/Net10}/MainForm.cs (100%) rename Tests/{CrossFramework.WinForms.Net10 => CrossFramework/WinForms/Net10}/Program.cs (100%) rename Tests/{CrossFramework.WinForms.Net35 => CrossFramework/WinForms/Net35}/CrossFramework.WinForms.Net35.csproj (81%) rename Tests/{CrossFramework.WinForms.Net35 => CrossFramework/WinForms/Net35}/MainForm.cs (100%) rename Tests/{CrossFramework.WinForms.Net35 => CrossFramework/WinForms/Net35}/Program.cs (100%) rename Tests/{CrossFramework.WinForms.Net40 => CrossFramework/WinForms/Net40}/CrossFramework.WinForms.Net40.csproj (81%) rename Tests/{CrossFramework.WinForms.Net40 => CrossFramework/WinForms/Net40}/MainForm.cs (100%) rename Tests/{CrossFramework.WinForms.Net40 => CrossFramework/WinForms/Net40}/Program.cs (100%) rename Tests/{CrossFramework.WinForms.Net48 => CrossFramework/WinForms/Net48}/CrossFramework.WinForms.Net48.csproj (73%) rename Tests/{CrossFramework.WinForms.Net48 => CrossFramework/WinForms/Net48}/MainForm.cs (100%) rename Tests/{CrossFramework.WinForms.Net48 => CrossFramework/WinForms/Net48}/Program.cs (100%) rename Tests/{CrossFramework.WinForms.Net6 => CrossFramework/WinForms/Net6}/CrossFramework.WinForms.Net6.csproj (74%) rename Tests/{CrossFramework.WinForms.Net6 => CrossFramework/WinForms/Net6}/MainForm.cs (100%) rename Tests/{CrossFramework.WinForms.Net6 => CrossFramework/WinForms/Net6}/Program.cs (100%) rename Tests/{CrossFramework.WinForms.Net8 => CrossFramework/WinForms/Net8}/CrossFramework.WinForms.Net8.csproj (74%) rename Tests/{CrossFramework.WinForms.Net8 => CrossFramework/WinForms/Net8}/MainForm.cs (100%) rename Tests/{CrossFramework.WinForms.Net8 => CrossFramework/WinForms/Net8}/Program.cs (100%) diff --git a/Confuser2.sln b/Confuser2.sln index 9dc5bf919..88757d2fe 100644 --- a/Confuser2.sln +++ b/Confuser2.sln @@ -155,55 +155,55 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Confuser.CLI.Test", "Tests\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Confuser.GUI.Test", "Tests\Confuser.GUI.Test\Confuser.GUI.Test.csproj", "{C3EA99B7-7B9F-4589-BCCC-5D3D61C082B5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Console.Net10", "Tests\CrossFramework.Console.Net10\CrossFramework.Console.Net10.csproj", "{DA4385C6-BEEA-475F-947D-D06CC15AE36F}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Console.Net10", "Tests\CrossFramework\Console\Net10\CrossFramework.Console.Net10.csproj", "{DA4385C6-BEEA-475F-947D-D06CC15AE36F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Console.Net20", "Tests\CrossFramework.Console.Net20\CrossFramework.Console.Net20.csproj", "{6D4C6502-8155-49DD-BBB1-7E99F5782738}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Console.Net20", "Tests\CrossFramework\Console\Net20\CrossFramework.Console.Net20.csproj", "{6D4C6502-8155-49DD-BBB1-7E99F5782738}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Console.Net35", "Tests\CrossFramework.Console.Net35\CrossFramework.Console.Net35.csproj", "{56A9138E-23D0-45AE-AA0C-EB27AC14064C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Console.Net35", "Tests\CrossFramework\Console\Net35\CrossFramework.Console.Net35.csproj", "{56A9138E-23D0-45AE-AA0C-EB27AC14064C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Console.Net40", "Tests\CrossFramework.Console.Net40\CrossFramework.Console.Net40.csproj", "{2B085FD5-3AC9-4E5E-A9B1-E8A7B0628FD7}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Console.Net40", "Tests\CrossFramework\Console\Net40\CrossFramework.Console.Net40.csproj", "{2B085FD5-3AC9-4E5E-A9B1-E8A7B0628FD7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Console.Net48", "Tests\CrossFramework.Console.Net48\CrossFramework.Console.Net48.csproj", "{7DDCA8D5-BE85-4BD4-9B47-85B29D2FA3AF}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Console.Net48", "Tests\CrossFramework\Console\Net48\CrossFramework.Console.Net48.csproj", "{7DDCA8D5-BE85-4BD4-9B47-85B29D2FA3AF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Console.Net6", "Tests\CrossFramework.Console.Net6\CrossFramework.Console.Net6.csproj", "{FA75BDE0-9364-4A18-BF26-2110DA417797}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Console.Net6", "Tests\CrossFramework\Console\Net6\CrossFramework.Console.Net6.csproj", "{FA75BDE0-9364-4A18-BF26-2110DA417797}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Console.Net8", "Tests\CrossFramework.Console.Net8\CrossFramework.Console.Net8.csproj", "{9273835F-C47F-4705-87AE-0EF7BDBFC85A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Console.Net8", "Tests\CrossFramework\Console\Net8\CrossFramework.Console.Net8.csproj", "{9273835F-C47F-4705-87AE-0EF7BDBFC85A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Library.Net8", "Tests\CrossFramework.Library.Net8\CrossFramework.Library.Net8.csproj", "{3C326F28-33CE-44FC-89BE-2DAA0D087644}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Library.Net8", "Tests\CrossFramework\Library\Net8\CrossFramework.Library.Net8.csproj", "{3C326F28-33CE-44FC-89BE-2DAA0D087644}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Library.NetStd20", "Tests\CrossFramework.Library.NetStd20\CrossFramework.Library.NetStd20.csproj", "{61AC60A9-C462-44BB-B792-3D6EA1C1FFAA}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Library.NetStd20", "Tests\CrossFramework\Library\NetStd20\CrossFramework.Library.NetStd20.csproj", "{61AC60A9-C462-44BB-B792-3D6EA1C1FFAA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Test", "Tests\CrossFramework.Test\CrossFramework.Test.csproj", "{88CA9001-6138-4D0F-98D9-EC59A75FE1E8}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Test", "Tests\CrossFramework\Test\CrossFramework.Test.csproj", "{88CA9001-6138-4D0F-98D9-EC59A75FE1E8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WinForms.Net35", "Tests\CrossFramework.WinForms.Net35\CrossFramework.WinForms.Net35.csproj", "{EA611DF7-7FA6-4FA4-BE18-E6C569618FB0}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WinForms.Net35", "Tests\CrossFramework\WinForms\Net35\CrossFramework.WinForms.Net35.csproj", "{EA611DF7-7FA6-4FA4-BE18-E6C569618FB0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WinForms.Net40", "Tests\CrossFramework.WinForms.Net40\CrossFramework.WinForms.Net40.csproj", "{1227C9C3-068C-4643-8E7C-8D90552243EE}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WinForms.Net40", "Tests\CrossFramework\WinForms\Net40\CrossFramework.WinForms.Net40.csproj", "{1227C9C3-068C-4643-8E7C-8D90552243EE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WinForms.Net48", "Tests\CrossFramework.WinForms.Net48\CrossFramework.WinForms.Net48.csproj", "{8A95BAB0-ACFF-43B5-BA77-A78A3C24611B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WinForms.Net48", "Tests\CrossFramework\WinForms\Net48\CrossFramework.WinForms.Net48.csproj", "{8A95BAB0-ACFF-43B5-BA77-A78A3C24611B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WinForms.Net10", "Tests\CrossFramework.WinForms.Net10\CrossFramework.WinForms.Net10.csproj", "{0323A3B1-8607-4914-894F-08BBBD1D8618}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WinForms.Net10", "Tests\CrossFramework\WinForms\Net10\CrossFramework.WinForms.Net10.csproj", "{0323A3B1-8607-4914-894F-08BBBD1D8618}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WPF.Net35", "Tests\CrossFramework.WPF.Net35\CrossFramework.WPF.Net35.csproj", "{CB4835D2-06BC-472F-9B5A-D3844C5D8D81}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WPF.Net35", "Tests\CrossFramework\WPF\Net35\CrossFramework.WPF.Net35.csproj", "{CB4835D2-06BC-472F-9B5A-D3844C5D8D81}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WPF.Net40", "Tests\CrossFramework.WPF.Net40\CrossFramework.WPF.Net40.csproj", "{79A8C4D6-2199-4B0D-BCBE-E0FCA93A8BDC}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WPF.Net40", "Tests\CrossFramework\WPF\Net40\CrossFramework.WPF.Net40.csproj", "{79A8C4D6-2199-4B0D-BCBE-E0FCA93A8BDC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WPF.Net48", "Tests\CrossFramework.WPF.Net48\CrossFramework.WPF.Net48.csproj", "{EB8D610B-E413-4C6E-97A2-AA92EA2C1DA0}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WPF.Net48", "Tests\CrossFramework\WPF\Net48\CrossFramework.WPF.Net48.csproj", "{EB8D610B-E413-4C6E-97A2-AA92EA2C1DA0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WPF.Net10", "Tests\CrossFramework.WPF.Net10\CrossFramework.WPF.Net10.csproj", "{DE876679-5C28-45B3-BDFB-952C9C586CA1}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WPF.Net10", "Tests\CrossFramework\WPF\Net10\CrossFramework.WPF.Net10.csproj", "{DE876679-5C28-45B3-BDFB-952C9C586CA1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WinForms.Net6", "Tests\CrossFramework.WinForms.Net6\CrossFramework.WinForms.Net6.csproj", "{3FB8F727-BD28-455C-A14A-09DCE7C42E90}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WinForms.Net6", "Tests\CrossFramework\WinForms\Net6\CrossFramework.WinForms.Net6.csproj", "{3FB8F727-BD28-455C-A14A-09DCE7C42E90}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WinForms.Net8", "Tests\CrossFramework.WinForms.Net8\CrossFramework.WinForms.Net8.csproj", "{424E07DB-C32E-4AAE-BE50-D839AF541041}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WinForms.Net8", "Tests\CrossFramework\WinForms\Net8\CrossFramework.WinForms.Net8.csproj", "{424E07DB-C32E-4AAE-BE50-D839AF541041}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Library.Net48", "Tests\CrossFramework.Library.Net48\CrossFramework.Library.Net48.csproj", "{EF581C5C-5AAB-4A35-A2E4-1583CB4FDB9F}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Library.Net48", "Tests\CrossFramework\Library\Net48\CrossFramework.Library.Net48.csproj", "{EF581C5C-5AAB-4A35-A2E4-1583CB4FDB9F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Library.Net6", "Tests\CrossFramework.Library.Net6\CrossFramework.Library.Net6.csproj", "{0E65E38D-3A0D-42DB-ACD1-38096CE56092}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Library.Net6", "Tests\CrossFramework\Library\Net6\CrossFramework.Library.Net6.csproj", "{0E65E38D-3A0D-42DB-ACD1-38096CE56092}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WPF.Net6", "Tests\CrossFramework.WPF.Net6\CrossFramework.WPF.Net6.csproj", "{3942E3FD-06BC-470C-A1ED-BB18F2332B94}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WPF.Net6", "Tests\CrossFramework\WPF\Net6\CrossFramework.WPF.Net6.csproj", "{3942E3FD-06BC-470C-A1ED-BB18F2332B94}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WPF.Net8", "Tests\CrossFramework.WPF.Net8\CrossFramework.WPF.Net8.csproj", "{30DC9F52-E08A-4EF0-B041-04AED6135C3D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.WPF.Net8", "Tests\CrossFramework\WPF\Net8\CrossFramework.WPF.Net8.csproj", "{30DC9F52-E08A-4EF0-B041-04AED6135C3D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Library.Net10", "Tests\CrossFramework.Library.Net10\CrossFramework.Library.Net10.csproj", "{4458415A-0F5E-4136-B723-7A67955D6047}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrossFramework.Library.Net10", "Tests\CrossFramework\Library\Net10\CrossFramework.Library.Net10.csproj", "{4458415A-0F5E-4136-B723-7A67955D6047}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Confuser.Analyzers", "Confuser.Analyzers\Confuser.Analyzers.csproj", "{886E5FF3-DF94-4C6B-A5C3-E97038B5C275}" EndProject diff --git a/Tests/CrossFramework.Test/CrossFramework.Test.csproj b/Tests/CrossFramework.Test/CrossFramework.Test.csproj deleted file mode 100644 index 8ce559c21..000000000 --- a/Tests/CrossFramework.Test/CrossFramework.Test.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - net10.0-windows - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Tests/CrossFramework.Console.Net10/CrossFramework.Console.Net10.csproj b/Tests/CrossFramework/Console/Net10/CrossFramework.Console.Net10.csproj similarity index 100% rename from Tests/CrossFramework.Console.Net10/CrossFramework.Console.Net10.csproj rename to Tests/CrossFramework/Console/Net10/CrossFramework.Console.Net10.csproj diff --git a/Tests/CrossFramework.Console.Net10/Program.cs b/Tests/CrossFramework/Console/Net10/Program.cs similarity index 100% rename from Tests/CrossFramework.Console.Net10/Program.cs rename to Tests/CrossFramework/Console/Net10/Program.cs diff --git a/Tests/CrossFramework.Console.Net10/Resource.txt b/Tests/CrossFramework/Console/Net10/Resource.txt similarity index 100% rename from Tests/CrossFramework.Console.Net10/Resource.txt rename to Tests/CrossFramework/Console/Net10/Resource.txt diff --git a/Tests/CrossFramework.Console.Net20/CrossFramework.Console.Net20.csproj b/Tests/CrossFramework/Console/Net20/CrossFramework.Console.Net20.csproj similarity index 100% rename from Tests/CrossFramework.Console.Net20/CrossFramework.Console.Net20.csproj rename to Tests/CrossFramework/Console/Net20/CrossFramework.Console.Net20.csproj diff --git a/Tests/CrossFramework.Console.Net20/Program.cs b/Tests/CrossFramework/Console/Net20/Program.cs similarity index 100% rename from Tests/CrossFramework.Console.Net20/Program.cs rename to Tests/CrossFramework/Console/Net20/Program.cs diff --git a/Tests/CrossFramework.Console.Net35/CrossFramework.Console.Net35.csproj b/Tests/CrossFramework/Console/Net35/CrossFramework.Console.Net35.csproj similarity index 100% rename from Tests/CrossFramework.Console.Net35/CrossFramework.Console.Net35.csproj rename to Tests/CrossFramework/Console/Net35/CrossFramework.Console.Net35.csproj diff --git a/Tests/CrossFramework.Console.Net35/Program.cs b/Tests/CrossFramework/Console/Net35/Program.cs similarity index 100% rename from Tests/CrossFramework.Console.Net35/Program.cs rename to Tests/CrossFramework/Console/Net35/Program.cs diff --git a/Tests/CrossFramework.Console.Net40/CrossFramework.Console.Net40.csproj b/Tests/CrossFramework/Console/Net40/CrossFramework.Console.Net40.csproj similarity index 100% rename from Tests/CrossFramework.Console.Net40/CrossFramework.Console.Net40.csproj rename to Tests/CrossFramework/Console/Net40/CrossFramework.Console.Net40.csproj diff --git a/Tests/CrossFramework.Console.Net40/Program.cs b/Tests/CrossFramework/Console/Net40/Program.cs similarity index 100% rename from Tests/CrossFramework.Console.Net40/Program.cs rename to Tests/CrossFramework/Console/Net40/Program.cs diff --git a/Tests/CrossFramework.Console.Net48/CrossFramework.Console.Net48.csproj b/Tests/CrossFramework/Console/Net48/CrossFramework.Console.Net48.csproj similarity index 100% rename from Tests/CrossFramework.Console.Net48/CrossFramework.Console.Net48.csproj rename to Tests/CrossFramework/Console/Net48/CrossFramework.Console.Net48.csproj diff --git a/Tests/CrossFramework.Console.Net48/Program.cs b/Tests/CrossFramework/Console/Net48/Program.cs similarity index 100% rename from Tests/CrossFramework.Console.Net48/Program.cs rename to Tests/CrossFramework/Console/Net48/Program.cs diff --git a/Tests/CrossFramework.Console.Net6/CrossFramework.Console.Net6.csproj b/Tests/CrossFramework/Console/Net6/CrossFramework.Console.Net6.csproj similarity index 71% rename from Tests/CrossFramework.Console.Net6/CrossFramework.Console.Net6.csproj rename to Tests/CrossFramework/Console/Net6/CrossFramework.Console.Net6.csproj index 85048abf7..01d184acc 100644 --- a/Tests/CrossFramework.Console.Net6/CrossFramework.Console.Net6.csproj +++ b/Tests/CrossFramework/Console/Net6/CrossFramework.Console.Net6.csproj @@ -5,6 +5,6 @@ disable - + diff --git a/Tests/CrossFramework.Console.Net6/Program.cs b/Tests/CrossFramework/Console/Net6/Program.cs similarity index 100% rename from Tests/CrossFramework.Console.Net6/Program.cs rename to Tests/CrossFramework/Console/Net6/Program.cs diff --git a/Tests/CrossFramework.Console.Net8/CrossFramework.Console.Net8.csproj b/Tests/CrossFramework/Console/Net8/CrossFramework.Console.Net8.csproj similarity index 71% rename from Tests/CrossFramework.Console.Net8/CrossFramework.Console.Net8.csproj rename to Tests/CrossFramework/Console/Net8/CrossFramework.Console.Net8.csproj index fdb01141b..4a7a33a58 100644 --- a/Tests/CrossFramework.Console.Net8/CrossFramework.Console.Net8.csproj +++ b/Tests/CrossFramework/Console/Net8/CrossFramework.Console.Net8.csproj @@ -5,6 +5,6 @@ disable - + diff --git a/Tests/CrossFramework.Console.Net8/Program.cs b/Tests/CrossFramework/Console/Net8/Program.cs similarity index 100% rename from Tests/CrossFramework.Console.Net8/Program.cs rename to Tests/CrossFramework/Console/Net8/Program.cs diff --git a/Tests/CrossFramework.Library.Net10/CrossFramework.Library.Net10.csproj b/Tests/CrossFramework/Library/Net10/CrossFramework.Library.Net10.csproj similarity index 100% rename from Tests/CrossFramework.Library.Net10/CrossFramework.Library.Net10.csproj rename to Tests/CrossFramework/Library/Net10/CrossFramework.Library.Net10.csproj diff --git a/Tests/CrossFramework.Library.Net10/SampleService.cs b/Tests/CrossFramework/Library/Net10/SampleService.cs similarity index 100% rename from Tests/CrossFramework.Library.Net10/SampleService.cs rename to Tests/CrossFramework/Library/Net10/SampleService.cs diff --git a/Tests/CrossFramework.Library.Net48/CrossFramework.Library.Net48.csproj b/Tests/CrossFramework/Library/Net48/CrossFramework.Library.Net48.csproj similarity index 100% rename from Tests/CrossFramework.Library.Net48/CrossFramework.Library.Net48.csproj rename to Tests/CrossFramework/Library/Net48/CrossFramework.Library.Net48.csproj diff --git a/Tests/CrossFramework.Library.Net48/SampleService.cs b/Tests/CrossFramework/Library/Net48/SampleService.cs similarity index 100% rename from Tests/CrossFramework.Library.Net48/SampleService.cs rename to Tests/CrossFramework/Library/Net48/SampleService.cs diff --git a/Tests/CrossFramework.Library.Net6/CrossFramework.Library.Net6.csproj b/Tests/CrossFramework/Library/Net6/CrossFramework.Library.Net6.csproj similarity index 100% rename from Tests/CrossFramework.Library.Net6/CrossFramework.Library.Net6.csproj rename to Tests/CrossFramework/Library/Net6/CrossFramework.Library.Net6.csproj diff --git a/Tests/CrossFramework.Library.Net6/SampleService.cs b/Tests/CrossFramework/Library/Net6/SampleService.cs similarity index 100% rename from Tests/CrossFramework.Library.Net6/SampleService.cs rename to Tests/CrossFramework/Library/Net6/SampleService.cs diff --git a/Tests/CrossFramework.Library.Net8/CrossFramework.Library.Net8.csproj b/Tests/CrossFramework/Library/Net8/CrossFramework.Library.Net8.csproj similarity index 100% rename from Tests/CrossFramework.Library.Net8/CrossFramework.Library.Net8.csproj rename to Tests/CrossFramework/Library/Net8/CrossFramework.Library.Net8.csproj diff --git a/Tests/CrossFramework.Library.Net8/SampleService.cs b/Tests/CrossFramework/Library/Net8/SampleService.cs similarity index 100% rename from Tests/CrossFramework.Library.Net8/SampleService.cs rename to Tests/CrossFramework/Library/Net8/SampleService.cs diff --git a/Tests/CrossFramework.Library.NetStd20/CrossFramework.Library.NetStd20.csproj b/Tests/CrossFramework/Library/NetStd20/CrossFramework.Library.NetStd20.csproj similarity index 100% rename from Tests/CrossFramework.Library.NetStd20/CrossFramework.Library.NetStd20.csproj rename to Tests/CrossFramework/Library/NetStd20/CrossFramework.Library.NetStd20.csproj diff --git a/Tests/CrossFramework.Library.NetStd20/SampleService.cs b/Tests/CrossFramework/Library/NetStd20/SampleService.cs similarity index 100% rename from Tests/CrossFramework.Library.NetStd20/SampleService.cs rename to Tests/CrossFramework/Library/NetStd20/SampleService.cs diff --git a/Tests/CrossFramework.Test/ConsoleFrameworkTest.cs b/Tests/CrossFramework/Test/ConsoleFrameworkTest.cs similarity index 100% rename from Tests/CrossFramework.Test/ConsoleFrameworkTest.cs rename to Tests/CrossFramework/Test/ConsoleFrameworkTest.cs diff --git a/Tests/CrossFramework/Test/CrossFramework.Test.csproj b/Tests/CrossFramework/Test/CrossFramework.Test.csproj new file mode 100644 index 000000000..c95e7526b --- /dev/null +++ b/Tests/CrossFramework/Test/CrossFramework.Test.csproj @@ -0,0 +1,40 @@ + + + + net10.0-windows + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/CrossFramework.Test/LibraryFrameworkTest.cs b/Tests/CrossFramework/Test/LibraryFrameworkTest.cs similarity index 100% rename from Tests/CrossFramework.Test/LibraryFrameworkTest.cs rename to Tests/CrossFramework/Test/LibraryFrameworkTest.cs diff --git a/Tests/CrossFramework.Test/WinFormsFrameworkTest.cs b/Tests/CrossFramework/Test/WinFormsFrameworkTest.cs similarity index 100% rename from Tests/CrossFramework.Test/WinFormsFrameworkTest.cs rename to Tests/CrossFramework/Test/WinFormsFrameworkTest.cs diff --git a/Tests/CrossFramework.Test/WpfFrameworkTest.cs b/Tests/CrossFramework/Test/WpfFrameworkTest.cs similarity index 100% rename from Tests/CrossFramework.Test/WpfFrameworkTest.cs rename to Tests/CrossFramework/Test/WpfFrameworkTest.cs diff --git a/Tests/CrossFramework.WPF.Net10/App.xaml b/Tests/CrossFramework/WPF/Net10/App.xaml similarity index 100% rename from Tests/CrossFramework.WPF.Net10/App.xaml rename to Tests/CrossFramework/WPF/Net10/App.xaml diff --git a/Tests/CrossFramework.WPF.Net10/App.xaml.cs b/Tests/CrossFramework/WPF/Net10/App.xaml.cs similarity index 100% rename from Tests/CrossFramework.WPF.Net10/App.xaml.cs rename to Tests/CrossFramework/WPF/Net10/App.xaml.cs diff --git a/Tests/CrossFramework.WPF.Net10/CrossFramework.WPF.Net10.csproj b/Tests/CrossFramework/WPF/Net10/CrossFramework.WPF.Net10.csproj similarity index 75% rename from Tests/CrossFramework.WPF.Net10/CrossFramework.WPF.Net10.csproj rename to Tests/CrossFramework/WPF/Net10/CrossFramework.WPF.Net10.csproj index a6e9fd78b..222cf2b30 100644 --- a/Tests/CrossFramework.WPF.Net10/CrossFramework.WPF.Net10.csproj +++ b/Tests/CrossFramework/WPF/Net10/CrossFramework.WPF.Net10.csproj @@ -6,6 +6,6 @@ disable - + diff --git a/Tests/CrossFramework.WPF.Net10/MainWindow.xaml b/Tests/CrossFramework/WPF/Net10/MainWindow.xaml similarity index 100% rename from Tests/CrossFramework.WPF.Net10/MainWindow.xaml rename to Tests/CrossFramework/WPF/Net10/MainWindow.xaml diff --git a/Tests/CrossFramework.WPF.Net10/MainWindow.xaml.cs b/Tests/CrossFramework/WPF/Net10/MainWindow.xaml.cs similarity index 100% rename from Tests/CrossFramework.WPF.Net10/MainWindow.xaml.cs rename to Tests/CrossFramework/WPF/Net10/MainWindow.xaml.cs diff --git a/Tests/CrossFramework.WPF.Net35/CrossFramework.WPF.Net35.csproj b/Tests/CrossFramework/WPF/Net35/CrossFramework.WPF.Net35.csproj similarity index 85% rename from Tests/CrossFramework.WPF.Net35/CrossFramework.WPF.Net35.csproj rename to Tests/CrossFramework/WPF/Net35/CrossFramework.WPF.Net35.csproj index 64c9caf68..30f81f7c1 100644 --- a/Tests/CrossFramework.WPF.Net35/CrossFramework.WPF.Net35.csproj +++ b/Tests/CrossFramework/WPF/Net35/CrossFramework.WPF.Net35.csproj @@ -10,6 +10,6 @@ - + diff --git a/Tests/CrossFramework.WPF.Net35/Program.cs b/Tests/CrossFramework/WPF/Net35/Program.cs similarity index 100% rename from Tests/CrossFramework.WPF.Net35/Program.cs rename to Tests/CrossFramework/WPF/Net35/Program.cs diff --git a/Tests/CrossFramework.WPF.Net40/CrossFramework.WPF.Net40.csproj b/Tests/CrossFramework/WPF/Net40/CrossFramework.WPF.Net40.csproj similarity index 85% rename from Tests/CrossFramework.WPF.Net40/CrossFramework.WPF.Net40.csproj rename to Tests/CrossFramework/WPF/Net40/CrossFramework.WPF.Net40.csproj index 4c6ba01a3..6079b9223 100644 --- a/Tests/CrossFramework.WPF.Net40/CrossFramework.WPF.Net40.csproj +++ b/Tests/CrossFramework/WPF/Net40/CrossFramework.WPF.Net40.csproj @@ -10,6 +10,6 @@ - + diff --git a/Tests/CrossFramework.WPF.Net40/Program.cs b/Tests/CrossFramework/WPF/Net40/Program.cs similarity index 100% rename from Tests/CrossFramework.WPF.Net40/Program.cs rename to Tests/CrossFramework/WPF/Net40/Program.cs diff --git a/Tests/CrossFramework.WPF.Net48/App.xaml b/Tests/CrossFramework/WPF/Net48/App.xaml similarity index 100% rename from Tests/CrossFramework.WPF.Net48/App.xaml rename to Tests/CrossFramework/WPF/Net48/App.xaml diff --git a/Tests/CrossFramework.WPF.Net48/App.xaml.cs b/Tests/CrossFramework/WPF/Net48/App.xaml.cs similarity index 100% rename from Tests/CrossFramework.WPF.Net48/App.xaml.cs rename to Tests/CrossFramework/WPF/Net48/App.xaml.cs diff --git a/Tests/CrossFramework.WPF.Net48/CrossFramework.WPF.Net48.csproj b/Tests/CrossFramework/WPF/Net48/CrossFramework.WPF.Net48.csproj similarity index 74% rename from Tests/CrossFramework.WPF.Net48/CrossFramework.WPF.Net48.csproj rename to Tests/CrossFramework/WPF/Net48/CrossFramework.WPF.Net48.csproj index 56aa48505..dc35de7d8 100644 --- a/Tests/CrossFramework.WPF.Net48/CrossFramework.WPF.Net48.csproj +++ b/Tests/CrossFramework/WPF/Net48/CrossFramework.WPF.Net48.csproj @@ -6,6 +6,6 @@ 7.3 - + diff --git a/Tests/CrossFramework.WPF.Net48/MainWindow.xaml b/Tests/CrossFramework/WPF/Net48/MainWindow.xaml similarity index 100% rename from Tests/CrossFramework.WPF.Net48/MainWindow.xaml rename to Tests/CrossFramework/WPF/Net48/MainWindow.xaml diff --git a/Tests/CrossFramework.WPF.Net48/MainWindow.xaml.cs b/Tests/CrossFramework/WPF/Net48/MainWindow.xaml.cs similarity index 100% rename from Tests/CrossFramework.WPF.Net48/MainWindow.xaml.cs rename to Tests/CrossFramework/WPF/Net48/MainWindow.xaml.cs diff --git a/Tests/CrossFramework.WPF.Net6/App.xaml b/Tests/CrossFramework/WPF/Net6/App.xaml similarity index 100% rename from Tests/CrossFramework.WPF.Net6/App.xaml rename to Tests/CrossFramework/WPF/Net6/App.xaml diff --git a/Tests/CrossFramework.WPF.Net6/App.xaml.cs b/Tests/CrossFramework/WPF/Net6/App.xaml.cs similarity index 100% rename from Tests/CrossFramework.WPF.Net6/App.xaml.cs rename to Tests/CrossFramework/WPF/Net6/App.xaml.cs diff --git a/Tests/CrossFramework.WPF.Net6/CrossFramework.WPF.Net6.csproj b/Tests/CrossFramework/WPF/Net6/CrossFramework.WPF.Net6.csproj similarity index 75% rename from Tests/CrossFramework.WPF.Net6/CrossFramework.WPF.Net6.csproj rename to Tests/CrossFramework/WPF/Net6/CrossFramework.WPF.Net6.csproj index 28e790533..e098c3975 100644 --- a/Tests/CrossFramework.WPF.Net6/CrossFramework.WPF.Net6.csproj +++ b/Tests/CrossFramework/WPF/Net6/CrossFramework.WPF.Net6.csproj @@ -6,6 +6,6 @@ disable - + diff --git a/Tests/CrossFramework.WPF.Net6/MainWindow.xaml b/Tests/CrossFramework/WPF/Net6/MainWindow.xaml similarity index 100% rename from Tests/CrossFramework.WPF.Net6/MainWindow.xaml rename to Tests/CrossFramework/WPF/Net6/MainWindow.xaml diff --git a/Tests/CrossFramework.WPF.Net6/MainWindow.xaml.cs b/Tests/CrossFramework/WPF/Net6/MainWindow.xaml.cs similarity index 100% rename from Tests/CrossFramework.WPF.Net6/MainWindow.xaml.cs rename to Tests/CrossFramework/WPF/Net6/MainWindow.xaml.cs diff --git a/Tests/CrossFramework.WPF.Net8/App.xaml b/Tests/CrossFramework/WPF/Net8/App.xaml similarity index 100% rename from Tests/CrossFramework.WPF.Net8/App.xaml rename to Tests/CrossFramework/WPF/Net8/App.xaml diff --git a/Tests/CrossFramework.WPF.Net8/App.xaml.cs b/Tests/CrossFramework/WPF/Net8/App.xaml.cs similarity index 100% rename from Tests/CrossFramework.WPF.Net8/App.xaml.cs rename to Tests/CrossFramework/WPF/Net8/App.xaml.cs diff --git a/Tests/CrossFramework.WPF.Net8/CrossFramework.WPF.Net8.csproj b/Tests/CrossFramework/WPF/Net8/CrossFramework.WPF.Net8.csproj similarity index 75% rename from Tests/CrossFramework.WPF.Net8/CrossFramework.WPF.Net8.csproj rename to Tests/CrossFramework/WPF/Net8/CrossFramework.WPF.Net8.csproj index 791db65ff..5ec018f86 100644 --- a/Tests/CrossFramework.WPF.Net8/CrossFramework.WPF.Net8.csproj +++ b/Tests/CrossFramework/WPF/Net8/CrossFramework.WPF.Net8.csproj @@ -6,6 +6,6 @@ disable - + diff --git a/Tests/CrossFramework.WPF.Net8/MainWindow.xaml b/Tests/CrossFramework/WPF/Net8/MainWindow.xaml similarity index 100% rename from Tests/CrossFramework.WPF.Net8/MainWindow.xaml rename to Tests/CrossFramework/WPF/Net8/MainWindow.xaml diff --git a/Tests/CrossFramework.WPF.Net8/MainWindow.xaml.cs b/Tests/CrossFramework/WPF/Net8/MainWindow.xaml.cs similarity index 100% rename from Tests/CrossFramework.WPF.Net8/MainWindow.xaml.cs rename to Tests/CrossFramework/WPF/Net8/MainWindow.xaml.cs diff --git a/Tests/CrossFramework.WinForms.Net10/CrossFramework.WinForms.Net10.csproj b/Tests/CrossFramework/WinForms/Net10/CrossFramework.WinForms.Net10.csproj similarity index 75% rename from Tests/CrossFramework.WinForms.Net10/CrossFramework.WinForms.Net10.csproj rename to Tests/CrossFramework/WinForms/Net10/CrossFramework.WinForms.Net10.csproj index 3bac47f2e..181756823 100644 --- a/Tests/CrossFramework.WinForms.Net10/CrossFramework.WinForms.Net10.csproj +++ b/Tests/CrossFramework/WinForms/Net10/CrossFramework.WinForms.Net10.csproj @@ -6,6 +6,6 @@ disable - + diff --git a/Tests/CrossFramework.WinForms.Net10/MainForm.cs b/Tests/CrossFramework/WinForms/Net10/MainForm.cs similarity index 100% rename from Tests/CrossFramework.WinForms.Net10/MainForm.cs rename to Tests/CrossFramework/WinForms/Net10/MainForm.cs diff --git a/Tests/CrossFramework.WinForms.Net10/Program.cs b/Tests/CrossFramework/WinForms/Net10/Program.cs similarity index 100% rename from Tests/CrossFramework.WinForms.Net10/Program.cs rename to Tests/CrossFramework/WinForms/Net10/Program.cs diff --git a/Tests/CrossFramework.WinForms.Net35/CrossFramework.WinForms.Net35.csproj b/Tests/CrossFramework/WinForms/Net35/CrossFramework.WinForms.Net35.csproj similarity index 81% rename from Tests/CrossFramework.WinForms.Net35/CrossFramework.WinForms.Net35.csproj rename to Tests/CrossFramework/WinForms/Net35/CrossFramework.WinForms.Net35.csproj index 4b7fa9e07..a64e96ebe 100644 --- a/Tests/CrossFramework.WinForms.Net35/CrossFramework.WinForms.Net35.csproj +++ b/Tests/CrossFramework/WinForms/Net35/CrossFramework.WinForms.Net35.csproj @@ -8,6 +8,6 @@ - + diff --git a/Tests/CrossFramework.WinForms.Net35/MainForm.cs b/Tests/CrossFramework/WinForms/Net35/MainForm.cs similarity index 100% rename from Tests/CrossFramework.WinForms.Net35/MainForm.cs rename to Tests/CrossFramework/WinForms/Net35/MainForm.cs diff --git a/Tests/CrossFramework.WinForms.Net35/Program.cs b/Tests/CrossFramework/WinForms/Net35/Program.cs similarity index 100% rename from Tests/CrossFramework.WinForms.Net35/Program.cs rename to Tests/CrossFramework/WinForms/Net35/Program.cs diff --git a/Tests/CrossFramework.WinForms.Net40/CrossFramework.WinForms.Net40.csproj b/Tests/CrossFramework/WinForms/Net40/CrossFramework.WinForms.Net40.csproj similarity index 81% rename from Tests/CrossFramework.WinForms.Net40/CrossFramework.WinForms.Net40.csproj rename to Tests/CrossFramework/WinForms/Net40/CrossFramework.WinForms.Net40.csproj index fa63b342a..33b68ebb1 100644 --- a/Tests/CrossFramework.WinForms.Net40/CrossFramework.WinForms.Net40.csproj +++ b/Tests/CrossFramework/WinForms/Net40/CrossFramework.WinForms.Net40.csproj @@ -8,6 +8,6 @@ - + diff --git a/Tests/CrossFramework.WinForms.Net40/MainForm.cs b/Tests/CrossFramework/WinForms/Net40/MainForm.cs similarity index 100% rename from Tests/CrossFramework.WinForms.Net40/MainForm.cs rename to Tests/CrossFramework/WinForms/Net40/MainForm.cs diff --git a/Tests/CrossFramework.WinForms.Net40/Program.cs b/Tests/CrossFramework/WinForms/Net40/Program.cs similarity index 100% rename from Tests/CrossFramework.WinForms.Net40/Program.cs rename to Tests/CrossFramework/WinForms/Net40/Program.cs diff --git a/Tests/CrossFramework.WinForms.Net48/CrossFramework.WinForms.Net48.csproj b/Tests/CrossFramework/WinForms/Net48/CrossFramework.WinForms.Net48.csproj similarity index 73% rename from Tests/CrossFramework.WinForms.Net48/CrossFramework.WinForms.Net48.csproj rename to Tests/CrossFramework/WinForms/Net48/CrossFramework.WinForms.Net48.csproj index 7406e3c63..9770d2631 100644 --- a/Tests/CrossFramework.WinForms.Net48/CrossFramework.WinForms.Net48.csproj +++ b/Tests/CrossFramework/WinForms/Net48/CrossFramework.WinForms.Net48.csproj @@ -6,6 +6,6 @@ 7.3 - + diff --git a/Tests/CrossFramework.WinForms.Net48/MainForm.cs b/Tests/CrossFramework/WinForms/Net48/MainForm.cs similarity index 100% rename from Tests/CrossFramework.WinForms.Net48/MainForm.cs rename to Tests/CrossFramework/WinForms/Net48/MainForm.cs diff --git a/Tests/CrossFramework.WinForms.Net48/Program.cs b/Tests/CrossFramework/WinForms/Net48/Program.cs similarity index 100% rename from Tests/CrossFramework.WinForms.Net48/Program.cs rename to Tests/CrossFramework/WinForms/Net48/Program.cs diff --git a/Tests/CrossFramework.WinForms.Net6/CrossFramework.WinForms.Net6.csproj b/Tests/CrossFramework/WinForms/Net6/CrossFramework.WinForms.Net6.csproj similarity index 74% rename from Tests/CrossFramework.WinForms.Net6/CrossFramework.WinForms.Net6.csproj rename to Tests/CrossFramework/WinForms/Net6/CrossFramework.WinForms.Net6.csproj index 661aab39c..8e9275af0 100644 --- a/Tests/CrossFramework.WinForms.Net6/CrossFramework.WinForms.Net6.csproj +++ b/Tests/CrossFramework/WinForms/Net6/CrossFramework.WinForms.Net6.csproj @@ -6,6 +6,6 @@ disable - + diff --git a/Tests/CrossFramework.WinForms.Net6/MainForm.cs b/Tests/CrossFramework/WinForms/Net6/MainForm.cs similarity index 100% rename from Tests/CrossFramework.WinForms.Net6/MainForm.cs rename to Tests/CrossFramework/WinForms/Net6/MainForm.cs diff --git a/Tests/CrossFramework.WinForms.Net6/Program.cs b/Tests/CrossFramework/WinForms/Net6/Program.cs similarity index 100% rename from Tests/CrossFramework.WinForms.Net6/Program.cs rename to Tests/CrossFramework/WinForms/Net6/Program.cs diff --git a/Tests/CrossFramework.WinForms.Net8/CrossFramework.WinForms.Net8.csproj b/Tests/CrossFramework/WinForms/Net8/CrossFramework.WinForms.Net8.csproj similarity index 74% rename from Tests/CrossFramework.WinForms.Net8/CrossFramework.WinForms.Net8.csproj rename to Tests/CrossFramework/WinForms/Net8/CrossFramework.WinForms.Net8.csproj index 451dca704..36d5932dc 100644 --- a/Tests/CrossFramework.WinForms.Net8/CrossFramework.WinForms.Net8.csproj +++ b/Tests/CrossFramework/WinForms/Net8/CrossFramework.WinForms.Net8.csproj @@ -6,6 +6,6 @@ disable - + diff --git a/Tests/CrossFramework.WinForms.Net8/MainForm.cs b/Tests/CrossFramework/WinForms/Net8/MainForm.cs similarity index 100% rename from Tests/CrossFramework.WinForms.Net8/MainForm.cs rename to Tests/CrossFramework/WinForms/Net8/MainForm.cs diff --git a/Tests/CrossFramework.WinForms.Net8/Program.cs b/Tests/CrossFramework/WinForms/Net8/Program.cs similarity index 100% rename from Tests/CrossFramework.WinForms.Net8/Program.cs rename to Tests/CrossFramework/WinForms/Net8/Program.cs