diff --git a/.gitignore b/.gitignore index 8e63755..65e1bbc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,9 @@ deploy artifacts .ionide .DS_Store -.fake \ No newline at end of file +.fake +.idea +Gauge.Dotnet.sln.DotSettings.user +_testdata/Sample/.gauge/ +_testdata/Sample/logs/ +_testdata/Sample/reports/ diff --git a/README.MD b/README.MD index 3e7d7ca..1e6a3f0 100644 --- a/README.MD +++ b/README.MD @@ -37,7 +37,7 @@ gauge run specs #### Install specific version ``` -gauge install dotnet --version 0.5.2 +gauge install dotnet --version 0.6.0 ``` #### Offline installation @@ -45,7 +45,7 @@ gauge install dotnet --version 0.5.2 Download the plugin from [Releases](https://github.com/getgauge/gauge-dotnet/releases) ``` -gauge install dotnet --file gauge-dotnet-0.5.2.zip +gauge install dotnet --file gauge-dotnet-0.6.0.zip ``` #### Build from Source diff --git a/_testdata/Sample/IntegrationTestSample.csproj b/_testdata/Sample/IntegrationTestSample.csproj index 5d0ca69..5c822ae 100644 --- a/_testdata/Sample/IntegrationTestSample.csproj +++ b/_testdata/Sample/IntegrationTestSample.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 diff --git a/_testdata/Sample/StepImplementation.cs b/_testdata/Sample/StepImplementation.cs index 8d14cd1..bd6bdea 100644 --- a/_testdata/Sample/StepImplementation.cs +++ b/_testdata/Sample/StepImplementation.cs @@ -30,6 +30,14 @@ public void SaySomething(string what, string who) GaugeMessages.WriteMessage("{0}, {1}!", what, who); } + [Step("Say to async")] + public async Task SaySomethingAsync(string what, string who) + { + Console.WriteLine("{0}, {1}!", what, who); + GaugeMessages.WriteMessage("{0}, {1}!", what, who); + await Task.Delay(100); + } + [Step("I throw an unserializable exception")] public void ThrowUnserializableException() { diff --git a/_testdata/Sample/gauge_bin/IntegrationTestSample.deps.json b/_testdata/Sample/gauge_bin/IntegrationTestSample.deps.json index 6d92b42..17b6981 100644 --- a/_testdata/Sample/gauge_bin/IntegrationTestSample.deps.json +++ b/_testdata/Sample/gauge_bin/IntegrationTestSample.deps.json @@ -1,11 +1,12 @@ { "runtimeTarget": { - "name": ".NETCoreApp,Version=v3.0", + "name": ".NETCoreApp,Version=v7.0/linux-x64", "signature": "" }, "compilationOptions": {}, "targets": { - ".NETCoreApp,Version=v3.0": { + ".NETCoreApp,Version=v7.0": {}, + ".NETCoreApp,Version=v7.0/linux-x64": { "IntegrationTestSample/1.0.0": { "dependencies": { "Gauge.CSharp.Lib": "0.7.6" diff --git a/_testdata/Sample/gauge_bin/IntegrationTestSample.dll b/_testdata/Sample/gauge_bin/IntegrationTestSample.dll index cb96ec3..e565ba3 100644 Binary files a/_testdata/Sample/gauge_bin/IntegrationTestSample.dll and b/_testdata/Sample/gauge_bin/IntegrationTestSample.dll differ diff --git a/_testdata/Sample/gauge_bin/IntegrationTestSample.pdb b/_testdata/Sample/gauge_bin/IntegrationTestSample.pdb index fa95a68..2680ea0 100644 Binary files a/_testdata/Sample/gauge_bin/IntegrationTestSample.pdb and b/_testdata/Sample/gauge_bin/IntegrationTestSample.pdb differ diff --git a/integration-test/ExecuteStepProcessorTests.cs b/integration-test/ExecuteStepProcessorTests.cs index 0009623..5a8367a 100644 --- a/integration-test/ExecuteStepProcessorTests.cs +++ b/integration-test/ExecuteStepProcessorTests.cs @@ -6,6 +6,7 @@ using System.Threading; +using System.Threading.Tasks; using Gauge.Dotnet.Models; using Gauge.Dotnet.Processors; using Gauge.Dotnet.Wrappers; @@ -18,7 +19,7 @@ namespace Gauge.Dotnet.IntegrationTests public class ExecuteStepProcessorTests : IntegrationTestsBase { [Test] - public void ShouldExecuteMethodFromRequest() + public async Task ShouldExecuteMethodFromRequest() { const string parameterizedStepText = "Step that takes a table {}"; const string stepText = "Step that takes a table "; @@ -64,7 +65,7 @@ public void ShouldExecuteMethodFromRequest() } } }; - var result = executeStepProcessor.Process(message); + var result = await executeStepProcessor.Process(message); var protoExecutionResult = result.ExecutionResult; ClassicAssert.IsNotNull(protoExecutionResult); @@ -72,7 +73,7 @@ public void ShouldExecuteMethodFromRequest() } [Test] - public void ShouldCaptureScreenshotOnFailure() + public async Task ShouldCaptureScreenshotOnFailure() { const string stepText = "I throw a serializable exception"; var reflectionWrapper = new ReflectionWrapper(); @@ -96,7 +97,7 @@ public void ShouldCaptureScreenshotOnFailure() ActualStepText = stepText }; - var result = executeStepProcessor.Process(message); + var result = await executeStepProcessor.Process(message); var protoExecutionResult = result.ExecutionResult; ClassicAssert.IsNotNull(protoExecutionResult); diff --git a/integration-test/ExecutionOrchestratorTests.cs b/integration-test/ExecutionOrchestratorTests.cs index d326bce..0c7b93d 100644 --- a/integration-test/ExecutionOrchestratorTests.cs +++ b/integration-test/ExecutionOrchestratorTests.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Gauge.CSharp.Lib; using Gauge.Dotnet.Models; using Gauge.Dotnet.Wrappers; @@ -19,7 +20,7 @@ namespace Gauge.Dotnet.IntegrationTests public class ExecutionOrchestratorTests : IntegrationTestsBase { [Test] - public void RecoverableIsTrueOnExceptionThrownWhenContinueOnFailure() + public async Task RecoverableIsTrueOnExceptionThrownWhenContinueOnFailure() { var reflectionWrapper = new ReflectionWrapper(); var activatorWrapper = new ActivatorWrapper(); @@ -33,13 +34,13 @@ public void RecoverableIsTrueOnExceptionThrownWhenContinueOnFailure() new StepExecutor(assemblyLoader, reflectionWrapper, classInstanceManager)); var gaugeMethod = assemblyLoader.GetStepRegistry() .MethodFor("I throw a serializable exception and continue"); - var executionResult = orchestrator.ExecuteStep(gaugeMethod); + var executionResult = await orchestrator.ExecuteStep(gaugeMethod); ClassicAssert.IsTrue(executionResult.Failed); ClassicAssert.IsTrue(executionResult.RecoverableError); } [Test] - public void ShouldCreateTableFromTargetType() + public async Task ShouldCreateTableFromTargetType() { var reflectionWrapper = new ReflectionWrapper(); var activatorWrapper = new ActivatorWrapper(); @@ -56,12 +57,12 @@ public void ShouldCreateTableFromTargetType() table.AddRow(new List { "foorow1", "barrow1" }); table.AddRow(new List { "foorow2", "barrow2" }); - var executionResult = orchestrator.ExecuteStep(gaugeMethod, SerializeTable(table)); + var executionResult = await orchestrator.ExecuteStep(gaugeMethod, SerializeTable(table)); ClassicAssert.False(executionResult.Failed); } [Test] - public void ShouldExecuteMethodAndReturnResult() + public async Task ShouldExecuteMethodAndReturnResult() { var reflectionWrapper = new ReflectionWrapper(); var activatorWrapper = new ActivatorWrapper(); @@ -76,13 +77,12 @@ public void ShouldExecuteMethodAndReturnResult() var gaugeMethod = assemblyLoader.GetStepRegistry() .MethodFor("A context step which gets executed before every scenario"); - var executionResult = orchestrator.ExecuteStep(gaugeMethod); + var executionResult = await orchestrator.ExecuteStep(gaugeMethod); ClassicAssert.False(executionResult.Failed); } - [Test] - public void ShouldGetPendingMessages() + public async Task ShouldGetPendingMessages() { var reflectionWrapper = new ReflectionWrapper(); var activatorWrapper = new ActivatorWrapper(); @@ -97,14 +97,36 @@ public void ShouldGetPendingMessages() var gaugeMethod = assemblyLoader.GetStepRegistry().MethodFor("Say {} to {}"); - var executionResult = executionOrchestrator.ExecuteStep(gaugeMethod, "hello", "world"); + var executionResult = await executionOrchestrator.ExecuteStep(gaugeMethod, "hello", "world"); ClassicAssert.False(executionResult.Failed); ClassicAssert.Contains("hello, world!", executionResult.Message); } [Test] - public void ShouldGetStacktraceForAggregateException() + public async Task ShouldExecuteAsyncStepImplementation() + { + var reflectionWrapper = new ReflectionWrapper(); + var activatorWrapper = new ActivatorWrapper(); + var path = new AssemblyLocater(new DirectoryWrapper()).GetTestAssembly(); + var assemblyLoader = new AssemblyLoader(path, new GaugeLoadContext(path), reflectionWrapper, activatorWrapper, new StepRegistry()); + var classInstanceManager = assemblyLoader.GetClassInstanceManager(); + var executionInfoMapper = new ExecutionInfoMapper(assemblyLoader, activatorWrapper); + var executionOrchestrator = new ExecutionOrchestrator(reflectionWrapper, assemblyLoader, + classInstanceManager, + new HookExecutor(assemblyLoader, reflectionWrapper, classInstanceManager, executionInfoMapper), + new StepExecutor(assemblyLoader, reflectionWrapper, classInstanceManager)); + + var gaugeMethod = assemblyLoader.GetStepRegistry().MethodFor("Say {} to {} async"); + + var executionResult = await executionOrchestrator.ExecuteStep(gaugeMethod, "hello", "async world"); + + Assert.That(executionResult.Failed, Is.False, executionResult.ErrorMessage); + StringAssert.Contains("hello, async world!", executionResult.Message.ToString()); + } + + [Test] + public async Task ShouldGetStacktraceForAggregateException() { var reflectionWrapper = new ReflectionWrapper(); var activatorWrapper = new ActivatorWrapper(); @@ -118,7 +140,7 @@ public void ShouldGetStacktraceForAggregateException() new StepExecutor(assemblyLoader, reflectionWrapper, classInstanceManager)); var gaugeMethod = assemblyLoader.GetStepRegistry().MethodFor("I throw an AggregateException"); - var executionResult = executionOrchestrator.ExecuteStep(gaugeMethod); + var executionResult = await executionOrchestrator.ExecuteStep(gaugeMethod); ClassicAssert.True(executionResult.Failed); ClassicAssert.True(executionResult.StackTrace.Contains("First Exception")); @@ -141,7 +163,7 @@ public void ShouldGetStepTextsForMethod() } [Test] - public void SuccessIsFalseOnSerializableExceptionThrown() + public async Task SuccessIsFalseOnSerializableExceptionThrown() { const string expectedMessage = "I am a custom serializable exception"; var reflectionWrapper = new ReflectionWrapper(); @@ -156,7 +178,7 @@ public void SuccessIsFalseOnSerializableExceptionThrown() new StepExecutor(assemblyLoader, reflectionWrapper, classInstanceManager)); var gaugeMethod = assemblyLoader.GetStepRegistry().MethodFor("I throw a serializable exception"); - var executionResult = executionOrchestrator.ExecuteStep(gaugeMethod); + var executionResult = await executionOrchestrator.ExecuteStep(gaugeMethod); ClassicAssert.True(executionResult.Failed); ClassicAssert.AreEqual(expectedMessage, executionResult.ErrorMessage); @@ -165,7 +187,7 @@ public void SuccessIsFalseOnSerializableExceptionThrown() } [Test] - public void SuccessIsFalseOnUnserializableExceptionThrown() + public async Task SuccessIsFalseOnUnserializableExceptionThrown() { const string expectedMessage = "I am a custom exception"; var reflectionWrapper = new ReflectionWrapper(); @@ -180,7 +202,7 @@ public void SuccessIsFalseOnUnserializableExceptionThrown() new StepExecutor(assemblyLoader, reflectionWrapper, classInstanceManager)); var gaugeMethod = assemblyLoader.GetStepRegistry().MethodFor("I throw an unserializable exception"); - var executionResult = executionOrchestrator.ExecuteStep(gaugeMethod); + var executionResult = await executionOrchestrator.ExecuteStep(gaugeMethod); ClassicAssert.True(executionResult.Failed); ClassicAssert.AreEqual(expectedMessage, executionResult.ErrorMessage); StringAssert.Contains("IntegrationTestSample.StepImplementation.ThrowUnserializableException", diff --git a/integration-test/ExternalReferenceTests.cs b/integration-test/ExternalReferenceTests.cs index d7df069..d20d5ab 100644 --- a/integration-test/ExternalReferenceTests.cs +++ b/integration-test/ExternalReferenceTests.cs @@ -7,6 +7,7 @@ using System; using System.Threading; +using System.Threading.Tasks; using Gauge.Dotnet.Models; using Gauge.Dotnet.Processors; using Gauge.Dotnet.Wrappers; @@ -43,7 +44,7 @@ public void ShouldGetStepsFromReference(string referenceType, string stepText, s [Test] [TestCase("ProjectReference", "Take Screenshot in reference Project", "ReferenceProject-IDoNotExist.png")] [TestCase("DllReference", "Take Screenshot in reference DLL", "ReferenceDll-IDoNotExist.png")] - public void ShouldRegisterScreenshotWriterFromReference(string referenceType, string stepText, string expected) + public async Task ShouldRegisterScreenshotWriterFromReference(string referenceType, string stepText, string expected) { Environment.SetEnvironmentVariable("GAUGE_PROJECT_ROOT", TestUtils.GetIntegrationTestSampleDirectory(referenceType)); var reflectionWrapper = new ReflectionWrapper(); @@ -66,7 +67,7 @@ public void ShouldRegisterScreenshotWriterFromReference(string referenceType, st ActualStepText = stepText }; - var result = executeStepProcessor.Process(message); + var result = await executeStepProcessor.Process(message); var protoExecutionResult = result.ExecutionResult; ClassicAssert.IsNotNull(protoExecutionResult); diff --git a/integration-test/ImplementCodeProcessorTests.cs b/integration-test/ImplementCodeProcessorTests.cs index 3d2b7a5..3a33018 100644 --- a/integration-test/ImplementCodeProcessorTests.cs +++ b/integration-test/ImplementCodeProcessorTests.cs @@ -77,7 +77,7 @@ public void ShouldProcessMessageForExistingClass() var result = processor.Process(message); ClassicAssert.AreEqual(1, result.TextDiffs.Count); StringAssert.Contains("Step Method", result.TextDiffs[0].Content); - ClassicAssert.AreEqual(107, result.TextDiffs[0].Span.Start); + ClassicAssert.AreEqual(115, result.TextDiffs[0].Span.Start); } [Test] diff --git a/src/ExecutableRunnerServiceHandler.cs b/src/ExecutableRunnerServiceHandler.cs index 830d519..5319ddd 100644 --- a/src/ExecutableRunnerServiceHandler.cs +++ b/src/ExecutableRunnerServiceHandler.cs @@ -38,54 +38,54 @@ public ExecutableRunnerServiceHandler(IActivatorWrapper activationWrapper, IRefl IAssemblyLoader assemblyLoader, IStaticLoader loader, ExecutorPool pool, IHostApplicationLifetime lifetime) : base(loader, pool, lifetime) { - this._activatorWrapper = activationWrapper; - this._reflectionWrapper = reflectionWrapper; - this._assemblyLoader = assemblyLoader; + _activatorWrapper = activationWrapper; + _reflectionWrapper = reflectionWrapper; + _assemblyLoader = assemblyLoader; _stepRegistry = assemblyLoader.GetStepRegistry(); InitializeExecutionMessageHandlers(); } public override Task InitializeSuiteDataStore(SuiteDataStoreInitRequest request, ServerCallContext context) { - return _pool.Execute(getStream(request.Stream), () => this.suiteDataStoreInitProcessor.Process()); + return _pool.Execute(GetStream(request.Stream), () => suiteDataStoreInitProcessor.Process()); } public override Task ExecuteStep(ExecuteStepRequest request, ServerCallContext context) { - return _pool.Execute(getStream(request.Stream), () => this.executeStepProcessor.Process(request)); + return _pool.Execute(GetStream(request.Stream), async () => await executeStepProcessor.Process(request)); } public override Task FinishExecution(ExecutionEndingRequest request, ServerCallContext context) { - return _pool.Execute(getStream(request.Stream), () => this.executionEndingProcessor.Process(request)); + return _pool.Execute(GetStream(request.Stream), async () => await executionEndingProcessor.Process(request)); } public override Task FinishScenarioExecution(ScenarioExecutionEndingRequest request, ServerCallContext context) { - return _pool.Execute(getStream(request.Stream), () => this.scenarioExecutionEndingProcessor.Process(request)); + return _pool.Execute(GetStream(request.Stream), async () => await scenarioExecutionEndingProcessor.Process(request)); } public override Task FinishSpecExecution(SpecExecutionEndingRequest request, ServerCallContext context) { - return _pool.Execute(getStream(request.Stream), () => this.specExecutionEndingProcessor.Process(request)); + return _pool.Execute(GetStream(request.Stream), async () => await specExecutionEndingProcessor.Process(request)); } public override Task FinishStepExecution(StepExecutionEndingRequest request, ServerCallContext context) { - return _pool.Execute(getStream(request.Stream), () => this.stepExecutionEndingProcessor.Process(request)); + return _pool.Execute(GetStream(request.Stream), async () => await stepExecutionEndingProcessor.Process(request)); } public override Task InitializeScenarioDataStore(ScenarioDataStoreInitRequest request, ServerCallContext context) { - return _pool.Execute(getStream(request.Stream), () => this.scenarioDataStoreInitProcessor.Process()); + return _pool.Execute(GetStream(request.Stream), () => scenarioDataStoreInitProcessor.Process()); } public override Task InitializeSpecDataStore(SpecDataStoreInitRequest request, ServerCallContext context) { try { - return _pool.Execute(getStream(request.Stream), () => this.specDataStoreInitProcessor.Process()); + return _pool.Execute(GetStream(request.Stream), () => specDataStoreInitProcessor.Process()); } - catch (System.Exception e) + catch (Exception e) { Console.WriteLine(e); Environment.Exit(1); @@ -95,58 +95,51 @@ public override Task InitializeSpecDataStore(SpecDataSt public override Task StartExecution(ExecutionStartingRequest request, ServerCallContext context) { - return _pool.Execute(getStream(request.Stream), () => this.executionStartingProcessor.Process(request)); + return _pool.Execute(GetStream(request.Stream), async () => await executionStartingProcessor.Process(request)); } public override Task StartScenarioExecution(ScenarioExecutionStartingRequest request, ServerCallContext context) { - return _pool.Execute(getStream(request.Stream), () => this.scenarioExecutionStartingProcessor.Process(request)); + return _pool.Execute(GetStream(request.Stream), async () => await scenarioExecutionStartingProcessor.Process(request)); } public override Task StartSpecExecution(SpecExecutionStartingRequest request, ServerCallContext context) { - return _pool.Execute(getStream(request.Stream), () => this.specExecutionStartingProcessor.Process(request)); + return _pool.Execute(GetStream(request.Stream), async () => await specExecutionStartingProcessor.Process(request)); } public override Task StartStepExecution(StepExecutionStartingRequest request, ServerCallContext context) { - return _pool.Execute(getStream(request.Stream), () => this.stepExecutionStartingProcessor.Process(request)); + return _pool.Execute(GetStream(request.Stream), async () => await stepExecutionStartingProcessor.Process(request)); } private void InitializeExecutionMessageHandlers() { - var tableFormatter = new TableFormatter(this._assemblyLoader, this._activatorWrapper); - var classInstanceManager = new ThreadLocal(() => - { - return this._assemblyLoader.GetClassInstanceManager(); - }); - var executionInfoMapper = new ExecutionInfoMapper(this._assemblyLoader, this._activatorWrapper); - var executionOrchestrator = new ExecutionOrchestrator(this._reflectionWrapper, this._assemblyLoader, + var tableFormatter = new TableFormatter(_assemblyLoader, _activatorWrapper); + var classInstanceManager = new ThreadLocal(() => _assemblyLoader.GetClassInstanceManager()); + var executionInfoMapper = new ExecutionInfoMapper(_assemblyLoader, _activatorWrapper); + var executionOrchestrator = new ExecutionOrchestrator(_reflectionWrapper, _assemblyLoader, classInstanceManager.Value, - new HookExecutor(this._assemblyLoader, this._reflectionWrapper, classInstanceManager.Value, executionInfoMapper), - new StepExecutor(this._assemblyLoader, this._reflectionWrapper, classInstanceManager.Value)); - - this.executionStartingProcessor = new ExecutionStartingProcessor(executionOrchestrator); - this.executionEndingProcessor = new ExecutionEndingProcessor(executionOrchestrator); - this.specExecutionStartingProcessor = new SpecExecutionStartingProcessor(executionOrchestrator); - this.specExecutionEndingProcessor = new SpecExecutionEndingProcessor(executionOrchestrator); - this.scenarioExecutionStartingProcessor = new ScenarioExecutionStartingProcessor(executionOrchestrator); - this.scenarioExecutionEndingProcessor = new ScenarioExecutionEndingProcessor(executionOrchestrator); - this.stepExecutionStartingProcessor = new StepExecutionStartingProcessor(executionOrchestrator); - this.stepExecutionEndingProcessor = new StepExecutionEndingProcessor(executionOrchestrator); - this.executeStepProcessor = new ExecuteStepProcessor(_stepRegistry, executionOrchestrator, tableFormatter); - this.scenarioDataStoreInitProcessor = new ScenarioDataStoreInitProcessor(this._assemblyLoader); - this.specDataStoreInitProcessor = new SpecDataStoreInitProcessor(this._assemblyLoader); - this.suiteDataStoreInitProcessor = new SuiteDataStoreInitProcessor(this._assemblyLoader); + new HookExecutor(_assemblyLoader, _reflectionWrapper, classInstanceManager.Value, executionInfoMapper), + new StepExecutor(_assemblyLoader, _reflectionWrapper, classInstanceManager.Value)); + + executionStartingProcessor = new ExecutionStartingProcessor(executionOrchestrator); + executionEndingProcessor = new ExecutionEndingProcessor(executionOrchestrator); + specExecutionStartingProcessor = new SpecExecutionStartingProcessor(executionOrchestrator); + specExecutionEndingProcessor = new SpecExecutionEndingProcessor(executionOrchestrator); + scenarioExecutionStartingProcessor = new ScenarioExecutionStartingProcessor(executionOrchestrator); + scenarioExecutionEndingProcessor = new ScenarioExecutionEndingProcessor(executionOrchestrator); + stepExecutionStartingProcessor = new StepExecutionStartingProcessor(executionOrchestrator); + stepExecutionEndingProcessor = new StepExecutionEndingProcessor(executionOrchestrator); + executeStepProcessor = new ExecuteStepProcessor(_stepRegistry, executionOrchestrator, tableFormatter); + scenarioDataStoreInitProcessor = new ScenarioDataStoreInitProcessor(_assemblyLoader); + specDataStoreInitProcessor = new SpecDataStoreInitProcessor(_assemblyLoader); + suiteDataStoreInitProcessor = new SuiteDataStoreInitProcessor(_assemblyLoader); } - private int getStream(int stream) + private int GetStream(int stream) { - if (!_pool.IsMultithreading) - { - return 1; - } - return Math.Max(stream, 1); + return _pool.IsMultithreading ? Math.Max(stream, 1) : 1; } } } \ No newline at end of file diff --git a/src/ExecutionOrchestrator.cs b/src/ExecutionOrchestrator.cs index f748ce6..f8f3123 100644 --- a/src/ExecutionOrchestrator.cs +++ b/src/ExecutionOrchestrator.cs @@ -9,6 +9,7 @@ using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using Gauge.CSharp.Core; using Gauge.Dotnet.Models; using Gauge.Dotnet.Strategy; @@ -38,11 +39,11 @@ public ExecutionOrchestrator(IReflectionWrapper reflectionWrapper, IAssemblyLoad [DebuggerHidden] - public ProtoExecutionResult ExecuteStep(GaugeMethod method, params string[] args) + public async Task ExecuteStep(GaugeMethod method, params string[] args) { var stopwatch = Stopwatch.StartNew(); - var executionResult = _stepExecutor.Execute(method, args); + var executionResult = await _stepExecutor.Execute(method, args); return BuildResult(stopwatch, executionResult); } @@ -79,11 +80,12 @@ public IEnumerable GetAllPendingScreenshotFiles() } [DebuggerHidden] - public ProtoExecutionResult ExecuteHooks(string hookType, HooksStrategy strategy, IList applicableTags, + public async Task ExecuteHooks(string hookType, HooksStrategy strategy, + IList applicableTags, ExecutionInfo info) { var stopwatch = Stopwatch.StartNew(); - var executionResult = _hookExecutor.Execute(hookType, strategy, applicableTags, info); + var executionResult = await _hookExecutor.Execute(hookType, strategy, applicableTags, info); return BuildResult(stopwatch, executionResult); } diff --git a/src/Executor/ExecutorPool.cs b/src/Executor/ExecutorPool.cs index df7751b..058ee66 100644 --- a/src/Executor/ExecutorPool.cs +++ b/src/Executor/ExecutorPool.cs @@ -14,16 +14,16 @@ namespace Gauge.Dotnet.Executor public class ExecutorPool : IDisposable { public bool IsMultithreading { get; internal set; } - private ConcurrentDictionary _workers = new ConcurrentDictionary(); - + private ConcurrentDictionary _workers = new ConcurrentDictionary(); + public ExecutorPool(int size, bool isMultithreading) { for (int i = 1; i <= size; i++) { - bool added = _workers.TryAdd(GetName(i), new CustomTaskScheduler()); + bool added = _workers.TryAdd(GetName(i), new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, new CustomTaskScheduler())); if (!added) { - Logger.Fatal("Failed to add Wroker for stream " + i); + Logger.Fatal("Failed to add Worker for stream " + i); } } @@ -34,16 +34,25 @@ public void Dispose() { foreach (var w in _workers) { - w.Value.Dispose(); + ((CustomTaskScheduler)w.Value.Scheduler)?.Dispose(); } } public Task Execute(int stream, Func fn) { - bool found = _workers.TryGetValue(GetName(stream), out CustomTaskScheduler scheduler); + bool found = _workers.TryGetValue(GetName(stream), out TaskFactory taskFactory); + if (found) + { + return taskFactory.StartNew(fn); + } + throw new StreamNotFountException(stream); + } + public Task Execute(int stream, Func> fn) + { + bool found = _workers.TryGetValue(GetName(stream), out TaskFactory taskFactory); if (found) { - return Task.Factory.StartNew(fn, new CancellationToken(), TaskCreationOptions.None, scheduler); + return taskFactory.StartNew(fn).Unwrap(); } throw new StreamNotFountException(stream); } diff --git a/src/Extensions/MethodInfoExtensions.cs b/src/Extensions/MethodInfoExtensions.cs index eebaebe..c83252d 100644 --- a/src/Extensions/MethodInfoExtensions.cs +++ b/src/Extensions/MethodInfoExtensions.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; namespace Gauge.Dotnet.Extensions { @@ -34,5 +35,10 @@ public static bool IsRecoverableStep(this MethodInfo info, IAssemblyLoader assem return customAttributes.Any(stepType.IsInstanceOfType) && customAttributes.Any(continueOnFailureType.IsInstanceOfType); } + + public static bool IsAsyncVoid(this MethodInfo info) + { + return info.GetCustomAttributes(typeof(AsyncStateMachineAttribute),true).Any() && info.ReturnType == typeof(void); + } } } \ No newline at end of file diff --git a/src/Gauge.Dotnet.csproj b/src/Gauge.Dotnet.csproj index 7fdfefd..990a3fe 100644 --- a/src/Gauge.Dotnet.csproj +++ b/src/Gauge.Dotnet.csproj @@ -5,7 +5,7 @@ net6.0;net7.0;net8.0 Runner.NetCore30 The Gauge Team - 0.5.3 + 0.6.0 ThoughtWorks Inc. Gauge C# runner for Gauge. https://gauge.org diff --git a/src/HookExecutor.cs b/src/HookExecutor.cs index b391ee0..b5aea9c 100644 --- a/src/HookExecutor.cs +++ b/src/HookExecutor.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using Gauge.Dotnet.Models; using Gauge.Dotnet.Strategy; using Gauge.Dotnet.Wrappers; @@ -31,7 +32,7 @@ public HookExecutor(IAssemblyLoader assemblyLoader, IReflectionWrapper reflectio _executionInfoMapper = mapper; } - public ExecutionResult Execute(string hookType, IHooksStrategy strategy, IList applicableTags, + public async Task Execute(string hookType, IHooksStrategy strategy, IList applicableTags, ExecutionInfo info) { var methods = GetHookMethods(hookType, strategy, applicableTags); @@ -45,7 +46,7 @@ public ExecutionResult Execute(string hookType, IHooksStrategy strategy, IList ExecuteStep(GaugeMethod method, params string[] args); - ProtoExecutionResult ExecuteHooks(string hookType, HooksStrategy strategy, IList applicableTags, + Task ExecuteHooks(string hookType, HooksStrategy strategy, IList applicableTags, ExecutionInfo context); void ClearCache(); diff --git a/src/IHookExecutor.cs b/src/IHookExecutor.cs index 90a7acf..0175188 100644 --- a/src/IHookExecutor.cs +++ b/src/IHookExecutor.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; +using System.Threading.Tasks; using Gauge.Messages; using Gauge.Dotnet.Models; using Gauge.Dotnet.Strategy; @@ -14,7 +15,7 @@ namespace Gauge.Dotnet { public interface IHookExecutor { - ExecutionResult Execute(string hookType, IHooksStrategy strategy, IList applicableTags, + Task Execute(string hookType, IHooksStrategy strategy, IList applicableTags, ExecutionInfo context); } } \ No newline at end of file diff --git a/src/IStepExecutor.cs b/src/IStepExecutor.cs index 334073c..cc704f4 100644 --- a/src/IStepExecutor.cs +++ b/src/IStepExecutor.cs @@ -5,12 +5,13 @@ *----------------------------------------------------------------*/ +using System.Threading.Tasks; using Gauge.Dotnet.Models; namespace Gauge.Dotnet { public interface IStepExecutor { - ExecutionResult Execute(GaugeMethod gaugeMethod, string[] args); + Task Execute(GaugeMethod gaugeMethod, string[] args); } } \ No newline at end of file diff --git a/src/MethodExecutor.cs b/src/MethodExecutor.cs index c74dc71..2d2a8c4 100644 --- a/src/MethodExecutor.cs +++ b/src/MethodExecutor.cs @@ -6,7 +6,10 @@ using System; +using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; using Gauge.Dotnet.Wrappers; namespace Gauge.Dotnet @@ -26,7 +29,7 @@ protected MethodExecutor(Type type, _classInstanceManager = classInstanceManager; } - protected void Execute(MethodInfo method, params object[] parameters) + protected Task Execute(MethodInfo method, params object[] parameters) { var typeToLoad = method.DeclaringType; var instance = @@ -38,7 +41,12 @@ protected void Execute(MethodInfo method, params object[] parameters) throw new TypeLoadException(error); } - _reflectionWrapper.Invoke(method, instance, parameters); + if (method.GetCustomAttributes(typeof(AsyncStateMachineAttribute), false).Any()) + { + return (Task)_reflectionWrapper.Invoke(method, instance, parameters); + } + + return Task.Run(() => _reflectionWrapper.Invoke(method, instance, parameters)); } } } \ No newline at end of file diff --git a/src/Models/GaugeMethod.cs b/src/Models/GaugeMethod.cs index c70a505..592cd52 100644 --- a/src/Models/GaugeMethod.cs +++ b/src/Models/GaugeMethod.cs @@ -26,6 +26,6 @@ public class GaugeMethod public FileLinePositionSpan Span { get; internal set; } public bool HasAlias { get; set; } public IEnumerable Aliases { get; set; } - public bool IsExternal {get; set;} + public bool IsExternal {get; set;} } } \ No newline at end of file diff --git a/src/Models/IStepRegistry.cs b/src/Models/IStepRegistry.cs index 804ae2b..96739a2 100644 --- a/src/Models/IStepRegistry.cs +++ b/src/Models/IStepRegistry.cs @@ -21,5 +21,6 @@ public interface IStepRegistry void RemoveSteps(string filepath); IEnumerable GetStepPositions(string filePath); bool IsFileCached(string file); + bool HasAsyncVoidImplementation(string parsedStepText); } } \ No newline at end of file diff --git a/src/Models/StepRegistry.cs b/src/Models/StepRegistry.cs index eca789b..b74f84b 100644 --- a/src/Models/StepRegistry.cs +++ b/src/Models/StepRegistry.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Gauge.Dotnet.Extensions; using Gauge.Messages; using static Gauge.Messages.StepPositionsResponse.Types; @@ -119,5 +120,10 @@ public bool IsFileCached(string file) } return false; } + + public bool HasAsyncVoidImplementation(string parsedStepText) + { + return !ContainsStep(parsedStepText) || _registry[parsedStepText].Any(x => x.MethodInfo.IsAsyncVoid()); + } } } \ No newline at end of file diff --git a/src/Processors/ExecuteStepProcessor.cs b/src/Processors/ExecuteStepProcessor.cs index e6b37eb..bc30d74 100644 --- a/src/Processors/ExecuteStepProcessor.cs +++ b/src/Processors/ExecuteStepProcessor.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Linq; +using System.Threading.Tasks; using Gauge.Dotnet.Models; using Gauge.Messages; @@ -27,7 +28,7 @@ public ExecuteStepProcessor(IStepRegistry registry, IExecutionOrchestrator execu } [DebuggerHidden] - public ExecutionStatusResponse Process(ExecuteStepRequest request) + public async Task Process(ExecuteStepRequest request) { if (!_stepRegistry.ContainsStep(request.ParsedStepText)) return ExecutionError("Step Implementation not found"); @@ -53,7 +54,7 @@ public ExecutionStatusResponse Process(ExecuteStepRequest request) args[i] = validTableParamTypes.Contains(stepParameter[i].ParameterType) ? _tableFormatter.GetJSON(stepParameter[i].Table) : stepParameter[i].Value; - var protoExecutionResult = _executionOrchestrator.ExecuteStep(method, args); + var protoExecutionResult = await _executionOrchestrator.ExecuteStep(method, args); return new ExecutionStatusResponse { ExecutionResult = protoExecutionResult }; } diff --git a/src/Processors/ExecutionEndingProcessor.cs b/src/Processors/ExecutionEndingProcessor.cs index c8a055d..a283e64 100644 --- a/src/Processors/ExecutionEndingProcessor.cs +++ b/src/Processors/ExecutionEndingProcessor.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading.Tasks; using Gauge.Messages; namespace Gauge.Dotnet.Processors @@ -20,9 +21,9 @@ public ExecutionEndingProcessor(IExecutionOrchestrator executionOrchestrator) } [DebuggerHidden] - public virtual ExecutionStatusResponse Process(ExecutionEndingRequest request) + public virtual async Task Process(ExecutionEndingRequest request) { - var result = ExecuteHooks(request.CurrentExecutionInfo); + var result = await ExecuteHooks(request.CurrentExecutionInfo); ClearCacheForConfiguredLevel(); return result; } diff --git a/src/Processors/ExecutionStartingProcessor.cs b/src/Processors/ExecutionStartingProcessor.cs index 82650e8..d57d417 100644 --- a/src/Processors/ExecutionStartingProcessor.cs +++ b/src/Processors/ExecutionStartingProcessor.cs @@ -8,6 +8,7 @@ using System; using System.Diagnostics; using System.Threading; +using System.Threading.Tasks; using Gauge.CSharp.Core; using Gauge.Messages; @@ -24,7 +25,7 @@ public ExecutionStartingProcessor(IExecutionOrchestrator executionOrchestrator) [DebuggerHidden] - public virtual ExecutionStatusResponse Process(ExecutionStartingRequest request) + public virtual async Task Process(ExecutionStartingRequest request) { var debuggingEnv = Utils.TryReadEnvValue("DEBUGGING"); if (debuggingEnv != null && debuggingEnv.ToLower().Equals("true")) @@ -43,7 +44,7 @@ public virtual ExecutionStatusResponse Process(ExecutionStartingRequest request) break; } } - return ExecuteHooks(request.CurrentExecutionInfo); + return await ExecuteHooks(request.CurrentExecutionInfo); } diff --git a/src/Processors/HookExecutionProcessor.cs b/src/Processors/HookExecutionProcessor.cs index 9c8e3d4..56481bd 100644 --- a/src/Processors/HookExecutionProcessor.cs +++ b/src/Processors/HookExecutionProcessor.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Gauge.CSharp.Core; using Gauge.Dotnet.Strategy; using Gauge.Messages; @@ -33,11 +34,11 @@ protected HookExecutionProcessor(IExecutionOrchestrator executionOrchestrator) protected virtual string CacheClearLevel => null; - protected virtual ExecutionStatusResponse ExecuteHooks(ExecutionInfo info) + protected virtual async Task ExecuteHooks(ExecutionInfo info) { var applicableTags = GetApplicableTags(info); var protoExecutionResult = - ExecutionOrchestrator.ExecuteHooks(HookType, Strategy, applicableTags, info); + await ExecutionOrchestrator.ExecuteHooks(HookType, Strategy, applicableTags, info); var allPendingMessages = ExecutionOrchestrator.GetAllPendingMessages().Where(m => m != null); var allPendingScreenShotFiles = ExecutionOrchestrator.GetAllPendingScreenshotFiles(); protoExecutionResult.Message.AddRange(allPendingMessages); diff --git a/src/Processors/ScenarioExecutionEndingProcessor.cs b/src/Processors/ScenarioExecutionEndingProcessor.cs index 93eaaf0..508f27f 100644 --- a/src/Processors/ScenarioExecutionEndingProcessor.cs +++ b/src/Processors/ScenarioExecutionEndingProcessor.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Gauge.Messages; namespace Gauge.Dotnet.Processors @@ -26,10 +27,10 @@ public ScenarioExecutionEndingProcessor(IExecutionOrchestrator executionOrchestr protected override string CacheClearLevel => ScenarioLevel; - public ExecutionStatusResponse Process(ScenarioExecutionEndingRequest request) + public async Task Process(ScenarioExecutionEndingRequest request) { _executionOrchestrator.CloseExecutionScope(); - var result = ExecuteHooks(request.CurrentExecutionInfo); + var result = await ExecuteHooks(request.CurrentExecutionInfo); ClearCacheForConfiguredLevel(); return result; } diff --git a/src/Processors/ScenarioExecutionStartingProcessor.cs b/src/Processors/ScenarioExecutionStartingProcessor.cs index 406b53b..e135da7 100644 --- a/src/Processors/ScenarioExecutionStartingProcessor.cs +++ b/src/Processors/ScenarioExecutionStartingProcessor.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Gauge.Messages; namespace Gauge.Dotnet.Processors @@ -23,10 +24,10 @@ public ScenarioExecutionStartingProcessor(IExecutionOrchestrator executionOrches protected override string HookType => "BeforeScenario"; - public ExecutionStatusResponse Process(ScenarioExecutionStartingRequest request) + public async Task Process(ScenarioExecutionStartingRequest request) { _executionOrchestrator.StartExecutionScope("scenario"); - return ExecuteHooks(request.CurrentExecutionInfo); + return await ExecuteHooks(request.CurrentExecutionInfo); } protected override List GetApplicableTags(ExecutionInfo info) diff --git a/src/Processors/SpecExecutionEndingProcessor.cs b/src/Processors/SpecExecutionEndingProcessor.cs index b376d86..f81cf79 100644 --- a/src/Processors/SpecExecutionEndingProcessor.cs +++ b/src/Processors/SpecExecutionEndingProcessor.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Gauge.Messages; namespace Gauge.Dotnet.Processors @@ -29,10 +30,10 @@ protected override List GetApplicableTags(ExecutionInfo info) return info.CurrentSpec.Tags.ToList(); } - public ExecutionStatusResponse Process(SpecExecutionEndingRequest request) + public async Task Process(SpecExecutionEndingRequest request) { _executionOrchestrator.CloseExecutionScope(); - var result = ExecuteHooks(request.CurrentExecutionInfo); + var result = await ExecuteHooks(request.CurrentExecutionInfo); ClearCacheForConfiguredLevel(); return result; } diff --git a/src/Processors/SpecExecutionStartingProcessor.cs b/src/Processors/SpecExecutionStartingProcessor.cs index e3889f7..65c1091 100644 --- a/src/Processors/SpecExecutionStartingProcessor.cs +++ b/src/Processors/SpecExecutionStartingProcessor.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Gauge.Messages; namespace Gauge.Dotnet.Processors @@ -27,10 +28,10 @@ protected override List GetApplicableTags(ExecutionInfo info) return info.CurrentSpec.Tags.ToList(); } - public ExecutionStatusResponse Process(SpecExecutionStartingRequest request) + public async Task Process(SpecExecutionStartingRequest request) { _executionOrchestrator.StartExecutionScope("spec"); - return ExecuteHooks(request.CurrentExecutionInfo); + return await ExecuteHooks(request.CurrentExecutionInfo); } } } \ No newline at end of file diff --git a/src/Processors/StepExecutionEndingProcessor.cs b/src/Processors/StepExecutionEndingProcessor.cs index fe19c36..89b14e8 100644 --- a/src/Processors/StepExecutionEndingProcessor.cs +++ b/src/Processors/StepExecutionEndingProcessor.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Gauge.Messages; namespace Gauge.Dotnet.Processors @@ -20,9 +21,9 @@ public StepExecutionEndingProcessor(IExecutionOrchestrator executionOrchestrator protected override string HookType => "AfterStep"; - public ExecutionStatusResponse Process(StepExecutionEndingRequest request) + public async Task Process(StepExecutionEndingRequest request) { - return base.ExecuteHooks(request.CurrentExecutionInfo); + return await base.ExecuteHooks(request.CurrentExecutionInfo); } protected override List GetApplicableTags(ExecutionInfo info) diff --git a/src/Processors/StepExecutionStartingProcessor.cs b/src/Processors/StepExecutionStartingProcessor.cs index 89d6906..66b1bfb 100644 --- a/src/Processors/StepExecutionStartingProcessor.cs +++ b/src/Processors/StepExecutionStartingProcessor.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Gauge.Messages; namespace Gauge.Dotnet.Processors @@ -20,9 +21,9 @@ public StepExecutionStartingProcessor(IExecutionOrchestrator executionOrchestrat protected override string HookType => "BeforeStep"; - public ExecutionStatusResponse Process(StepExecutionStartingRequest request) + public async Task Process(StepExecutionStartingRequest request) { - return ExecuteHooks(request.CurrentExecutionInfo); + return await ExecuteHooks(request.CurrentExecutionInfo); } protected override List GetApplicableTags(ExecutionInfo info) diff --git a/src/Processors/StepValidationProcessor.cs b/src/Processors/StepValidationProcessor.cs index d934378..3f38193 100644 --- a/src/Processors/StepValidationProcessor.cs +++ b/src/Processors/StepValidationProcessor.cs @@ -24,24 +24,25 @@ public StepValidationProcessor(IStepRegistry stepRegistry) public StepValidateResponse Process(StepValidateRequest request) { - var stepToValidate = request.StepText; - var isValid = true; - var errorMessage = ""; - var suggestion = ""; - var errorType = StepValidateResponse.Types.ErrorType.StepImplementationNotFound; - if (!_stepRegistry.ContainsStep(stepToValidate)) + if (!_stepRegistry.ContainsStep(request.StepText)) { - isValid = false; - errorMessage = string.Format("No implementation found for : {0}. Full Step Text :", stepToValidate); - suggestion = GetSuggestion(request.StepValue); + return GetStepValidateResponseMessage(false, StepValidateResponse.Types.ErrorType.StepImplementationNotFound, + $"No implementation found for : {request.StepText}. Full Step Text :", GetSuggestion(request.StepValue)); } - else if (_stepRegistry.HasMultipleImplementations(stepToValidate)) + + if (_stepRegistry.HasMultipleImplementations(request.StepText)) { - isValid = false; - errorType = StepValidateResponse.Types.ErrorType.DuplicateStepImplementation; - errorMessage = string.Format("Multiple step implementations found for : {0}", stepToValidate); + return GetStepValidateResponseMessage(false, StepValidateResponse.Types.ErrorType.DuplicateStepImplementation, + $"Multiple step implementations found for : {request.StepText}", string.Empty); } - return GetStepValidateResponseMessage(isValid, errorType, errorMessage, suggestion); + + if (_stepRegistry.HasAsyncVoidImplementation(request.StepText)) + { + return GetStepValidateResponseMessage(false, StepValidateResponse.Types.ErrorType.StepImplementationNotFound, + string.Empty, $"Found a potential step implementation with 'async void' return for : {request.StepText}. Usage of 'async void' is discouraged (https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming#avoid-async-void). Use `async Task` instead."); + } + return GetStepValidateResponseMessage(true, + StepValidateResponse.Types.ErrorType.StepImplementationNotFound, string.Empty, string.Empty); } private string GetSuggestion(ProtoStepValue stepValue) diff --git a/src/StepExecutor.cs b/src/StepExecutor.cs index 98f99e2..07b7901 100644 --- a/src/StepExecutor.cs +++ b/src/StepExecutor.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Runtime.Serialization.Json; using System.Text; +using System.Threading.Tasks; using Gauge.Dotnet.Converters; using Gauge.Dotnet.Models; using Gauge.Dotnet.Wrappers; @@ -28,7 +29,7 @@ public StepExecutor(IAssemblyLoader assemblyLoader, IReflectionWrapper reflectio _assemblyLoader = assemblyLoader; } - public ExecutionResult Execute(GaugeMethod gaugeMethod, params string[] args) + public async Task Execute(GaugeMethod gaugeMethod, params string[] args) { { var method = gaugeMethod.MethodInfo; @@ -47,7 +48,7 @@ public ExecutionResult Execute(GaugeMethod gaugeMethod, params string[] args) } }).ToArray(); Logger.Debug($"Executing method: {gaugeMethod.Name}"); - Execute(method, StringParamConverter.TryConvertParams(method, parameters)); + await base.Execute(method, StringParamConverter.TryConvertParams(method, parameters)); executionResult.Success = true; } catch (Exception ex) diff --git a/src/dotnet.json b/src/dotnet.json index b2c6614..085707d 100644 --- a/src/dotnet.json +++ b/src/dotnet.json @@ -1,6 +1,6 @@ { "id": "dotnet", - "version": "0.5.3", + "version": "0.6.0", "description": "C# support for gauge + .NET 6.0/7.0/8.0", "run": { "windows": [ diff --git a/test/ExecutionOrchestratorTests.cs b/test/ExecutionOrchestratorTests.cs index eadaaf2..4a83bf2 100644 --- a/test/ExecutionOrchestratorTests.cs +++ b/test/ExecutionOrchestratorTests.cs @@ -10,6 +10,7 @@ using System.IO; using System.Reflection; using System.Threading; +using System.Threading.Tasks; using Gauge.CSharp.Core; using Gauge.Dotnet.Models; using Gauge.Dotnet.Strategy; @@ -38,7 +39,7 @@ public void TearDown() } [Test] - public void ShouldExecuteHooks() + public async Task ShouldExecuteHooks() { var pendingMessages = new List { "Foo", "Bar" }; var pendingScreenshots = new List { "screenshot.png" }; @@ -47,11 +48,11 @@ public void ShouldExecuteHooks() var mockAssemblyLoader = new Mock(); var mockClassInstanceManager = new Mock().Object; var hooksStrategy = new HooksStrategy(); - var mockHookExecuter = new Mock(); - mockHookExecuter.Setup(m => + var mockHookExecutor = new Mock(); + mockHookExecutor.Setup(m => m.Execute("hooks", hooksStrategy, new List(), It.IsAny()) - ).Returns(executionResult).Verifiable(); - var mockStepExecuter = new Mock(); + ).Returns(Task.FromResult(executionResult)).Verifiable(); + var mockStepExecutor = new Mock(); var reflectionWrapper = mockReflectionWrapper.Object; var mockType = new Mock().Object; mockAssemblyLoader.Setup(x => x.GetLibType(LibType.MessageCollector)).Returns(mockType); @@ -64,17 +65,17 @@ public void ShouldExecuteHooks() .Returns(pendingScreenshots); var assemblyLoader = mockAssemblyLoader.Object; var executionOrchestrator = new ExecutionOrchestrator(reflectionWrapper, assemblyLoader, - mockClassInstanceManager, mockHookExecuter.Object, mockStepExecuter.Object); + mockClassInstanceManager, mockHookExecutor.Object, mockStepExecutor.Object); - var result = executionOrchestrator.ExecuteHooks("hooks", hooksStrategy, new List(), + var result = await executionOrchestrator.ExecuteHooks("hooks", hooksStrategy, new List(), It.IsAny()); - mockHookExecuter.VerifyAll(); + mockHookExecutor.VerifyAll(); ClassicAssert.False(result.Failed); } [Test] - public void ShouldExecuteHooksAndNotTakeScreenshotOnFailureWhenDisabled() + public async Task ShouldExecuteHooksAndNotTakeScreenshotOnFailureWhenDisabled() { var pendingMessages = new List { "Foo", "Bar" }; var pendingScreenshots = new List { "screenshot.png" }; @@ -97,7 +98,7 @@ public void ShouldExecuteHooksAndNotTakeScreenshotOnFailureWhenDisabled() mockHookExecuter.Object, mockStepExecuter.Object); mockHookExecuter.Setup(executor => executor.Execute("hooks", hooksStrategy, new List(), It.IsAny()) - ).Returns(executionResult).Verifiable(); + ).Returns(Task.FromResult(executionResult)).Verifiable(); var mockType = new Mock().Object; mockAssemblyLoader.Setup(x => x.GetLibType(LibType.MessageCollector)).Returns(mockType); mockAssemblyLoader.Setup(x => x.GetLibType(LibType.ScreenshotFilesCollector)).Returns(mockType); @@ -111,7 +112,7 @@ public void ShouldExecuteHooksAndNotTakeScreenshotOnFailureWhenDisabled() var screenshotEnabled = Utils.TryReadEnvValue("SCREENSHOT_ON_FAILURE"); Environment.SetEnvironmentVariable("SCREENSHOT_ON_FAILURE", "false"); - var result = orchestrator.ExecuteHooks("hooks", hooksStrategy, new List(), + var result = await orchestrator.ExecuteHooks("hooks", hooksStrategy, new List(), It.IsAny()); mockHookExecuter.VerifyAll(); @@ -121,7 +122,7 @@ public void ShouldExecuteHooksAndNotTakeScreenshotOnFailureWhenDisabled() } [Test] - public void ShouldExecuteMethod() + public async Task ShouldExecuteMethod() { var pendingMessages = new List { "Foo", "Bar" }; var pendingScreenshots = new List { "screenshot.png" }; @@ -135,7 +136,7 @@ public void ShouldExecuteMethod() var mockStepExecutor = new Mock(); mockStepExecutor.Setup(executor => executor.Execute(gaugeMethod, It.IsAny())) - .Returns(() => new ExecutionResult { Success = true }) + .Returns(() => Task.FromResult(new ExecutionResult { Success = true })) .Callback(() => Thread.Sleep(1)); // Simulate a delay in method execution var orchestrator = new ExecutionOrchestrator(mockReflectionWrapper.Object, mockAssemblyLoader.Object, @@ -150,14 +151,14 @@ public void ShouldExecuteMethod() mockReflectionWrapper.Setup(x => x.InvokeMethod(mockType, null, "GetAllPendingScreenshotFiles", It.IsAny())) .Returns(pendingScreenshots); - var result = orchestrator.ExecuteStep(gaugeMethod, args); + var result = await orchestrator.ExecuteStep(gaugeMethod, args); mockStepExecutor.VerifyAll(); ClassicAssert.False(result.Failed); ClassicAssert.True(result.ExecutionTime > 0); } [Test] - public void ShouldNotTakeScreenShotWhenDisabled() + public async Task ShouldNotTakeScreenShotWhenDisabled() { var pendingMessages = new List { "Foo", "Bar" }; var pendingScreenshots = new List { "screenshot.png" }; @@ -175,7 +176,7 @@ public void ShouldNotTakeScreenShotWhenDisabled() var mockHookExecuter = new Mock(); var mockStepExecutor = new Mock(); mockStepExecutor.Setup(executor => executor.Execute(gaugeMethod, It.IsAny())) - .Returns(executionResult); + .Returns(Task.FromResult(executionResult)); var mockType = new Mock().Object; mockAssemblyLoader.Setup(x => x.GetLibType(LibType.MessageCollector)).Returns(mockType); mockAssemblyLoader.Setup(x => x.GetLibType(LibType.ScreenshotFilesCollector)).Returns(mockType); @@ -192,7 +193,7 @@ public void ShouldNotTakeScreenShotWhenDisabled() var screenshotEnabled = Utils.TryReadEnvValue("SCREENSHOT_ON_FAILURE"); Environment.SetEnvironmentVariable("SCREENSHOT_ON_FAILURE", "false"); - var result = orchestrator.ExecuteStep(gaugeMethod, "Bar", "string"); + var result = await orchestrator.ExecuteStep(gaugeMethod, "Bar", "string"); mockStepExecutor.VerifyAll(); ClassicAssert.True(string.IsNullOrEmpty(result.FailureScreenshotFile)); @@ -200,7 +201,7 @@ public void ShouldNotTakeScreenShotWhenDisabled() } [Test] - public void ShouldTakeScreenShotOnFailedExecution() + public async Task ShouldTakeScreenShotOnFailedExecution() { var pendingMessages = new List { "Foo", "Bar" }; var expectedScreenshot = "Testscreenshot.png"; @@ -218,7 +219,7 @@ public void ShouldTakeScreenShotOnFailedExecution() var mockHookExecuter = new Mock(); var mockStepExecutor = new Mock(); mockStepExecutor.Setup(executor => executor.Execute(gaugeMethod, It.IsAny())) - .Returns(executionResult).Verifiable(); + .Returns(Task.FromResult(executionResult)).Verifiable(); var mockType = new Mock().Object; mockAssemblyLoader.Setup(x => x.GetLibType(LibType.MessageCollector)).Returns(mockType); mockAssemblyLoader.Setup(x => x.GetLibType(LibType.ScreenshotFilesCollector)).Returns(mockType); @@ -232,7 +233,7 @@ public void ShouldTakeScreenShotOnFailedExecution() var orchestrator = new ExecutionOrchestrator(mockReflectionWrapper.Object, mockAssemblyLoader.Object, mockInstance, mockHookExecuter.Object, mockStepExecutor.Object); - var result = orchestrator.ExecuteStep(gaugeMethod, "Bar", "String"); + var result = await orchestrator.ExecuteStep(gaugeMethod, "Bar", "String"); mockStepExecutor.VerifyAll(); diff --git a/test/Extensions/MethodInfoExtensionTests.cs b/test/Extensions/MethodInfoExtensionTests.cs index 0a3f9cc..b0a7395 100644 --- a/test/Extensions/MethodInfoExtensionTests.cs +++ b/test/Extensions/MethodInfoExtensionTests.cs @@ -99,5 +99,29 @@ public void ShouldNotBeRecoverableWhenContinueOnFailureOnNonStep() ClassicAssert.False(bazMethod.IsRecoverableStep(assemblyLoader.Object), "Recoverable is true only when method is a Step"); } + + [Test] + public void ShouldDetectAsyncVoidReturnType() + { + var assemblyLoader = new Mock(); + var fooMethod = new MockMethodBuilder(assemblyLoader) + .WithName("Foo") + .WithAsyncVoidReturn() + .Build(); + + Assert.That(fooMethod.IsAsyncVoid(), Is.True, "Async void method was not detected."); + } + + [Test] + public void ShouldNotDetectAsyncVoidReturnTypeWhenNotPresent() + { + var assemblyLoader = new Mock(); + var fooMethod = new MockMethodBuilder(assemblyLoader) + .WithName("Foo") + .Build(); + + Assert.That(fooMethod.IsAsyncVoid(), Is.False, "Async void method was detected incorrectly."); + } + } } \ No newline at end of file diff --git a/test/Helpers/MockMethodBuilder.cs b/test/Helpers/MockMethodBuilder.cs index 97ce016..bcfc3ce 100644 --- a/test/Helpers/MockMethodBuilder.cs +++ b/test/Helpers/MockMethodBuilder.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Moq; namespace Gauge.Dotnet.UnitTests.Helpers @@ -115,6 +116,13 @@ public MockMethodBuilder WithTagAggregation(int aggregation) return this; } + public MockMethodBuilder WithAsyncVoidReturn() + { + mockMethod.Setup(x => x.ReturnType).Returns(typeof(void)); + mockMethod.Setup(x => x.GetCustomAttributes(typeof(AsyncStateMachineAttribute),true)).Returns(new []{new AsyncStateMachineAttribute(null)}); + return this; + } + private static ParameterInfo[] GetParameters(IEnumerable> methodParams) { if (methodParams is null) diff --git a/test/HookExecutorTests.cs b/test/HookExecutorTests.cs index 9732c92..2518fc0 100644 --- a/test/HookExecutorTests.cs +++ b/test/HookExecutorTests.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Reflection; using System.Threading; +using System.Threading.Tasks; using Gauge.Dotnet.Strategy; using Gauge.Dotnet.UnitTests.Helpers; using Gauge.Dotnet.Wrappers; @@ -24,7 +25,7 @@ namespace Gauge.Dotnet.UnitTests internal class HookExecutorTests { [Test] - public void ShoudExecuteHooks() + public async Task ShoudExecuteHooks() { var mockInstance = new Mock().Object; var mockClassInstanceManagerType = new Mock().Object; @@ -54,13 +55,13 @@ public void ShoudExecuteHooks() var executor = new HookExecutor(mockAssemblyLoader.Object, mockReflectionWrapper.Object, mockClassInstanceManager, mockExecutionInfoMapper.Object); - var result = executor.Execute("BeforeSuite", new HooksStrategy(), new List(), + var result = await executor.Execute("BeforeSuite", new HooksStrategy(), new List(), new ExecutionInfo()); ClassicAssert.True(result.Success, $"Hook execution failed: {result.ExceptionMessage}\n{result.StackTrace}"); } [Test] - public void ShoudExecuteHooksWithExecutionContext() + public async Task ShoudExecuteHooksWithExecutionContext() { var mockInstance = new Mock().Object; var mockClassInstanceManagerType = new Mock().Object; @@ -95,14 +96,14 @@ public void ShoudExecuteHooksWithExecutionContext() var executor = new HookExecutor(mockAssemblyLoader.Object, mockReflectionWrapper.Object, mockClassInstanceManager, mockExecutionInfoMapper.Object); - var result = executor.Execute("BeforeSuite", new HooksStrategy(), new List(), + var result = await executor.Execute("BeforeSuite", new HooksStrategy(), new List(), executionInfo); ClassicAssert.True(result.Success, $"Hook execution failed: {result.ExceptionMessage}\n{result.StackTrace}"); mockReflectionWrapper.VerifyAll(); } [Test] - public void ShoudExecuteHooksAndGetTheError() + public async Task ShoudExecuteHooksAndGetTheError() { var mockInstance = new Mock().Object; var mockClassInstanceManagerType = new Mock().Object; @@ -129,10 +130,10 @@ public void ShoudExecuteHooksAndGetTheError() .Returns(new { Foo = "bar" }); var executor = new HookExecutor(mockAssemblyLoader.Object, mockReflectionWrapper.Object, mockClassInstanceManager, mockExecutionInfoMapper.Object); - mockReflectionWrapper.Setup(x => x.Invoke(methodInfo, mockInstance)) + mockReflectionWrapper.Setup(x => x.Invoke(methodInfo, mockInstance, It.IsAny())) .Throws(new Exception("hook failed")); - var result = executor.Execute("BeforeSuite", new HooksStrategy(), new List(), + var result = await executor.Execute("BeforeSuite", new HooksStrategy(), new List(), new ExecutionInfo()); ClassicAssert.False(result.Success, "Hook execution passed, expected failure"); ClassicAssert.AreEqual(result.ExceptionMessage, "hook failed"); diff --git a/test/Processors/ExecuteStepProcessorTests.cs b/test/Processors/ExecuteStepProcessorTests.cs index c09641f..1413738 100644 --- a/test/Processors/ExecuteStepProcessorTests.cs +++ b/test/Processors/ExecuteStepProcessorTests.cs @@ -5,6 +5,7 @@ *----------------------------------------------------------------*/ +using System.Threading.Tasks; using Gauge.Dotnet.Models; using Gauge.Dotnet.Processors; using Gauge.Messages; @@ -22,7 +23,7 @@ public void Foo(string param) } [Test] - public void ShouldProcessExecuteStepRequest() + public async Task ShouldProcessExecuteStepRequest() { const string parsedStepText = "Foo"; var request = new ExecuteStepRequest @@ -45,12 +46,12 @@ public void ShouldProcessExecuteStepRequest() mockStepRegistry.Setup(x => x.MethodFor(parsedStepText)).Returns(fooMethodInfo); var mockOrchestrator = new Mock(); mockOrchestrator.Setup(e => e.ExecuteStep(fooMethodInfo, It.IsAny())) - .Returns(() => new ProtoExecutionResult { ExecutionTime = 1, Failed = false }); + .Returns(() => Task.FromResult(new ProtoExecutionResult { ExecutionTime = 1, Failed = false })); var mockTableFormatter = new Mock(); var processor = new ExecuteStepProcessor(mockStepRegistry.Object, mockOrchestrator.Object, mockTableFormatter.Object); - var response = processor.Process(request); + var response = await processor.Process(request); ClassicAssert.False(response.ExecutionResult.Failed); } @@ -58,7 +59,7 @@ public void ShouldProcessExecuteStepRequest() [Test] [TestCase(Parameter.Types.ParameterType.Table)] [TestCase(Parameter.Types.ParameterType.SpecialTable)] - public void ShouldProcessExecuteStepRequestForTableParam(Parameter.Types.ParameterType parameterType) + public async Task ShouldProcessExecuteStepRequestForTableParam(Parameter.Types.ParameterType parameterType) { const string parsedStepText = "Foo"; var protoTable = new ProtoTable(); @@ -83,11 +84,11 @@ public void ShouldProcessExecuteStepRequestForTableParam(Parameter.Types.Paramet mockStepRegistry.Setup(x => x.MethodFor(parsedStepText)).Returns(fooMethodInfo); var mockOrchestrator = new Mock(); mockOrchestrator.Setup(e => e.ExecuteStep(fooMethodInfo, It.IsAny())).Returns(() => - new ProtoExecutionResult + Task.FromResult(new ProtoExecutionResult { ExecutionTime = 1, Failed = false - }); + })); var mockAssemblyLoader = new Mock(); mockAssemblyLoader.Setup(x => x.GetLibType(LibType.MessageCollector)); @@ -95,7 +96,7 @@ public void ShouldProcessExecuteStepRequestForTableParam(Parameter.Types.Paramet mockTableFormatter.Setup(x => x.GetJSON(protoTable)) .Returns(tableJSON); var processor = new ExecuteStepProcessor(mockStepRegistry.Object, mockOrchestrator.Object, mockTableFormatter.Object); - var response = processor.Process(request); + var response = await processor.Process(request); mockOrchestrator.Verify(executor => executor.ExecuteStep(fooMethodInfo, It.Is(strings => strings[0] == tableJSON))); @@ -103,7 +104,7 @@ public void ShouldProcessExecuteStepRequestForTableParam(Parameter.Types.Paramet } [Test] - public void ShouldReportArgumentMismatch() + public async Task ShouldReportArgumentMismatch() { const string parsedStepText = "Foo"; var request = new ExecuteStepRequest @@ -120,7 +121,7 @@ public void ShouldReportArgumentMismatch() var mockTableFormatter = new Mock(); var processor = new ExecuteStepProcessor(mockStepRegistry.Object, mockOrchestrator.Object, mockTableFormatter.Object); - var response = processor.Process(request); + var response = await processor.Process(request); ClassicAssert.True(response.ExecutionResult.Failed); ClassicAssert.AreEqual(response.ExecutionResult.ErrorMessage, @@ -128,7 +129,7 @@ public void ShouldReportArgumentMismatch() } [Test] - public void ShouldReportMissingStep() + public async Task ShouldReportMissingStep() { const string parsedStepText = "Foo"; var request = new ExecuteStepRequest @@ -142,7 +143,7 @@ public void ShouldReportMissingStep() var mockTableFormatter = new Mock(); var processor = new ExecuteStepProcessor(mockStepRegistry.Object, mockOrchestrator.Object, mockTableFormatter.Object); - var response = processor.Process(request); + var response = await processor.Process(request); ClassicAssert.True(response.ExecutionResult.Failed); ClassicAssert.AreEqual(response.ExecutionResult.ErrorMessage, diff --git a/test/Processors/ExecutionEndingProcessorTests.cs b/test/Processors/ExecutionEndingProcessorTests.cs index f07f1d1..162ff61 100644 --- a/test/Processors/ExecutionEndingProcessorTests.cs +++ b/test/Processors/ExecutionEndingProcessorTests.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; +using System.Threading.Tasks; using Gauge.Dotnet.Models; using Gauge.Dotnet.Processors; using Gauge.Dotnet.Strategy; @@ -53,7 +54,7 @@ public void Setup() _mockMethodExecutor.Setup(x => x.ExecuteHooks("AfterSuite", It.IsAny(), It.IsAny>(), It.IsAny())) - .Returns(_protoExecutionResult); + .Returns(Task.FromResult(_protoExecutionResult)); _mockMethodExecutor.Setup(x => x.GetAllPendingMessages()).Returns(_pendingMessages); _mockMethodExecutor.Setup(x => @@ -86,9 +87,9 @@ public void ShouldGetEmptyTagListByDefault() } [Test] - public void ShouldProcessHooks() + public async Task ShouldProcessHooks() { - var result = _executionEndingProcessor.Process(_request); + var result = await _executionEndingProcessor.Process(_request); _mockMethodExecutor.VerifyAll(); ClassicAssert.AreEqual(result.ExecutionResult.Message, _pendingMessages); ClassicAssert.AreEqual(result.ExecutionResult.ScreenshotFiles, _pendingScreenshotFiles); diff --git a/test/Processors/ExecutionStartingProcessorTests.cs b/test/Processors/ExecutionStartingProcessorTests.cs index 6a454bc..c1067a0 100644 --- a/test/Processors/ExecutionStartingProcessorTests.cs +++ b/test/Processors/ExecutionStartingProcessorTests.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; +using System.Threading.Tasks; using Gauge.Dotnet.Models; using Gauge.Dotnet.Processors; using Gauge.Dotnet.Strategy; @@ -46,7 +47,7 @@ public void Setup() _mockMethodExecutor.Setup(x => x.ExecuteHooks("BeforeSuite", It.IsAny(), It.IsAny>(), It.IsAny())) - .Returns(_protoExecutionResult); + .Returns(Task.FromResult(_protoExecutionResult)); _mockMethodExecutor.Setup(x => x.GetAllPendingMessages()).Returns(_pendingMessages); _mockMethodExecutor.Setup(x => @@ -100,10 +101,10 @@ public void ShouldGetEmptyTagListByDefault() } [Test] - public void ShouldProcessHooks() + public async Task ShouldProcessHooks() { var executionStartingRequest = new ExecutionStartingRequest(); - var result = _executionStartingProcessor.Process(executionStartingRequest); + var result = await _executionStartingProcessor.Process(executionStartingRequest); _mockMethodExecutor.VerifyAll(); ClassicAssert.AreEqual(result.ExecutionResult.Message, _pendingMessages); diff --git a/test/Processors/ScenarioExecutionEndingProcessorTests.cs b/test/Processors/ScenarioExecutionEndingProcessorTests.cs index 0cdb3ab..f20f7ad 100644 --- a/test/Processors/ScenarioExecutionEndingProcessorTests.cs +++ b/test/Processors/ScenarioExecutionEndingProcessorTests.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Gauge.Dotnet.Processors; using Gauge.Dotnet.Strategy; using Gauge.Messages; @@ -45,10 +46,6 @@ public void ShouldGetTagListFromSpecAndScenario() CurrentSpec = specInfo, CurrentScenario = scenarioInfo }; - var currentExecutionInfo = new ScenarioExecutionEndingRequest - { - CurrentExecutionInfo = currentScenario - }; var tags = AssertEx.ExecuteProtectedMethod("GetApplicableTags", currentScenario) .ToList(); @@ -90,7 +87,7 @@ public void ShouldNotGetDuplicateTags() } [Test] - public void ShouldExecutreBeforeScenarioHook() + public async Task ShouldExecutreBeforeScenarioHook() { var scenarioExecutionStartingRequest = new ScenarioExecutionEndingRequest { @@ -113,7 +110,7 @@ public void ShouldExecutreBeforeScenarioHook() mockMethodExecutor.Setup(x => x.ExecuteHooks("AfterScenario", It.IsAny(), It.IsAny>(), It.IsAny())) - .Returns(protoExecutionResult); + .Returns(Task.FromResult(protoExecutionResult)); mockMethodExecutor.Setup(x => x.GetAllPendingMessages()).Returns(pendingMessages); mockMethodExecutor.Setup(x => @@ -121,7 +118,7 @@ public void ShouldExecutreBeforeScenarioHook() var processor = new ScenarioExecutionEndingProcessor(mockMethodExecutor.Object); - var result = processor.Process(scenarioExecutionStartingRequest); + var result = await processor.Process(scenarioExecutionStartingRequest); ClassicAssert.False(result.ExecutionResult.Failed); ClassicAssert.AreEqual(result.ExecutionResult.Message.ToList(), pendingMessages); ClassicAssert.AreEqual(result.ExecutionResult.ScreenshotFiles.ToList(), pendingScreenshotFiles); diff --git a/test/Processors/ScenarioExecutionStartingProcessorTests.cs b/test/Processors/ScenarioExecutionStartingProcessorTests.cs index 7ea08be..c187be9 100644 --- a/test/Processors/ScenarioExecutionStartingProcessorTests.cs +++ b/test/Processors/ScenarioExecutionStartingProcessorTests.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Gauge.Dotnet.Processors; using Gauge.Dotnet.Strategy; using Gauge.Messages; @@ -45,12 +46,7 @@ public void ShouldGetTagListFromExecutionInfo() CurrentSpec = specInfo, CurrentScenario = scenarioInfo }; - var currentExecutionInfo = new ScenarioExecutionStartingRequest - { - CurrentExecutionInfo = currentScenario - }; - - + var tags = AssertEx.ExecuteProtectedMethod("GetApplicableTags", currentScenario) .ToList(); @@ -92,7 +88,7 @@ public void ShouldNotFetchDuplicateTags() } [Test] - public void ShouldExecuteBeforeScenarioHook() + public async Task ShouldExecuteBeforeScenarioHook() { var scenarioExecutionEndingRequest = new ScenarioExecutionStartingRequest { @@ -116,7 +112,7 @@ public void ShouldExecuteBeforeScenarioHook() mockMethodExecutor.Setup(x => x.ExecuteHooks("BeforeScenario", It.IsAny(), It.IsAny>(), It.IsAny())) - .Returns(protoExecutionResult); + .Returns(Task.FromResult(protoExecutionResult)); mockMethodExecutor.Setup(x => x.GetAllPendingMessages()).Returns(pendingMessages); mockMethodExecutor.Setup(x => @@ -124,7 +120,7 @@ public void ShouldExecuteBeforeScenarioHook() var processor = new ScenarioExecutionStartingProcessor(mockMethodExecutor.Object); - var result = processor.Process(scenarioExecutionEndingRequest); + var result = await processor.Process(scenarioExecutionEndingRequest); ClassicAssert.False(result.ExecutionResult.Failed); ClassicAssert.AreEqual(result.ExecutionResult.Message.ToList(), pendingMessages); ClassicAssert.AreEqual(result.ExecutionResult.ScreenshotFiles.ToList(), pendingScreenshotFiles); diff --git a/test/Processors/SpecExecutionEndingProcessorTests.cs b/test/Processors/SpecExecutionEndingProcessorTests.cs index 2b4d9e9..1b1e796 100644 --- a/test/Processors/SpecExecutionEndingProcessorTests.cs +++ b/test/Processors/SpecExecutionEndingProcessorTests.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Gauge.Dotnet.Processors; using Gauge.Dotnet.Strategy; using Gauge.Messages; @@ -48,7 +49,7 @@ public void ShouldGetTagListFromExecutionInfo() } [Test] - public void ShouldExecuteBeforeSpecHook() + public async Task ShouldExecuteBeforeSpecHook() { var request = new SpecExecutionEndingRequest { @@ -71,14 +72,14 @@ public void ShouldExecuteBeforeSpecHook() mockMethodExecutor.Setup(x => x.ExecuteHooks("AfterSpec", It.IsAny(), It.IsAny>(), It.IsAny())) - .Returns(protoExecutionResult); + .Returns(Task.FromResult(protoExecutionResult)); mockMethodExecutor.Setup(x => x.GetAllPendingMessages()).Returns(pendingMessages); mockMethodExecutor.Setup(x => x.GetAllPendingScreenshotFiles()).Returns(pendingScreenshotFiles); var processor = new SpecExecutionEndingProcessor(mockMethodExecutor.Object); - var result = processor.Process(request); + var result = await processor.Process(request); ClassicAssert.False(result.ExecutionResult.Failed); ClassicAssert.AreEqual(result.ExecutionResult.Message.ToList(), pendingMessages); ClassicAssert.AreEqual(result.ExecutionResult.ScreenshotFiles.ToList(), pendingScreenshotFiles); diff --git a/test/Processors/SpecExecutionStartingProcessorTests.cs b/test/Processors/SpecExecutionStartingProcessorTests.cs index 33f4c2b..a576561 100644 --- a/test/Processors/SpecExecutionStartingProcessorTests.cs +++ b/test/Processors/SpecExecutionStartingProcessorTests.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Gauge.Dotnet.Processors; using Gauge.Dotnet.Strategy; using Gauge.Messages; @@ -48,7 +49,7 @@ public void ShouldGetTagListFromExecutionInfo() } [Test] - public void ShouldExecutreBeforeSpecHook() + public async Task ShouldExecutreBeforeSpecHook() { var specExecutionStartingRequest = new SpecExecutionStartingRequest { @@ -72,7 +73,7 @@ public void ShouldExecutreBeforeSpecHook() mockMethodExecutor.Setup(x => x.ExecuteHooks("BeforeSpec", It.IsAny(), It.IsAny>(), It.IsAny())) - .Returns(protoExecutionResult); + .Returns(Task.FromResult(protoExecutionResult)); mockMethodExecutor.Setup(x => x.GetAllPendingMessages()).Returns(pendingMessages); mockMethodExecutor.Setup(x => @@ -80,7 +81,7 @@ public void ShouldExecutreBeforeSpecHook() var processor = new SpecExecutionStartingProcessor(mockMethodExecutor.Object); - var result = processor.Process(request); + var result = await processor.Process(request); ClassicAssert.False(result.ExecutionResult.Failed); ClassicAssert.AreEqual(result.ExecutionResult.Message.ToList(), pendingMessages); ClassicAssert.AreEqual(result.ExecutionResult.ScreenshotFiles.ToList(), pendingScreenshotFiles); diff --git a/test/Processors/StepExecutionEndingProcessorTests.cs b/test/Processors/StepExecutionEndingProcessorTests.cs index 15c33cc..4399c1c 100644 --- a/test/Processors/StepExecutionEndingProcessorTests.cs +++ b/test/Processors/StepExecutionEndingProcessorTests.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Gauge.Dotnet.Models; using Gauge.Dotnet.Processors; using Gauge.Dotnet.Strategy; @@ -70,7 +71,7 @@ public void Setup() _mockMethodExecutor.Setup(x => x.ExecuteHooks("AfterStep", It.IsAny(), It.IsAny>(), It.IsAny())) - .Returns(_protoExecutionResult); + .Returns(Task.FromResult(_protoExecutionResult)); _mockMethodExecutor.Setup(x => x.GetAllPendingMessages()).Returns(_pendingMessages); _mockMethodExecutor.Setup(x => @@ -85,9 +86,9 @@ public void ShouldExtendFromHooksExecutionProcessor() } [Test] - public void ShouldReadPendingMessages() + public async Task ShouldReadPendingMessages() { - var response = _stepExecutionEndingProcessor.Process(_stepExecutionEndingRequest); + var response = await _stepExecutionEndingProcessor.Process(_stepExecutionEndingRequest); ClassicAssert.True(response != null); ClassicAssert.True(response.ExecutionResult != null); diff --git a/test/Processors/StepExecutionStartingProcessorTests.cs b/test/Processors/StepExecutionStartingProcessorTests.cs index 3c9919d..1cc086d 100644 --- a/test/Processors/StepExecutionStartingProcessorTests.cs +++ b/test/Processors/StepExecutionStartingProcessorTests.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Gauge.Dotnet.Models; using Gauge.Dotnet.Processors; using Gauge.Dotnet.Strategy; @@ -26,7 +27,7 @@ public void ShouldExtendFromHooksExecutionProcessor() } [Test] - public void ShouldClearExistingGaugeMessages() + public async Task ShouldClearExistingGaugeMessages() { var mockExecutionHelper = new Mock(); @@ -43,7 +44,7 @@ public void ShouldClearExistingGaugeMessages() mockExecutionHelper.Setup(executor => executor.ExecuteHooks(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) - .Returns(protoExecutionResult); + .Returns(Task.FromResult(protoExecutionResult)); var hookRegistry = new Mock(); hookRegistry.Setup(registry => registry.BeforeStepHooks).Returns(new HashSet()); @@ -53,14 +54,14 @@ public void ShouldClearExistingGaugeMessages() mockExecutionHelper.Setup(x => x.ExecuteHooks("BeforeStep", It.IsAny(), It.IsAny>(), It.IsAny())) - .Returns(protoExecutionResult); + .Returns(Task.FromResult(protoExecutionResult)); mockExecutionHelper.Setup(x => x.GetAllPendingMessages()).Returns(pendingMessages); mockExecutionHelper.Setup(x => x.GetAllPendingScreenshotFiles()).Returns(pendingScreenshotFiles); var processor = new StepExecutionStartingProcessor(mockExecutionHelper.Object); - var result = processor.Process(request); + var result = await processor.Process(request); ClassicAssert.AreEqual(result.ExecutionResult.Message, pendingMessages); ClassicAssert.AreEqual(result.ExecutionResult.ScreenshotFiles, pendingScreenshotFiles); } diff --git a/test/StepExecutorTests.cs b/test/StepExecutorTests.cs index d3492d9..722fee0 100644 --- a/test/StepExecutorTests.cs +++ b/test/StepExecutorTests.cs @@ -7,6 +7,7 @@ using System; using System.Threading; +using System.Threading.Tasks; using Gauge.Dotnet.Models; using Gauge.Dotnet.UnitTests.Helpers; using Gauge.Dotnet.Wrappers; @@ -20,7 +21,7 @@ namespace Gauge.Dotnet.UnitTests internal class StepExecutorTests { [Test] - public void ShoudExecuteStep() + public async Task ShoudExecuteStep() { var mockInstance = new Mock().Object; var mockClassInstanceManagerType = new Mock().Object; @@ -50,13 +51,12 @@ public void ShoudExecuteStep() mockReflectionWrapper.Setup(x => x.Invoke(methodInfo, mockInstance)) .Returns(null); - - var result = executor.Execute(gaugeMethod); + var result = await executor.Execute(gaugeMethod); ClassicAssert.True(result.Success); } [Test] - public void ShoudExecuteStepAndGetFailure() + public async Task ShoudExecuteStepAndGetFailure() { var mockInstance = new Mock().Object; var mockClassInstanceManagerType = new Mock().Object; @@ -86,13 +86,13 @@ public void ShoudExecuteStepAndGetFailure() mockReflectionWrapper.Setup(x => x.Invoke(methodInfo, mockInstance)) .Throws(new Exception("step execution failure")); - var result = executor.Execute(gaugeMethod); + var result = await executor.Execute(gaugeMethod); ClassicAssert.False(result.Success); ClassicAssert.AreEqual(result.ExceptionMessage, "step execution failure"); } [Test] - public void ShoudExecuteStepAndGetRecoverableError() + public async Task ShoudExecuteStepAndGetRecoverableError() { var mockInstance = new Mock().Object; var mockClassInstanceManagerType = new Mock().Object; @@ -124,7 +124,7 @@ public void ShoudExecuteStepAndGetRecoverableError() mockReflectionWrapper.Setup(x => x.Invoke(methodInfo, mockInstance)) .Throws(new Exception("step execution failure")); - var result = executor.Execute(gaugeMethod); + var result = await executor.Execute(gaugeMethod); ClassicAssert.False(result.Success); ClassicAssert.True(result.Recoverable); ClassicAssert.AreEqual(result.ExceptionMessage, "step execution failure"); diff --git a/test/StepRegistryTests.cs b/test/StepRegistryTests.cs index ce5e1b5..af7854d 100644 --- a/test/StepRegistryTests.cs +++ b/test/StepRegistryTests.cs @@ -8,6 +8,8 @@ using System.Collections.Generic; using System.Linq; using Gauge.Dotnet.Models; +using Gauge.Dotnet.UnitTests.Helpers; +using Moq; using NUnit.Framework; using NUnit.Framework.Legacy; @@ -187,10 +189,23 @@ public void ShouldNotContainStepPositionForExternalSteps() stepRegistry.AddStep("Foo", new GaugeMethod { Name = "Foo", StepText = "Foo", FileName = "foo.cs" }); stepRegistry.AddStep("Bar", new GaugeMethod { Name = "Bar", StepText = "Bar", IsExternal = true }); - var positions = stepRegistry.GetStepPositions("foo.cs"); + var positions = stepRegistry.GetStepPositions("foo.cs").ToArray(); - ClassicAssert.True(positions.Count() == 1); - ClassicAssert.AreNotEqual(positions.First().StepValue, "Bar"); + Assert.That(positions.Length, Is.EqualTo(1)); + Assert.That(positions.First().StepValue, Is.Not.EqualTo("Bar")); + } + + [Test] + public void ShouldCheckForAsyncVoidImplementation() + { + var stepRegistry = new StepRegistry(); + var mockAssemblyLoader = new Mock(); + var mockMethodBuilder = new MockMethodBuilder(mockAssemblyLoader); + var impl = new GaugeMethod { MethodInfo = mockMethodBuilder.WithName("Foo").WithStep("Foo").WithAsyncVoidReturn().Build() }; + + stepRegistry.AddStep("Foo", impl); + + Assert.That(stepRegistry.HasAsyncVoidImplementation("Foo"), Is.True); } } } \ No newline at end of file diff --git a/test/ValidateProcessorTests.cs b/test/ValidateProcessorTests.cs index 7967559..0709c29 100644 --- a/test/ValidateProcessorTests.cs +++ b/test/ValidateProcessorTests.cs @@ -48,7 +48,7 @@ public void ShouldGetErrorResponseForStepValidateRequestWhenMultipleStepImplFoun } [Test] - public void ShouldGetErrorResponseForStepValidateRequestWhennNoImplFound() + public void ShouldGetErrorResponseForStepValidateRequestWhenNoImplFound() { var request = new StepValidateRequest { @@ -73,7 +73,7 @@ public void ShouldGetErrorResponseForStepValidateRequestWhennNoImplFound() [Test] - public void ShouldGetVaildResponseForStepValidateRequest() + public void ShouldGetValidResponseForStepValidateRequest() { var request = new StepValidateRequest { @@ -83,11 +83,38 @@ public void ShouldGetVaildResponseForStepValidateRequest() _mockStepRegistry.Setup(registry => registry.ContainsStep("step_text_1")).Returns(true); _mockStepRegistry.Setup(registry => registry.HasMultipleImplementations("step_text_1")).Returns(false); + _mockStepRegistry.Setup(registry => registry.HasAsyncVoidImplementation("step_text_1")).Returns(false); + + var response = new StepValidationProcessor(_mockStepRegistry.Object).Process(request); + + Assert.That(response.IsValid, Is.True, $"Expected true, got error: {response.ErrorMessage}"); + } + [Test] + public void ShouldGetErrorResponseForStepValidateRequestWhenAsyncVoidImplFound() + { + var request = new StepValidateRequest + { + StepText = "step_text_1", + NumberOfParameters = 0, + StepValue = new ProtoStepValue + { + ParameterizedStepValue = "step_text_1", + StepValue = "step_text_1" + } + }; + _mockStepRegistry.Setup(registry => registry.ContainsStep("step_text_1")).Returns(true); + _mockStepRegistry.Setup(registry => registry.HasMultipleImplementations("step_text_1")).Returns(false); + _mockStepRegistry.Setup(registry => registry.HasAsyncVoidImplementation("step_text_1")).Returns(true); var processor = new StepValidationProcessor(_mockStepRegistry.Object); var response = processor.Process(request); - ClassicAssert.AreEqual(true, response.IsValid); + ClassicAssert.AreEqual(false, response.IsValid); + ClassicAssert.AreEqual(StepValidateResponse.Types.ErrorType.StepImplementationNotFound, + response.ErrorType); + StringAssert.StartsWith("Found a potential step implementation with 'async void' return for : step_text_1.", + response.Suggestion); + StringAssert.Contains("Usage of 'async void' is discouraged", response.Suggestion); } } } \ No newline at end of file