diff --git a/CHANGELOG.md b/CHANGELOG.md index af7eff7..198b63b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,28 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.4.0] / 2024-09-05 +### Features +- Support `NUnit.Timeout` option to abort application. (default '10' minutes) (Fix: #51) +- Autoclose `VIEWER MODE` warning dialog box when Revit starts. (Fix: #52) +### Application +- Add `DialogBoxResolver` to cancel dialog box before revit initialization. +### Console +- Change `TimeoutMinutesDefault` to 10 minutes in the `RevitTestUtils`. +- Remove deprecated `wait` option and `RevitDebugUtils`. +- Add `Timeout` option to abort application. +- Fail all tests when `Timeout` happen. +- Update execution/timeout message in the log. +### Command +- Add `RevitTestProcessStart` to run tests with `ProcessStart`. +- Remove deprecated `wait` option. +- Add `Timeout` option to abort application. +### TestAdapter +- Add project `ricaun.RevitTest.Command` with `ricaun.NUnit` +- Remove references `CommandLineParser` and `NUnit` not used. +- Use `RevitTestProcessStartUtils` to start the process. +- Add `NUnit.Timeout` option to timeout the application. + ## [1.3.6] / 2024-08-30 ### TestAdapter - Remove log in the `LocalFileExists` in the `ApplicationUtils`. @@ -451,6 +473,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - [x] TestsFail [vNext]: ../../compare/1.0.0...HEAD +[1.4.0]: ../../compare/1.3.6...1.4.0 [1.3.6]: ../../compare/1.3.5...1.3.6 [1.3.5]: ../../compare/1.3.4...1.3.5 [1.3.4]: ../../compare/1.3.3...1.3.4 diff --git a/Directory.Build.props b/Directory.Build.props index 5d22c7b..7d5925a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,5 +1,5 @@ - 1.3.6 + 1.4.0 \ No newline at end of file diff --git a/README.old.md b/README.old.md index 5c78f2f..6782573 100644 --- a/README.old.md +++ b/README.old.md @@ -60,18 +60,19 @@ For more information see [Wiki](https://github.com/ricaun-io/ricaun.RevitTest/wi * [ricaun.Revit.UI.Tasks](https://github.com/ricaun-io/ricaun.Revit.UI.Tasks) * [ricaun.Revit.UI.Busy](https://github.com/ricaun-io/ricaun.Revit.UI.Busy) * [ricaun.RevitTest.Shared](ricaun.RevitTest.Shared) +#### Command +* [ricaun.NUnit](https://github.com/ricaun-io/ricaun.NUnit) #### Console * [ricaun.Revit.Installation](https://github.com/ricaun-io/ricaun.Revit.Installation) * [ricaun.RevitTest.Command](ricaun.RevitTest.Command) * [ricaun.RevitTest.Shared](ricaun.RevitTest.Shared) * [ricaun.RevitTest.Application.bundle.zip](ricaun.RevitTest.Application) -#### Command -* [ricaun.NUnit](https://github.com/ricaun-io/ricaun.NUnit) #### Shared * [ricaun.NUnit](https://github.com/ricaun-io/ricaun.NUnit) * [NamedPipeWrapper.Json](https://github.com/ricaun-io/named-pipe-wrapper-json) #### TestAdapter * [ricaun.RevitTest.Console.exe](ricaun.RevitTest.Console) +* [ricaun.RevitTest.Command](ricaun.RevitTest.Command) * [ricaun.Security.WinTrust](https://github.com/ricaun-io/ricaun.Security.WinTrust) ## CommandLine diff --git a/ricaun.RevitTest.Application/Revit/App.cs b/ricaun.RevitTest.Application/Revit/App.cs index 91752e6..c0ce3af 100644 --- a/ricaun.RevitTest.Application/Revit/App.cs +++ b/ricaun.RevitTest.Application/Revit/App.cs @@ -23,6 +23,7 @@ public class App : IExternalApplication private static PipeTestServer PipeTestServer; private static RevitTaskService RevitTask; private static RevitBusyService RevitBusyService; + private static DialogBoxResolver DialogBoxResolver; private static bool IsTestRunning = false; private const int TestThreadSleepMin = 50; @@ -38,6 +39,9 @@ public Result OnStartup(UIControlledApplication application) RevitTask = new RevitTaskService(application); RevitTask.Initialize(); + DialogBoxResolver = new DialogBoxResolver(application); + DialogBoxResolver.Initialize(); + Log.WriteLine(); Log.WriteLine($"{AppUtils.GetInfo()}"); @@ -219,9 +223,10 @@ public Result OnShutdown(UIControlledApplication application) }); ribbonPanel?.Remove(); + PipeTestServer?.Dispose(); RevitBusyService?.Dispose(); - + DialogBoxResolver?.Dispose(); RevitTask?.Dispose(); Log.Finish(); diff --git a/ricaun.RevitTest.Application/Revit/Utils/DialogBoxResolver.cs b/ricaun.RevitTest.Application/Revit/Utils/DialogBoxResolver.cs new file mode 100644 index 0000000..f69a430 --- /dev/null +++ b/ricaun.RevitTest.Application/Revit/Utils/DialogBoxResolver.cs @@ -0,0 +1,64 @@ +using Autodesk.Revit.UI; +using System; + +namespace ricaun.RevitTest.Application.Revit.Utils +{ + /// + /// DialogBoxResolver resolve the dialog box by DialogId and override the result to cancel. + /// + /// The resolver only works before Revit initialize. + public class DialogBoxResolver : IDisposable + { + private readonly UIControlledApplication application; + private readonly string[] TaskDialogIds; + public DialogBoxResolver(UIControlledApplication application) + { + this.application = application; + this.TaskDialogIds = new string[] + { + "TaskDialog_License_Current_Status_Demo_Mode", // Viewer Mode + "TaskDialog_DNSMTemplate" // Revit Warning + }; + } + + /// + /// Initialize the events to resolve the dialog box. + /// + /// The Initialize is ignored if the . + public virtual void Initialize() + { + if (this.application.IsLateAddinLoading) return; + Dispose(); + this.application.DialogBoxShowing += OnDialogBoxShowing; + this.application.ControlledApplication.ApplicationInitialized += OnApplicationInitialized; + } + + /// + /// Dispose the events. + /// + public virtual void Dispose() + { + this.application.DialogBoxShowing -= OnDialogBoxShowing; + this.application.ControlledApplication.ApplicationInitialized -= OnApplicationInitialized; + } + + private void OnDialogBoxShowing(object sender, Autodesk.Revit.UI.Events.DialogBoxShowingEventArgs e) + { + if (TaskDialogIds is null) return; + foreach (var dialogId in TaskDialogIds) + { + if (dialogId == e.DialogId) + { + Console.WriteLine($"{nameof(DialogBoxResolver)}: [{e.DialogId}]"); + e.OverrideResult((int)TaskDialogResult.Cancel); + return; + } + } + } + private void OnApplicationInitialized(object sender, Autodesk.Revit.DB.Events.ApplicationInitializedEventArgs e) + { + Dispose(); + } + } + +} \ No newline at end of file diff --git a/ricaun.RevitTest.Command/Command/IRunTestService.cs b/ricaun.RevitTest.Command/Command/IRunTestService.cs index b9ebdcc..9d7068b 100644 --- a/ricaun.RevitTest.Command/Command/IRunTestService.cs +++ b/ricaun.RevitTest.Command/Command/IRunTestService.cs @@ -11,8 +11,8 @@ public bool RunTests( Action actionOutput = null, string forceLanguageToRevit = null, bool forceToOpenNewRevit = false, - bool forceToWaitRevit = false, bool forceToCloseRevit = false, + int timeoutMinutes = 0, params string[] testFilters); } } \ No newline at end of file diff --git a/ricaun.RevitTest.Command/Command/Options.cs b/ricaun.RevitTest.Command/Command/Options.cs index 1326c87..0169dc5 100644 --- a/ricaun.RevitTest.Command/Command/Options.cs +++ b/ricaun.RevitTest.Command/Command/Options.cs @@ -54,16 +54,16 @@ private static Parser CreateParser() HelpText = "Force to open a new Revit process.")] public bool ForceToOpen { get; set; } - [Option("wait", - Default = false, - HelpText = "Force to wait after test done.")] - public bool ForceToWait { get; set; } - [Option("close", Default = false, HelpText = "Force to close the Revit process.")] public bool ForceToClose { get; set; } + [Option("timeout", + Default = 0, + HelpText = "Timeout in minutes to abort this application.")] + public int Timeout { get; set; } + [Option("debugger", Default = false, Hidden = true, diff --git a/ricaun.RevitTest.Command/Command/RunCommand.cs b/ricaun.RevitTest.Command/Command/RunCommand.cs index 401a71b..bd817da 100644 --- a/ricaun.RevitTest.Command/Command/RunCommand.cs +++ b/ricaun.RevitTest.Command/Command/RunCommand.cs @@ -1,4 +1,5 @@ -using ricaun.RevitTest.Command.Extensions; +using ricaun.NUnit; +using ricaun.RevitTest.Command.Extensions; using ricaun.RevitTest.Command.Utils; using System; using System.IO; @@ -30,7 +31,7 @@ private void ValidadeOptions() private void Initialize() { - NUnit.TestEngine.Initialize(out string init); + TestEngine.Initialize(out string init); Log.WriteLine(); Log.WriteLine($"{AppUtils.GetInfo()} [{init}]"); @@ -77,8 +78,8 @@ private bool RunOrReadTests() outputAction, options.RevitLanguage, options.ForceToOpen, - options.ForceToWait, options.ForceToClose, + options.Timeout, options.Test.ToArray()); return false; diff --git a/ricaun.RevitTest.TestAdapter/Services/EncodeParameterArgumentExtension.cs b/ricaun.RevitTest.Command/Extensions/EncodeParameterArgumentExtension.cs similarity index 98% rename from ricaun.RevitTest.TestAdapter/Services/EncodeParameterArgumentExtension.cs rename to ricaun.RevitTest.Command/Extensions/EncodeParameterArgumentExtension.cs index aefc4a2..6411425 100644 --- a/ricaun.RevitTest.TestAdapter/Services/EncodeParameterArgumentExtension.cs +++ b/ricaun.RevitTest.Command/Extensions/EncodeParameterArgumentExtension.cs @@ -2,7 +2,7 @@ using System.Text; using System.Text.RegularExpressions; -namespace ricaun.RevitTest.TestAdapter.Services +namespace ricaun.RevitTest.Command.Extensions { /// /// EncodeParameterArgumentExtension diff --git a/ricaun.RevitTest.TestAdapter/Services/ProcessStart.cs b/ricaun.RevitTest.Command/Process/ProcessStart.cs similarity index 69% rename from ricaun.RevitTest.TestAdapter/Services/ProcessStart.cs rename to ricaun.RevitTest.Command/Process/ProcessStart.cs index 8ec202c..6ce0847 100644 --- a/ricaun.RevitTest.TestAdapter/Services/ProcessStart.cs +++ b/ricaun.RevitTest.Command/Process/ProcessStart.cs @@ -4,11 +4,17 @@ using System.Diagnostics; using System.Linq; using System.Threading.Tasks; +using ricaun.RevitTest.Command.Extensions; -namespace ricaun.RevitTest.TestAdapter.Services +namespace ricaun.RevitTest.Command.Process { - internal class ProcessStart + public class ProcessStart { + protected virtual void WriteLine(string message) + { + Debug.WriteLine(message); + } + private const int DelayAfterExit = 100; private string processPath; private Dictionary argumentsPair = new Dictionary(); @@ -46,11 +52,11 @@ string ConvertValue(object value) arguments += $"{ConvertKey(item.Key)} "; arguments += $"{ConvertValue(item.Value)} "; } - AdapterLogger.Logger.Debug($"\tCreateArguments: {arguments}"); + WriteLine($"\tCreateArguments: {arguments}"); return arguments; } - public ProcessStart SetArgument(string name, object value = null) + protected ProcessStart SetArgument(string name, object value = null) { argumentsPair[name] = value; return this; @@ -76,53 +82,23 @@ private ProcessStartInfo NewProcessStartInfo(string arguments) CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, - //StandardOutputEncoding = System.Text.Encoding.UTF8, - //StandardErrorEncoding = System.Text.Encoding.UTF8, }; } - - public async Task Run() - { - var arguments = CreateArguments(); - Debug.WriteLine($"Run: {arguments}"); - return await Run(arguments); - } - public async Task Run(Action consoleAction, Action errorAction = null) { var arguments = CreateArguments(); - Debug.WriteLine($"Run: {arguments}"); - - AdapterLogger.Logger.DebugOnlyLocal($"ProcessStart: {processPath}"); - AdapterLogger.Logger.DebugOnlyLocal($"ProcessStart.Run: {arguments}"); - - foreach (var item in System.IO.Directory.GetFiles(System.IO.Path.GetDirectoryName(processPath))) - { - AdapterLogger.Logger.DebugOnlyLocal($"ProcessStart.File: {item}"); - } - + WriteLine($"ProcessStart: {processPath}"); + WriteLine($"ProcessStart.Run: {arguments}"); await Run(arguments, consoleAction, errorAction); } - - private async Task Run(string arguments) - { - if (string.IsNullOrEmpty(processPath)) return string.Empty; - var psi = NewProcessStartInfo(arguments); - var process = Process.Start(psi); - var output = await process.StandardOutput.ReadToEndAsync(); - //var error = await process.StandardError.ReadToEndAsync(); - process.WaitForExit(int.MaxValue); - return output; - } - private async Task Run(string arguments, Action consoleAction = null, Action errorAction = null) { if (string.IsNullOrEmpty(processPath)) return; var psi = NewProcessStartInfo(arguments); - var process = Process.Start(psi); + var process = System.Diagnostics.Process.Start(psi); process.OutputDataReceived += (sender, e) => { consoleAction?.Invoke(e.Data); diff --git a/ricaun.RevitTest.Command/Process/RevitTestProcessStart.cs b/ricaun.RevitTest.Command/Process/RevitTestProcessStart.cs new file mode 100644 index 0000000..aa1f653 --- /dev/null +++ b/ricaun.RevitTest.Command/Process/RevitTestProcessStart.cs @@ -0,0 +1,150 @@ +using ricaun.NUnit.Models; +using ricaun.RevitTest.Command.Extensions; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace ricaun.RevitTest.Command.Process +{ + public class RevitTestProcessStart : ProcessStart + { + public RevitTestProcessStart(string processPath) : base(processPath) + { + } + + private RevitTestProcessStart SetRevitArgument(string name, object value = null) + { + SetArgument(name, value); + return this; + } + public RevitTestProcessStart SetFile(string file) => SetRevitArgument("file", file); + public RevitTestProcessStart SetRead(bool read = true) + { + if (!read) return this; + return SetRevitArgument("read"); + } + public RevitTestProcessStart SetLog(bool log = true) + { + if (!log) return this; + return SetRevitArgument("log"); + } + public RevitTestProcessStart SetRevitVersion(int revitVersion) => SetRevitArgument("version", revitVersion); + public RevitTestProcessStart SetRevitLanguage(string revitLanguage) + { + if (string.IsNullOrWhiteSpace(revitLanguage) == false) + SetRevitArgument("language", revitLanguage); + + return this; + } + public RevitTestProcessStart SetOutput(string output) => SetRevitArgument("output", output); + public RevitTestProcessStart SetOutputConsole() => SetOutput("console"); + public RevitTestProcessStart SetOpen(bool open = true) + { + if (!open) return this; + return SetRevitArgument("open"); + } + public RevitTestProcessStart SetClose(bool close = true) + { + if (!close) return this; + return SetRevitArgument("close"); + } + public RevitTestProcessStart SetTimeout(int timeoutMinutes) => SetRevitArgument("timeout", timeoutMinutes); + public RevitTestProcessStart SetTestFilter(string[] testFilters) + { + if (testFilters.Length == 0) + return this; + return SetRevitArgument("test", testFilters); + } + public RevitTestProcessStart SetTestFilter(string testFilter) + { + if (string.IsNullOrEmpty(testFilter)) + return this; + return SetRevitArgument("test", testFilter); + } + public RevitTestProcessStart SetDebugger(bool debugger = true) + { + if (!debugger) return this; + return SetRevitArgument("debugger"); + } + + public Task RunExecuteTests(Action actionTest, + Action consoleAction = null, + Action debugAction = null, + Action errorAction = null) + { + bool testAssemblyEnabled = true; + Action outputConsole = (item) => + { + if (string.IsNullOrEmpty(item)) return; + + if (item.StartsWith("{\"FileName")) + { + debugAction?.Invoke($"OutputConsole: DEBUG: {item.Trim()}"); + + var testAssembly = item.Deserialize(); + + if (testAssemblyEnabled == false) return; + + consoleAction?.Invoke($"TestAssembly: {testAssembly}"); + foreach (var testModel in testAssembly.Tests.SelectMany(e => e.Tests)) + { + actionTest?.Invoke(testModel); + } + } + else if (item.StartsWith("{\"Name")) + { + debugAction?.Invoke($"OutputConsole: DEBUG: {item.Trim()}"); + + if (item.Deserialize() is TestModel testModel) + { + actionTest?.Invoke(testModel); + testAssemblyEnabled = false; + } + } + else if (item.StartsWith(" ")) + { + consoleAction?.Invoke($"OutputConsole: {item}"); + } + else + { + debugAction?.Invoke($"OutputConsole: DEBUG: {item}"); + } + }; + Action outputError = (item) => + { + if (string.IsNullOrEmpty(item)) return; + errorAction?.Invoke($"OutputConsole: ERROR: {item}"); + }; + return Run(outputConsole, outputError); + } + + public Task RunReadTests(Action actionTests, + Action consoleAction = null, + Action debugAction = null, + Action errorAction = null) + { + Action outputError = (item) => + { + if (string.IsNullOrEmpty(item)) return; + errorAction?.Invoke($"OutputConsole: ERROR: {item}"); + }; + + Action outputConsole = (item) => + { + if (string.IsNullOrEmpty(item)) return; + + debugAction?.Invoke($"OutputConsole: {item.Trim()}"); + + if (item.StartsWith("[")) + { + if (item.Deserialize() is string[] tests) + { + actionTests?.Invoke(tests); + debugAction?.Invoke($"OutputConsole: Deserialize {tests.Length} TestNames"); + } + } + }; + return Run(outputConsole, outputError); + } + } +} \ No newline at end of file diff --git a/ricaun.RevitTest.Command/Process/RevitTestProcessStartUtils.cs b/ricaun.RevitTest.Command/Process/RevitTestProcessStartUtils.cs new file mode 100644 index 0000000..eb0c208 --- /dev/null +++ b/ricaun.RevitTest.Command/Process/RevitTestProcessStartUtils.cs @@ -0,0 +1,80 @@ +using ricaun.NUnit.Models; +using System; +using System.Threading.Tasks; + +namespace ricaun.RevitTest.Command.Process +{ + public static class RevitTestProcessStartUtils + { + /// + /// Execute read Tests with output console and log. + /// + /// + /// + /// + /// + /// + /// + /// + public static async Task RunReadTests( + string applicationPath, + string file, + Action testResultAction, + Action consoleAction = null, + Action debugAction = null, + Action errorAction = null) + { + await new RevitTestProcessStart(applicationPath) + .SetFile(file) + .SetOutputConsole() + .SetRead() + .SetLog() + .RunReadTests(testResultAction, consoleAction, debugAction, errorAction); + } + + /// + /// Execute Tests with output console and log. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static async Task RunExecuteTests( + string applicationPath, + string file, + Action testResultAction, + int version = 0, + string language = null, + bool revitOpen = false, + bool revitClose = false, + int timeoutMinutes = 0, + bool debugger = false, + Action consoleAction = null, + Action debugAction = null, + Action errorAction = null, + params string[] filter) + { + await new RevitTestProcessStart(applicationPath) + .SetFile(file) + .SetRevitVersion(version) + .SetRevitLanguage(language) + .SetOutputConsole() + .SetOpen(revitOpen) + .SetClose(revitClose) + .SetTimeout(timeoutMinutes) + .SetLog() + .SetTestFilter(filter) + .SetDebugger(debugger) + .RunExecuteTests(testResultAction, consoleAction, debugAction, errorAction); + } + } +} \ No newline at end of file diff --git a/ricaun.RevitTest.Console/Program.cs b/ricaun.RevitTest.Console/Program.cs index 5d6835a..d9a6016 100644 --- a/ricaun.RevitTest.Console/Program.cs +++ b/ricaun.RevitTest.Console/Program.cs @@ -7,13 +7,6 @@ internal class Program static void Main(string[] args) { var result = RunTest.ParseArguments(args); - -#if DEBUG - if (!result) - { - Revit.Utils.RevitDebugUtils.ProcessServerSelect(); - } -#endif } } } \ No newline at end of file diff --git a/ricaun.RevitTest.Console/Resources/ricaun.RevitTest.Application.bundle.zip b/ricaun.RevitTest.Console/Resources/ricaun.RevitTest.Application.bundle.zip index 2e3fb64..66297c5 100644 Binary files a/ricaun.RevitTest.Console/Resources/ricaun.RevitTest.Application.bundle.zip and b/ricaun.RevitTest.Console/Resources/ricaun.RevitTest.Application.bundle.zip differ diff --git a/ricaun.RevitTest.Console/Revit/RevitRunTestService.cs b/ricaun.RevitTest.Console/Revit/RevitRunTestService.cs index 378f144..4e04cfd 100644 --- a/ricaun.RevitTest.Console/Revit/RevitRunTestService.cs +++ b/ricaun.RevitTest.Console/Revit/RevitRunTestService.cs @@ -16,13 +16,13 @@ public bool RunTests(string fileToTest, Action actionOutput = null, string forceLanguageToRevit = null, bool forceToOpenNewRevit = false, - bool forceToWaitRevit = false, bool forceToCloseRevit = false, + int timeoutMinutes = 0, params string[] testFilters) { RevitTestUtils.CreateRevitServer( fileToTest, revitVersionNumber, actionOutput, forceLanguageToRevit, - forceToOpenNewRevit, forceToWaitRevit, forceToCloseRevit, testFilters); + forceToOpenNewRevit, forceToCloseRevit, timeoutMinutes, testFilters); return true; } } diff --git a/ricaun.RevitTest.Console/Revit/Utils/RevitDebugUtils.cs b/ricaun.RevitTest.Console/Revit/Utils/RevitDebugUtils.cs deleted file mode 100644 index 416b6a2..0000000 --- a/ricaun.RevitTest.Console/Revit/Utils/RevitDebugUtils.cs +++ /dev/null @@ -1,109 +0,0 @@ -using ricaun.Revit.Installation; -using ricaun.RevitTest.Command; -using ricaun.RevitTest.Command.Utils; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace ricaun.RevitTest.Console.Revit.Utils -{ - public static class RevitDebugUtils - { - public static class App - { - public static string FilePath = ""; - public static string DefaultFilePath = @"D:\Users\ricau\source\repos\RevitTest0\RevitTest0\bin\Debug\RevitTest0.dll"; - } - - internal static void ProcessServerSelect() - { - Task.Run(RevitProcessServerSelectAsync).GetAwaiter().GetResult(); - } - - static async Task RevitProcessServerSelectAsync() - { - await Task.Delay(0); - - Log.WriteLine(); - Log.WriteLine($"{AppUtils.GetInfo()}"); - - Log.WriteLine(); - Log.WriteLine($"DebuggerUtils: {DebuggerUtils.IsDebuggerAttached}"); - - if (DebuggerUtils.IsDebuggerAttached) - { - Log.WriteLine($"DebuggerUtils: {VisualStudioDebugUtils.GetName()}"); - } - - var fileToTest = App.FilePath; - - if (string.IsNullOrEmpty(fileToTest)) - { - Log.WriteLine(); - Log.Write("FileToTest: "); - var line = System.Console.ReadLine(); - if (string.IsNullOrEmpty(line)) - { - fileToTest = App.DefaultFilePath; - Log.WriteLine(Path.GetFileName(fileToTest)); - } - else - { - fileToTest = line; - } - - if (!File.Exists(fileToTest)) - { - return; - } - } - - //foreach (var test in RevitTestUtils.GetTestFullNames(fileToTest)) - //{ - // Log.WriteLine(test); - //} - - var installedRevits = RevitInstallationUtils.InstalledRevit; - ConsoleKeyInfo keyLoop; - do - { - Log.WriteLine(); - for (int i = 0; i < installedRevits.Length; i++) - { - Log.WriteLine($"[NumPad{i + 1}] {installedRevits[i]}"); - } - Log.WriteLine(); - keyLoop = System.Console.ReadKey(true); - - var revitVersionNumber = 0; - - var number = keyLoop.Key - ConsoleKey.NumPad1; - if (keyLoop.Key == ConsoleKey.Spacebar) - { - - if (!RevitUtils.TryGetRevitVersion(fileToTest, out revitVersionNumber)) - { - break; - } - - Log.WriteLine($"TestFile {Path.GetFileName(fileToTest)} | Revit {revitVersionNumber}"); - } - else - { - if (number < 0) break; - if (number >= installedRevits.Length) break; - - revitVersionNumber = installedRevits[number].Version; - } - - RevitTestUtils.CreateRevitServer(fileToTest, revitVersionNumber, null, null, false, true, false); - - } while (keyLoop.Key != ConsoleKey.Escape); - - Log.WriteLine("..."); - - Thread.Sleep(1000); - } - } -} diff --git a/ricaun.RevitTest.Console/Revit/Utils/RevitTestUtils.cs b/ricaun.RevitTest.Console/Revit/Utils/RevitTestUtils.cs index a22c7ee..e2611aa 100644 --- a/ricaun.RevitTest.Console/Revit/Utils/RevitTestUtils.cs +++ b/ricaun.RevitTest.Console/Revit/Utils/RevitTestUtils.cs @@ -25,6 +25,8 @@ public static class RevitTestUtils private const int SleepMillisecondsBeforeFinish = 1000; private const int SleepMillisecondsDebuggerAttached = 1000; + private const int TimeoutMinutesDefault = 10; + /// /// Get Test Full Names using RevitInstallation if needed (Revit +2021) /// @@ -92,20 +94,25 @@ private static void LoggerTest(object logger) /// /// /// + /// /// - /// /// + /// + /// public static void CreateRevitServer( string fileToTest, int revitVersionNumber, Action actionOutput = null, string forceLanguageToRevit = null, bool forceToOpenNewRevit = false, - bool forceToWaitRevit = false, bool forceToCloseRevit = false, + int timeoutMinutes = 0, params string[] testFilters) { - int timeoutCountMax = forceToWaitRevit ? 0 : 10; + int timeoutNotBusyCountMax = 10; + + if (timeoutMinutes <= 0) + timeoutMinutes = TimeoutMinutesDefault; if (revitVersionNumber == 0) { @@ -123,9 +130,10 @@ public static void CreateRevitServer( return; } - int timeoutCount = 0; + int timeoutNotBusyCount = 0; bool sendFileWhenCreatedOrUpdated = true; bool testsFinishedForceToEnd = false; + bool timeoutForceToEnd = false; Action resetSendFile = (file, exists) => { @@ -197,13 +205,14 @@ public static void CreateRevitServer( } }; - for (int i = 0; i < 10 * 60; i++) + var timeoutSeconds = timeoutMinutes * 60; + for (int i = 0; i <= timeoutSeconds; i++) { Thread.Sleep(1000); if (i % 30 == 0 && i > 0) { - Log.WriteLine($"{revitInstallation}: Wait {i}s"); + Log.WriteLine($"{revitInstallation}: Execution time is {i / 60.0} minutes, maximum {timeoutMinutes} minutes."); } if (process.HasExited) break; @@ -212,13 +221,38 @@ public static void CreateRevitServer( continue; if (client.ServerMessage.IsBusy) - timeoutCount = 0; + timeoutNotBusyCount = 0; else - timeoutCount++; + timeoutNotBusyCount++; + + if (timeoutNotBusyCountMax > 0 && timeoutNotBusyCount > timeoutNotBusyCountMax) + { + var timeoutMessage = $"RevitTest: Timeout not busy for too long {timeoutNotBusyCountMax} seconds."; + Log.WriteLine($"{revitInstallation}: {timeoutMessage}"); + var exceptionTimeoutTests = new Exception(timeoutMessage); + var timeoutTests = TestEngine.Fail(fileToTest, exceptionTimeoutTests, testFilters); + foreach (var testModel in timeoutTests.Tests.SelectMany(e => e.Tests)) + { + actionOutput.Invoke(testModel.ToJson()); + } + Thread.Sleep(SleepMillisecondsBeforeFinish); + break; + } + + if (i == timeoutSeconds) + timeoutForceToEnd = true; - if (timeoutCountMax > 0 && timeoutCount > timeoutCountMax) + if (timeoutForceToEnd) { - Log.WriteLine($"{revitInstallation}: Timeout"); + var timeoutMessage = $"RevitTest: Timeout {timeoutMinutes} minutes."; + Log.WriteLine($"{revitInstallation}: {timeoutMessage}"); + var exceptionTimeoutTests = new Exception(timeoutMessage); + var timeoutTests = TestEngine.Fail(fileToTest, exceptionTimeoutTests, testFilters); + foreach (var testModel in timeoutTests.Tests.SelectMany(e => e.Tests)) + { + actionOutput?.Invoke(testModel.ToJson()); + } + Thread.Sleep(SleepMillisecondsBeforeFinish); break; } @@ -231,16 +265,6 @@ public static void CreateRevitServer( break; } - //if (System.Console.KeyAvailable) - //{ - // var cki = System.Console.ReadKey(true); - // if (cki.Key == ConsoleKey.Escape) break; - // if (cki.Key == ConsoleKey.Spacebar) - // { - // sendFileWhenCreatedOrUpdated = true; - // } - //} - if (sendFileWhenCreatedOrUpdated) { i = 0; diff --git a/ricaun.RevitTest.TestAdapter/Models/RunSettingsModel.cs b/ricaun.RevitTest.TestAdapter/Models/RunSettingsModel.cs index 611d6d9..6f88fa7 100644 --- a/ricaun.RevitTest.TestAdapter/Models/RunSettingsModel.cs +++ b/ricaun.RevitTest.TestAdapter/Models/RunSettingsModel.cs @@ -23,6 +23,9 @@ public class NUnitModel [XmlElement("Close")] public bool Close { get; set; } + [XmlElement("Timeout")] + public int Timeout { get; set; } + [XmlElement("Verbosity")] public int Verbosity { get; set; } diff --git a/ricaun.RevitTest.TestAdapter/Models/TestModel.cs b/ricaun.RevitTest.TestAdapter/Models/TestModel.cs deleted file mode 100644 index 2d03f19..0000000 --- a/ricaun.RevitTest.TestAdapter/Models/TestModel.cs +++ /dev/null @@ -1,155 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace ricaun.RevitTest.TestAdapter.Models -{ - /// - /// TestAssemblyModel - /// - internal class TestAssemblyModel : TestModel - { - /// - /// FileName - /// - public string FileName { get; set; } - /// - /// Version - /// - public string Version { get; set; } - /// - /// SuccessHate - /// - public double SuccessHate => GetSuccessHate(); - - /// - /// Tests - /// - public List Tests { get; set; } = new(); - - /// - /// Test Count - /// - public int TestCount => Tests.SelectMany(e => e.Tests).Count(); - - private double GetSuccessHate() - { - var tests = Tests.SelectMany(e => e.Tests); - var count = tests.Count(); - var success = Success ? 1.0 : 0; - return count == 0 ? success : Math.Round(tests.Where(e => e.Success).Count() / (double)count, 2); - } - - /// - /// ToString - /// - /// - public override string ToString() - { - return $"{Name} {TestCount} {Success} {Math.Round(SuccessHate * 100, 2)}% {Time}ms"; - } - - /// - /// Show in the Console - /// - public string AsString() - { - var text = $"{this}\n"; - foreach (var test in this.Tests) - { - text += $"\t{test}\n"; - foreach (var t in test.Tests) - { - text += $"\t\t{t}\n"; - } - } - return text; - } - } - - /// - /// TestTypeModel - /// - internal class TestTypeModel : TestModel - { - /// - /// Tests - /// - public List Tests { get; set; } = new(); - - /// - /// Test Count - /// - public int TestCount => Tests.Count; - - /// - /// SuccessHate - /// - public double SuccessHate => GetSuccessHate(); - private double GetSuccessHate() - { - var count = Tests.Count; - var success = Success ? 1.0 : 0; - return count == 0 ? success : Math.Round(Tests.Where(e => e.Success).Count() / (double)count, 2); - } - } - - /// - /// Test Model - /// - internal class TestModel - { - /// - /// Test Name - /// - public string Name { get; set; } - - /// - /// Test Alias - /// - public string Alias { get; set; } - - /// - /// Test FullName - /// - public string FullName { get; set; } - - /// - /// Test Success? - /// - public bool Success { get; set; } = true; - - /// - /// Test Skipped - /// - public bool Skipped { get; set; } = false; - - /// - /// Message Output - /// - public string Message { get; set; } = ""; - - /// - /// Console Output - /// - public string Console { get; set; } = ""; - - /// - /// Time in milliseconds - /// - public double Time { get; set; } - - /// - /// ToString - /// - /// - public override string ToString() - { - var result = Skipped ? "Skipped" : Success ? "Passed" : "Failed"; - var name = string.IsNullOrEmpty(Alias) ? Name : Alias != Name ? $"{Alias}[{Name}]" : Alias; - var message = Message; - //var message = (!Message.Contains("Exception:")) ? Message : Message.Substring(0, Message.IndexOf("Exception:") + "Exception".Length); - return $"{name}\t {result}\t {message}".Replace("\n", " ").Replace("\r", " "); - } - } -} diff --git a/ricaun.RevitTest.TestAdapter/Resources/net48/ricaun.RevitTest.Console.zip b/ricaun.RevitTest.TestAdapter/Resources/net48/ricaun.RevitTest.Console.zip index 3c20398..c377fcc 100644 Binary files a/ricaun.RevitTest.TestAdapter/Resources/net48/ricaun.RevitTest.Console.zip and b/ricaun.RevitTest.TestAdapter/Resources/net48/ricaun.RevitTest.Console.zip differ diff --git a/ricaun.RevitTest.TestAdapter/Resources/net8.0-windows/ricaun.RevitTest.Console.zip b/ricaun.RevitTest.TestAdapter/Resources/net8.0-windows/ricaun.RevitTest.Console.zip index 06659d6..544424c 100644 Binary files a/ricaun.RevitTest.TestAdapter/Resources/net8.0-windows/ricaun.RevitTest.Console.zip and b/ricaun.RevitTest.TestAdapter/Resources/net8.0-windows/ricaun.RevitTest.Console.zip differ diff --git a/ricaun.RevitTest.TestAdapter/Services/RevitTestConsole.cs b/ricaun.RevitTest.TestAdapter/Services/RevitTestConsole.cs index 59008d5..86f85b1 100644 --- a/ricaun.RevitTest.TestAdapter/Services/RevitTestConsole.cs +++ b/ricaun.RevitTest.TestAdapter/Services/RevitTestConsole.cs @@ -1,9 +1,8 @@ -using ricaun.RevitTest.TestAdapter.Extensions; +using ricaun.NUnit.Models; using System; using System.Diagnostics; using System.IO; using System.Linq; -using System.Reflection; using System.Threading.Tasks; namespace ricaun.RevitTest.TestAdapter.Services @@ -103,52 +102,36 @@ public bool IsTrusted(out string message) return isTrust; } - public async Task RunTestAction( + public async Task RunExecuteTests( string file, + Action testResultAction, int version = 0, string language = null, bool revitOpen = false, bool revitClose = false, + int timeoutMinutes = 0, Action consoleAction = null, + Action debugAction = null, Action errorAction = null, params string[] filter) { - await new RevitTestProcessStart(applicationPath) - .SetFile(file) - .SetRevitVersion(version) - .SetRevitLanguage(language) - .SetOutputConsole() - .SetOpen(revitOpen) - .SetClose(revitClose) - .SetLog() - .SetTestFilter(filter) - .SetDebugger(System.Diagnostics.Debugger.IsAttached) - .Run(consoleAction, errorAction); + await ricaun.RevitTest.Command.Process.RevitTestProcessStartUtils.RunExecuteTests( + applicationPath, file, testResultAction, + version, language, revitOpen, revitClose, timeoutMinutes, + System.Diagnostics.Debugger.IsAttached, + consoleAction, debugAction, errorAction, filter); } - private async Task RunTestRead(string file) - { - var read = await new RevitTestProcessStart(applicationPath) - .SetFile(file) - .SetOutputConsole() - .SetRead() - .Run(); - - var testNames = read.Deserialize(); - return testNames; - } - - public async Task RunTestReadWithLog( + public async Task RunReadTests( string file, - Action consoleAction, + Action testResultAction, + Action consoleAction = null, + Action debugAction = null, Action errorAction = null) { - await new RevitTestProcessStart(applicationPath) - .SetFile(file) - .SetOutputConsole() - .SetRead() - .SetLog() - .Run(consoleAction, errorAction); + await ricaun.RevitTest.Command.Process.RevitTestProcessStartUtils.RunReadTests( + applicationPath, file, testResultAction, + consoleAction, debugAction, errorAction); } public void Dispose() diff --git a/ricaun.RevitTest.TestAdapter/Services/RevitTestProcessStart.cs b/ricaun.RevitTest.TestAdapter/Services/RevitTestProcessStart.cs deleted file mode 100644 index 2430072..0000000 --- a/ricaun.RevitTest.TestAdapter/Services/RevitTestProcessStart.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace ricaun.RevitTest.TestAdapter.Services -{ - internal class RevitTestProcessStart : ProcessStart - { - public RevitTestProcessStart(string processPath) : base(processPath) - { - } - - private RevitTestProcessStart SetRevitArgument(string name, object value = null) - { - SetArgument(name, value); - return this; - } - public RevitTestProcessStart SetFile(string file) => SetRevitArgument("file", file); - public RevitTestProcessStart SetRead(bool read = true) - { - if (!read) return this; - return SetRevitArgument("read"); - } - public RevitTestProcessStart SetLog(bool log = true) - { - if (!log) return this; - return SetRevitArgument("log"); - } - public RevitTestProcessStart SetRevitVersion(int revitVersion) => SetRevitArgument("version", revitVersion); - public RevitTestProcessStart SetRevitLanguage(string revitLanguage) - { - if (string.IsNullOrWhiteSpace(revitLanguage) == false) - SetRevitArgument("language", revitLanguage); - - return this; - } - public RevitTestProcessStart SetOutput(string output) => SetRevitArgument("output", output); - public RevitTestProcessStart SetOutputConsole() => SetOutput("console"); - public RevitTestProcessStart SetOpen(bool open = true) - { - if (!open) return this; - return SetRevitArgument("open"); - } - public RevitTestProcessStart SetClose(bool close = true) - { - if (!close) return this; - return SetRevitArgument("close"); - } - public RevitTestProcessStart SetTestFilter(string[] testFilters) - { - if (testFilters.Length == 0) - return this; - return SetRevitArgument("test", testFilters); - } - public RevitTestProcessStart SetTestFilter(string testFilter) - { - if (string.IsNullOrEmpty(testFilter)) - return this; - return SetRevitArgument("test", testFilter); - } - public RevitTestProcessStart SetDebugger(bool debugger = true) - { - if (!debugger) return this; - return SetRevitArgument("debugger"); - } - } - -} \ No newline at end of file diff --git a/ricaun.RevitTest.TestAdapter/TestDiscoverer.cs b/ricaun.RevitTest.TestAdapter/TestDiscoverer.cs index 2c213cd..aeaddb0 100644 --- a/ricaun.RevitTest.TestAdapter/TestDiscoverer.cs +++ b/ricaun.RevitTest.TestAdapter/TestDiscoverer.cs @@ -58,29 +58,11 @@ internal static List GetTests( } var testNames = new string[] { }; - Action outputConsole = (item) => - { - if (string.IsNullOrEmpty(item)) return; - - AdapterLogger.Logger.Debug($"OutputConsole: {item.Trim()}"); - - if (item.StartsWith("[")) - { - if (item.Deserialize() is string[] tests) - { - testNames = tests; - AdapterLogger.Logger.Debug($"OutputConsole: Deserialize {testNames.Length} TestNames"); - } - } - }; - - Action outputError = (item) => - { - if (string.IsNullOrEmpty(item)) return; - AdapterLogger.Logger.Warning($"OutputConsole: ERROR: {item}"); - }; - await revit.RunTestReadWithLog(source, outputConsole, outputError); + await revit.RunReadTests(source, (tests) => { testNames = tests; }, + AdapterLogger.Logger.Debug, + AdapterLogger.Logger.Debug, + AdapterLogger.Logger.Warning); AdapterLogger.Logger.Debug("DiscoverTests: -------------------------------"); AdapterLogger.Logger.Info($"DiscoverTests: {testNames.ToJson()}"); diff --git a/ricaun.RevitTest.TestAdapter/TestExecutor.cs b/ricaun.RevitTest.TestAdapter/TestExecutor.cs index 526371c..5dc83d1 100644 --- a/ricaun.RevitTest.TestAdapter/TestExecutor.cs +++ b/ricaun.RevitTest.TestAdapter/TestExecutor.cs @@ -1,8 +1,6 @@ using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; -using NUnit.Engine.Internal; -using ricaun.RevitTest.TestAdapter.Extensions; -using ricaun.RevitTest.TestAdapter.Models; +using ricaun.NUnit.Models; using ricaun.RevitTest.TestAdapter.Services; using System; using System.Collections.Generic; @@ -106,62 +104,17 @@ private async Task RunTests(IFrameworkHandle frameworkHandle, string source, Lis AdapterLogger.Logger.Debug($"\tTestFilter: {filter}"); } - bool testAssemblyEnabled = true; - Action outputConsole = (item) => - { - if (string.IsNullOrEmpty(item)) return; - - if (item.StartsWith("{\"FileName")) - { - if (IsLogDebug) - AdapterLogger.Logger.Debug($"OutputConsole: DEBUG: {item.Trim()}"); - - var testAssembly = item.Deserialize(); - - if (testAssemblyEnabled == false) return; - - AdapterLogger.Logger.Info($"TestAssembly: {testAssembly}"); - foreach (var testModel in testAssembly.Tests.SelectMany(e => e.Tests)) - { - RecordResultTestModel(frameworkHandle, source, tests, testModel); - } - } - else if (item.StartsWith("{\"Name")) - { - if (IsLogDebug) - AdapterLogger.Logger.Debug($"OutputConsole: DEBUG: {item.Trim()}"); - - if (item.Deserialize() is TestModel testModel) - { - RecordResultTestModel(frameworkHandle, source, tests, testModel); - testAssemblyEnabled = false; - } - } - else if (item.StartsWith(" ")) - { - AdapterLogger.Logger.Debug($"OutputConsole: {item}"); - } - else - { - if (IsLogDebug) - AdapterLogger.Logger.Debug($"OutputConsole: DEBUG: {item}"); - } - }; - - Action outputError = (item) => - { - if (string.IsNullOrEmpty(item)) return; - AdapterLogger.Logger.Warning($"OutputConsole: ERROR: {item}"); - }; - AdapterLogger.Logger.Info($"RunRevitTest: {source} [Version: {AdapterSettings.Settings.NUnit.Version}] [TestFilter: {filters.Length}]"); - await revit.RunTestAction(source, + await revit.RunExecuteTests(source, (testModel) => { RecordResultTestModel(frameworkHandle, source, tests, testModel); }, AdapterSettings.Settings.NUnit.Version, AdapterSettings.Settings.NUnit.Language, AdapterSettings.Settings.NUnit.Open, AdapterSettings.Settings.NUnit.Close, - outputConsole, outputError, filters); + AdapterSettings.Settings.NUnit.Timeout, + AdapterLogger.Logger.Debug, (message) => { if (IsLogDebug) AdapterLogger.Logger.Debug(message); }, AdapterLogger.Logger.Warning, + filters); + } AdapterLogger.Logger.Info("---------"); diff --git a/ricaun.RevitTest.TestAdapter/ricaun.RevitTest.TestAdapter.csproj b/ricaun.RevitTest.TestAdapter/ricaun.RevitTest.TestAdapter.csproj index aec90e0..595d39b 100644 --- a/ricaun.RevitTest.TestAdapter/ricaun.RevitTest.TestAdapter.csproj +++ b/ricaun.RevitTest.TestAdapter/ricaun.RevitTest.TestAdapter.csproj @@ -34,6 +34,12 @@ + + + + + + diff --git a/ricaun.RevitTest.Tests/TestsDebugger.cs b/ricaun.RevitTest.Tests/TestsDebugger.cs index ee58b97..93eaf85 100644 --- a/ricaun.RevitTest.Tests/TestsDebugger.cs +++ b/ricaun.RevitTest.Tests/TestsDebugger.cs @@ -20,7 +20,9 @@ //[assembly: AssemblyMetadata("NUnit.Application", "..\\..\\..\\..\\ricaun.RevitTest.Console\\bin\\Debug\\net48\\ricaun.RevitTest.Console.exe")] //#endif -[assembly: AssemblyMetadata("NUnit.Application", "..\\..\\..\\..\\ricaun.RevitTest.Console\\bin\\Debug\\ricaun.DA4R.NUnit.Console.zip")] +//[assembly: AssemblyMetadata("NUnit.Application", "..\\..\\..\\..\\ricaun.RevitTest.Console\\bin\\Debug\\ricaun.DA4R.NUnit.Console.zip")] + +[assembly: AssemblyMetadata("NUnit.Timeout", "5")] //[assembly: AssemblyMetadata("NUnit.Language", "ENU /hosted")] #endif