Repository: Polly-Contrib/Simmy Branch: master Commit: 0a52b0a134f0 Files: 87 Total size: 553.9 KB Directory structure: gitextract_az8v0g9u/ ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── GitVersionConfig.yaml ├── LICENSE.txt ├── README.md ├── appveyor.yml ├── build.bat ├── build.cake ├── build.ps1 └── src/ ├── Polly.Contrib.Simmy/ │ ├── AsyncMonkeyEngine.cs │ ├── AsyncMonkeyPolicy.cs │ ├── Behavior/ │ │ ├── AsyncInjectBehaviourOptionsSyntax.cs │ │ ├── AsyncInjectBehaviourPolicy.cs │ │ ├── AsyncInjectBehaviourSyntax.cs │ │ ├── AsyncInjectBehaviourTResultOptionsSyntax.cs │ │ ├── AsyncInjectBehaviourTResultSyntax.cs │ │ ├── InjectBehaviourAsyncOptions.cs │ │ ├── InjectBehaviourAsyncOptionsExtensions.cs │ │ ├── InjectBehaviourOptions.cs │ │ ├── InjectBehaviourOptionsExtensions.cs │ │ ├── InjectBehaviourOptionsSyntax.cs │ │ ├── InjectBehaviourPolicy.cs │ │ ├── InjectBehaviourSyntax.cs │ │ ├── InjectBehaviourTResultOptionsSyntax.cs │ │ └── InjectBehaviourTResultSyntax.cs │ ├── IMonkeyPolicy.cs │ ├── InjectOptionsAsyncBase.cs │ ├── InjectOptionsAsyncBaseExtensions.cs │ ├── InjectOptionsBase.cs │ ├── InjectOptionsBaseExtensions.cs │ ├── Latency/ │ │ ├── AsyncInjectLatencyOptionsSyntax.cs │ │ ├── AsyncInjectLatencyPolicy.cs │ │ ├── AsyncInjectLatencySyntax.cs │ │ ├── InjectLatencyAsyncOptions.cs │ │ ├── InjectLatencyAsyncOptionsExtensions.cs │ │ ├── InjectLatencyOptions.cs │ │ ├── InjectLatencyOptionsExtensions.cs │ │ ├── InjectLatencyOptionsSyntax.cs │ │ ├── InjectLatencyPolicy.cs │ │ └── InjectLatencySyntax.cs │ ├── MonkeyEngine.cs │ ├── MonkeyPolicy.cs │ ├── Outcomes/ │ │ ├── AsyncInjectOutcomeOptionsSyntax.cs │ │ ├── AsyncInjectOutcomePolicy.cs │ │ ├── AsyncInjectOutcomeSyntax.cs │ │ ├── InjectOutcomeAsyncOptions.cs │ │ ├── InjectOutcomeAsyncOptionsExtensions.cs │ │ ├── InjectOutcomeOptions.cs │ │ ├── InjectOutcomeOptionsExtensions.cs │ │ ├── InjectOutcomeOptionsSyntax.cs │ │ ├── InjectOutcomePolicy.cs │ │ └── InjectOutcomeSyntax.cs │ ├── Polly.Contrib.Simmy.csproj │ └── Utilities/ │ ├── GuardExtensions.cs │ └── ThreadSafeRandom_LockOncePerThread.cs ├── Polly.Contrib.Simmy.Specs/ │ ├── Behavior/ │ │ ├── InjectBehaviourAsyncSpecs.cs │ │ ├── InjectBehaviourAsyncWithOptionsSpecs.cs │ │ ├── InjectBehaviourSpecs.cs │ │ ├── InjectBehaviourTResultAsyncSpecs.cs │ │ ├── InjectBehaviourTResultAsyncWithOptionsSpecs.cs │ │ ├── InjectBehaviourTResultSpecs.cs │ │ ├── InjectBehaviourTResultWithOptionsSpecs.cs │ │ └── InjectBehaviourWithOptionsSpecs.cs │ ├── Helpers/ │ │ ├── Constants.cs │ │ └── ResultPrimitive.cs │ ├── Latency/ │ │ ├── InjectLatencyAsyncSpecs.cs │ │ ├── InjectLatencyAsyncWithOptionsSpecs.cs │ │ ├── InjectLatencySpecs.cs │ │ ├── InjectLatencyTResultAsyncSpecs .cs │ │ ├── InjectLatencyTResultAsyncWithOptionsSpecs.cs │ │ ├── InjectLatencyTResultSpecs.cs │ │ ├── InjectLatencyTResultWithOptionsSpecs.cs │ │ └── InjectLatencyWithOptionsSpecs.cs │ ├── Outcomes/ │ │ ├── InjectFaultAsyncSpecs.cs │ │ ├── InjectFaultAsyncWithOptionsSpecs.cs │ │ ├── InjectFaultSpecs.cs │ │ ├── InjectFaultTResultAsyncSpecs.cs │ │ ├── InjectFaultTResultAsyncWithOptionsSpecs.cs │ │ ├── InjectFaultTResultSpecs.cs │ │ ├── InjectFaultTResultWithOptionsSpecs.cs │ │ └── InjectFaultWithOptionsSpecs.cs │ └── Polly.Contrib.Simmy.Specs.csproj ├── Polly.Contrib.Simmy.nuspec ├── Polly.Contrib.Simmy.sln └── Polly.Contrib.Simmy.snk ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) [Bb]in/ [Oo]bj/ # mstest test results TestResults ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.sln.docstates .vs/ .vscode/ .idea # Build results [Dd]ebug/ [Rr]elease/ x64/ *_i.c *_p.c *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.log *.vspscc *.vssscc .builds # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf # Visual Studio profiler *.psess *.vsp *.vspx # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper* # NCrunch *.ncrunch* .*crunch*.local.xml # GhostDoc *.GhostDoc.xml # Installshield output folder [Ee]xpress # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish # Publish Web Output *.Publish.xml # NuGet Packages Directory packages # Windows Azure Build Output csx *.build.csdef # Windows Store app package directory AppPackages/ # Others [Bb]in [Oo]bj sql TestResults [Tt]est[Rr]esult* *.Cache ClientBin [Ss]tyle[Cc]op.* ~$* *.dbmdl Generated_Code #added for RIA/Silverlight projects # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML artifacts build tools *.lock.json *.nuget.targets *.nuget.props ================================================ FILE: CHANGELOG.md ================================================ ## 0.3.0 - Add a new Fluent-builder syntax - Add intuitive syntax for result stubbing, for use in unit-tests or in other systems on how those systems handle faults - Compiles on mac and linux - Add support for .NET Standard 2.1 - Validates constant `injectionRate` at Policy configuration time ## 0.2.0 - Makes InjectLatency policies cancellable (both sync and async) - Add support for cancellation on async configuration-providing delegates ## 0.1.0 - Initial launch ================================================ FILE: CODE_OF_CONDUCT.md ================================================ We ask our contributors to abide by the [Code of Conduct of the .NET Foundation](https://www.dotnetfoundation.org/code-of-conduct). ================================================ FILE: CONTRIBUTING.md ================================================ Please see the Instructions for Contributing in the [Polly ReadMe](https://github.com/App-vNext/Polly#instructions-for-contributing) and [Polly wiki](https://github.com/App-vNext/Polly/wiki/Git-Workflow). ================================================ FILE: GitVersionConfig.yaml ================================================ next-version: 0.3.0 ================================================ FILE: LICENSE.txt ================================================ New BSD License = Copyright (c) 2018-2019, App vNext All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of App vNext nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ # Simmy Simmy is a [chaos-engineering](http://principlesofchaos.org/) and fault-injection tool, integrating with the [Polly resilience project for .NET](https://github.com/App-vNext/Polly). It is releasing April 2019 and works with [Polly v7.0.0](https://www.nuget.org/packages/Polly/7.1.0) onwards. Simmy allows you to introduce a chaos-injection policy or policies at any location where you execute code through Polly. [![NuGet version](https://badge.fury.io/nu/Polly.Contrib.Simmy.svg)](https://badge.fury.io/nu/Polly.Contrib.Simmy) [![Build status](https://ci.appveyor.com/api/projects/status/5v3bpgjkw4snv3no?svg=true)](https://ci.appveyor.com/project/Polly-Contrib/simmy) [![Slack Status](http://www.pollytalk.org/badge.svg)](http://www.pollytalk.org) Simmy # Motivation There are a lot of questions when it comes to chaos-engineering and making sure that a system is actually ready to face the worst possible scenarios: * Is my system resilient enough? * Am I handling the right exceptions/scenarios? * How will my system behave if X happens? * How can I test without waiting for a handled (or even unhandled) exception to happen in my production environment? Using Polly helps me introduce resilience to my project, but I don't want to have to wait for expected or unexpected failures to test it out. My resilience could be wrongly implemented; testing the scenarios is not straight forward; and mocking failure of some dependencies (for example a cloud SaaS or PaaS service) is not always straightforward. **What do I need, to simulate chaotic scenarios in my production environment?** * A way to mock failures of dependencies (any service dependency for example). * Define when to fail based on some external factors - maybe global configuration or some rule. * A way to revert easily, to control the blast radius. * Production grade, to run this in a production or near-production system with automation. # Chaos policies Simmy offers the following chaos-injection policies: |Policy| What does the policy do?| | ------------- |------------- | |**[Exception](#Inject-exception)**|Injects exceptions in your system.| |**[Result](#Inject-result)**|Substitute results to fake faults in your system.| |**[Latency](#Inject-latency)**|Injects latency into executions before the calls are made.| |**[Behavior](#Inject-behavior)**|Allows you to inject _any_ extra behaviour, before a call is placed. | # Usage ## Step 1: Set up the Monkey Policy ## Inject exception ```csharp var chaosPolicy = MonkeyPolicy.InjectException(Action>); ``` For example: ```csharp // Following example causes the policy to throw SocketException with a probability of 5% if enabled var fault = new SocketException(errorCode: 10013); var chaosPolicy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(0.05) .Enabled() ); ``` ## Inject result ```csharp var chaosPolicy = MonkeyPolicy.InjectResult(Action>); ``` For example: ```csharp // Following example causes the policy to return a bad request HttpResponseMessage with a probability of 5% if enabled var result = new HttpResponseMessage(HttpStatusCode.BadRequest); var chaosPolicy = MonkeyPolicy.InjectResult(with => with.Result(result) .InjectionRate(0.05) .Enabled() ); ``` ## Inject latency ```csharp var chaosPolicy = MonkeyPolicy.InjectLatency(Action); ``` For example: ```csharp // Following example causes policy to introduce an added latency of 5 seconds to a randomly-selected 10% of the calls. var isEnabled = true; var chaosPolicy = MonkeyPolicy.InjectLatency(with => with.Latency(TimeSpan.FromSeconds(5)) .InjectionRate(0.1) .Enabled(isEnabled) ); ``` ## Inject behavior ```csharp var chaosPolicy = MonkeyPolicy.InjectBehaviour(Action); ``` For example: ```csharp // Following example causes policy to execute a method to restart a virtual machine; the probability that method will be executed is 1% if enabled var chaosPolicy = MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => restartRedisVM()) .InjectionRate(0.01) .EnabledWhen((ctx, ct) => isEnabled(ctx, ct)) ); ``` ## Parameters All the parameters are expressed in a Fluent-builder syntax way. ### Enabled Determines whether the policy is enabled or not. * Configure that the monkey policy is enabled. ```csharp PolicyOptions.Enabled(); ``` * Receives a boolean value indicating whether the monkey policy is enabled. ```csharp PolicyOptions.Enabled(bool); ``` * Receives a delegate which can be executed to determine whether the monkey policy should be enabled. ```csharp PolicyOptions.EnabledWhen(Func); ``` ### InjectionRate A decimal between 0 and 1 inclusive. The policy will inject the fault, randomly, that proportion of the time, eg: if 0.2, twenty percent of calls will be randomly affected; if 0.01, one percent of calls; if 1, all calls. * Receives a double value between [0, 1] indicating the rate at which this monkey policy should inject chaos. ```csharp PolicyOptions.InjectionRate(Double); ``` * Receives a delegate which can be executed to determine the rate at which this monkey policy should inject chaos. ```csharp PolicyOptions.InjectionRate(Func); ``` ### Fault The fault to inject. The `Fault` api has overloads to build the policy in a generic way: `PolicyOptions.Fault(...)` * Receives an exception to configure the fault to inject with the monkey policy. ```csharp PolicyOptions.Fault(Exception); ``` * Receives a delegate representing the fault to inject with the monkey policy. ```csharp PolicyOptions.Fault(Func); ``` ### Result The result to inject. * Receives a generic TResult value to configure the result to inject with the monkey policy. ```csharp PolicyOptions.Result(TResult); ``` * Receives a delegate representing the result to inject with the monkey policy. ```csharp PolicyOptions.Result(Func); ``` ### Latency The latency to inject. * Receives a TimeSpan value to configure the latency to inject with the monkey policy. ```csharp PolicyOptions.Latency(TimeSpan); ``` * Receives a delegate representing the latency to inject with the monkey policy. ```csharp PolicyOptions.Latency(Func); ``` ### Behaviour The behaviour to inject. * Receives an Action to configure the behaviour to inject with the monkey policy. ```csharp PolicyOptions.Behaviour(Action); ``` * Receives a delegate representing the Action to inject with the monkey policy. ```csharp PolicyOptions.Behaviour(Action); ``` ### Context-driven behaviour All parameters are available in a `Func` form. This allows you to control the chaos injected: + in a **dynamic** manner: by eg driving the chaos from external configuration files + in a **targeted** manner: by tagging your policy executions with a [`Context.OperationKey`](https://github.com/App-vNext/Polly/wiki/Keys-And-Context-Data#pre-defined-keys-on-context) and introducing chaos targeting particular tagged operations The [example app](https://github.com/Polly-Contrib/Polly.Contrib.SimmyDemo_WebApi) demonstrates both these approaches in practice. ## Step 2: Execute code through the Monkey Policy ```csharp // Executes through the chaos policy directly chaosPolicy.Execute(() => someMethod()); // Executes through the chaos policy using Context chaosPolicy.Execute((ctx) => someMethod(), context); // Wrap the chaos policy inside other Polly resilience policies, using PolicyWrap var policyWrap = Policy .Wrap(fallbackPolicy, timeoutPolicy, chaosLatencyPolicy); policyWrap.Execute(() => someMethod()) // All policies are also available in async forms. var chaosLatencyPolicy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(TimeSpan.FromSeconds(5)) .InjectionRate(0.1) .Enabled() ); var policyWrap = Policy .WrapAsync(fallbackPolicy, timeoutPolicy, chaosLatencyPolicy); var result = await policyWrap.ExecuteAsync(token => service.GetFoo(parametersBar, token), myCancellationToken); // For general information on Polly policy syntax see: https://github.com/App-vNext/Polly ``` It is usual to place the Simmy policy innermost in a PolicyWrap. By placing the chaos policies innermost, they subvert the usual outbound call at the last minute, substituting their fault or adding extra latency. The existing Polly policies - further out in the PolicyWrap - still apply, so you can test how the Polly resilience you have configured handles the chaos/faults injected by Simmy. **Note:** The above examples demonstrate how to execute through a Simmy policy directly, and how to include a Simmy policy in an individual PolicyWrap. If your policies are configured by .NET Core DI at StartUp, for example via HttpClientFactory, there are also patterns which can configure Simmy into your app as a whole, at StartUp. See the Simmy Sample App discussed below. ## Example app: Controlling chaos via configuration and Polly.Context This [Simmy sample app](https://github.com/Polly-Contrib/Polly.Contrib.SimmyDemo_WebApi) shows different approaches/patterns for how you can configure Simmy to introduce chaos policies in a project. Patterns demonstrated are: * Configuring `StartUp` so that Simmy chaos policies are only introduced in builds for certain environments (for instance, Dev but not Prod). * Configuring Simmy chaos policies to be injected into the app without changing any existing Polly configuration code. * Injecting faults or chaos by modifying external configuration. The patterns shown in the sample app are intended as starting points but are not mandatory. Simmy is very flexible, and we would love to hear how you use it! ## Wrapping up All chaos policies (Monkey policies) are designed to inject behavior randomly (faults, latency or custom behavior), so a Monkey policy allows you to specify an injection rate between 0 and 1 (0-100%) thus, the higher is the injection rate the higher is the probability to inject them. Also it allows you to specify whether or not the random injection is enabled, that way you can release/hold (turn on/off) the monkeys regardless of injection rate you specify, it means, if you specify an injection rate of 100% but you tell to the policy that the random injection is disabled, it will do nothing. ## Further information See [Issues](https://github.com/App-vNext/Simmy/issues) for latest discussions on taking Simmy forward! ## Credits Simmy was [the brainchild of](https://github.com/App-vNext/Polly/issues/499) [@mebjas](https://github.com/mebjas) and [@reisenberger](https://github.com/reisenberger). The major part of the implementation was by [@vany0114](https://github.com/vany0114) and [@mebjas](https://github.com/mebjas), with contributions also from [@reisenberger](https://github.com/reisenberger) of the Polly team. ## Blogs and architecture samples around Simmy ### Blog posts * [Simmy, the monkey for making chaos](http://elvanydev.com/chaos-injection-with-simmy/) -by [Geovanny Alzate Sandoval](https://twitter.com/geovany0114) * [Simmy and Azure App Configuration](http://elvanydev.com/simmy-with-azure-app-configuration/) -by [Geovanny Alzate Sandoval](https://twitter.com/geovany0114) * [Simmy Chaos Engine for .NET – Part 1, Injecting Faults](https://nodogmablog.bryanhogan.net/2019/07/simmy-chaos-engine-for-net-part-1-injecting-faults/) -by [Bryan Hogan](https://twitter.com/bryanjhogan) * [Simmy Chaos Engine for .NET – Part 2, Resilience and Injected Faults](https://nodogmablog.bryanhogan.net/2019/07/simmy-chaos-engine-for-net-part-2-resilience-and-injected-faults/) -by [Bryan Hogan](https://twitter.com/bryanjhogan) * [Chaos Engineering your .NET applications using Simmy](http://josephwoodward.co.uk/2020/01/chaos-engineering-your-dot-net-application-simmy) -by [Joseph Woodward](https://twitter.com/joe_mighty) ### Samples * [Dylan Reisenberger](http://www.thepollyproject.org/author/dylan/) presents an [intentionally simple example](https://github.com/Polly-Contrib/Polly.Contrib.SimmyDemo_WebApi) .NET Core WebAPI app demonstrating how we can set up Simmy chaos policies for certain environments and without changing any existing configuration code injecting faults or chaos by modifying external configuration. * [Geovanny Alzate Sandoval](https://github.com/vany0114) made a [microservices based sample application](https://github.com/vany0114/chaos-injection-using-simmy) to demonstrate how chaos engineering works with Simmy using chaos policies in a distributed system and how we can inject even a custom behavior given our needs or infrastructure, this time injecting custom behavior to generate chaos in our Service Fabric Cluster. * [Bjørn Einar Bjartnes](https://github.com/bjartwolf) made a [red-green load-testing resilience workshop](https://github.com/bjartnes/bounded-disturbances) to understand how errors and resiliency mechanisms affect a system under load. It has been used to run workshops at for example [NDC Oslo](https://ndcoslo.com/workshops/building-and-testing-resilient-services/dbb2ed362bcc) and there is a video from the workshop at [DotNext](https://www.youtube.com/watch?v=_UFiaNlfxjI). ================================================ FILE: appveyor.yml ================================================ os: Visual Studio 2019 # Build script build_script: - ps: .\build.ps1 # Tests test: off artifacts: - path: artifacts\nuget-package\*.nupkg environment: # Skip dotnet package caching on build servers DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true ================================================ FILE: build.bat ================================================ @ECHO OFF PUSHD %~dp0 PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command "& './build.ps1'" IF %errorlevel% neq 0 PAUSE ================================================ FILE: build.cake ================================================ /////////////////////////////////////////////////////////////////////////////// // ARGUMENTS /////////////////////////////////////////////////////////////////////////////// var target = Argument("target", "Default"); var configuration = Argument("configuration", "Release"); ////////////////////////////////////////////////////////////////////// // EXTERNAL NUGET TOOLS ////////////////////////////////////////////////////////////////////// #tool nuget:?package=xunit.runner.console&version=2.4.1 #tool nuget:?package=GitVersion.CommandLine&version=5.0.1 #tool nuget:?package=Brutal.Dev.StrongNameSigner&version=2.4.0 ////////////////////////////////////////////////////////////////////// // EXTERNAL NUGET LIBRARIES ////////////////////////////////////////////////////////////////////// #addin nuget:?package=Cake.FileHelpers&version=3.2.1 /////////////////////////////////////////////////////////////////////////////// // GLOBAL VARIABLES /////////////////////////////////////////////////////////////////////////////// var projectName = "Polly.Contrib.Simmy"; var keyName = "Polly.Contrib.Simmy.snk"; var solutions = GetFiles("./**/*.sln"); var solutionPaths = solutions.Select(solution => solution.GetDirectory()); var srcDir = Directory("./src"); var buildDir = Directory("./build"); var artifactsDir = Directory("./artifacts"); var testResultsDir = artifactsDir + Directory("test-results"); // NuGet var nuspecFilename = projectName + ".nuspec"; var nuspecSrcFile = srcDir + File(nuspecFilename); var nuspecDestFile = buildDir + File(nuspecFilename); var nupkgDestDir = artifactsDir + Directory("nuget-package"); var snkFile = srcDir + File(keyName); // Gitversion var gitVersionPath = ToolsExePath("GitVersion.exe"); Dictionary gitVersionOutput; // Versioning string nugetVersion; string appveyorBuildNumber; string assemblyVersion; string assemblySemver; // StrongNameSigner var strongNameSignerPath = ToolsExePath("StrongNameSigner.Console.exe"); /////////////////////////////////////////////////////////////////////////////// // SETUP / TEARDOWN /////////////////////////////////////////////////////////////////////////////// Setup(_ => { Information(""); Information(" ███████╗██╗███╗ ███╗███╗ ███╗██╗ ██╗"); Information(" ██╔════╝██║████╗ ████║████╗ ████║╚██╗ ██╔╝"); Information(" ███████╗██║██╔████╔██║██╔████╔██║ ╚████╔╝ "); Information(" ╚════██║██║██║╚██╔╝██║██║╚██╔╝██║ ╚██╔╝ "); Information(" ███████║██║██║ ╚═╝ ██║██║ ╚═╝ ██║ ██║ "); Information(" ╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ "); Information(""); }); Teardown(_ => { Information("Finished running tasks."); }); ////////////////////////////////////////////////////////////////////// // PRIVATE TASKS ////////////////////////////////////////////////////////////////////// Task("__Clean") .Does(() => { DirectoryPath[] cleanDirectories = new DirectoryPath[] { buildDir, testResultsDir, nupkgDestDir, artifactsDir }; CleanDirectories(cleanDirectories); foreach(var path in cleanDirectories) { EnsureDirectoryExists(path); } foreach(var path in solutionPaths) { Information("Cleaning {0}", path); CleanDirectories(path + "/**/bin/" + configuration); CleanDirectories(path + "/**/obj/" + configuration); } }); Task("__RestoreNugetPackages") .Does(() => { foreach(var solution in solutions) { Information("Restoring NuGet Packages for {0}", solution); NuGetRestore(solution); } }); Task("__UpdateAssemblyVersionInformation") .Does(() => { var gitVersionSettings = new ProcessSettings() .SetRedirectStandardOutput(true); IEnumerable outputLines; StartProcess(gitVersionPath, gitVersionSettings, out outputLines); var output = string.Join("\n", outputLines); gitVersionOutput = Newtonsoft.Json.JsonConvert.DeserializeObject>(output); Information("Updated GlobalAssemblyInfo"); Information(""); Information("Obtained raw version info for package versioning:"); Information("NuGetVersion -> {0}", gitVersionOutput["NuGetVersion"]); Information("FullSemVer -> {0}", gitVersionOutput["FullSemVer"]); Information("AssemblySemVer -> {0}", gitVersionOutput["AssemblySemVer"]); appveyorBuildNumber = gitVersionOutput["FullSemVer"].ToString(); nugetVersion = gitVersionOutput["NuGetVersion"].ToString(); assemblyVersion = gitVersionOutput["Major"].ToString() + ".0.0.0"; assemblySemver = gitVersionOutput["AssemblySemVer"].ToString(); Information(""); Information("Mapping versioning information to:"); Information("Appveyor build number -> {0}", appveyorBuildNumber); Information("Nuget package version -> {0}", nugetVersion); Information("AssemblyVersion -> {0}", assemblyVersion); Information("AssemblyFileVersion -> {0}", assemblySemver); Information("AssemblyInformationalVersion -> {0}", assemblySemver); }); Task("__UpdateDotNetStandardAssemblyVersionNumber") .Does(() => { Information("Updating Assembly Version Information"); var attributeToValueMap = new Dictionary() { { "AssemblyVersion", assemblyVersion }, { "FileVersion", assemblySemver }, { "InformationalVersion", assemblySemver }, { "Version", nugetVersion }, { "PackageVersion", nugetVersion }, }; var csproj = File("./src/Polly.Contrib.Simmy/Polly.Contrib.Simmy.csproj"); foreach(var attributeMap in attributeToValueMap) { var attribute = attributeMap.Key; var value = attributeMap.Value; var replacedFiles = ReplaceRegexInFiles(csproj, $@"\<{attribute}\>[^\<]*\", $@"<{attribute}>{value}"); if (!replacedFiles.Any()) { throw new Exception($"{attribute} version could not be updated in {csproj}."); } } }); Task("__UpdateAppVeyorBuildNumber") .WithCriteria(() => AppVeyor.IsRunningOnAppVeyor) .Does(() => { AppVeyor.UpdateBuildVersion(appveyorBuildNumber); }); Task("__BuildSolutions") .Does(() => { foreach(var solution in solutions) { Information("Building {0}", solution); MSBuild(solution, settings => settings .SetConfiguration(configuration) .WithProperty("TreatWarningsAsErrors", "true") .UseToolVersion(MSBuildToolVersion.VS2019) .SetVerbosity(Verbosity.Minimal) .SetNodeReuse(false)); } }); Task("__RunTests") .Does(() => { foreach(var specsProj in GetFiles("./src/**/*.Specs.csproj")) { DotNetCoreTest(specsProj.FullPath, new DotNetCoreTestSettings { Configuration = configuration, NoBuild = true }); } }); Task("__CopyOutputToNugetFolder") .Does(() => { var sourceDir = srcDir + Directory("Polly.Contrib.Simmy") + Directory("bin") + Directory(configuration); var destDir = buildDir + Directory("lib"); Information("Copying {0} -> {1}.", sourceDir, destDir); CopyDirectory(sourceDir, destDir); CopyFile(nuspecSrcFile, nuspecDestFile); }); Task("__StronglySignAssemblies") .Does(() => { //see: https://github.com/brutaldev/StrongNameSigner var strongNameSignerSettings = new ProcessSettings() .WithArguments(args => args .Append("-in") .AppendQuoted(buildDir) .Append("-k") .AppendQuoted(snkFile) .Append("-l") .AppendQuoted("Changes")); StartProcess(strongNameSignerPath, strongNameSignerSettings); }); Task("__CreateSignedNugetPackage") .Does(() => { var packageName = projectName; Information("Building {0}.{1}.nupkg", packageName, nugetVersion); var nuGetPackSettings = new NuGetPackSettings { Id = packageName, Title = packageName, Version = nugetVersion, OutputDirectory = nupkgDestDir }; NuGetPack(nuspecDestFile, nuGetPackSettings); }); ////////////////////////////////////////////////////////////////////// // BUILD TASKS ////////////////////////////////////////////////////////////////////// Task("Build") .IsDependentOn("__Clean") .IsDependentOn("__RestoreNugetPackages") .IsDependentOn("__UpdateAssemblyVersionInformation") .IsDependentOn("__UpdateDotNetStandardAssemblyVersionNumber") .IsDependentOn("__UpdateAppVeyorBuildNumber") .IsDependentOn("__BuildSolutions") .IsDependentOn("__RunTests") .IsDependentOn("__CopyOutputToNugetFolder") .IsDependentOn("__StronglySignAssemblies") .IsDependentOn("__CreateSignedNugetPackage"); /////////////////////////////////////////////////////////////////////////////// // PRIMARY TARGETS /////////////////////////////////////////////////////////////////////////////// Task("Default") .IsDependentOn("Build"); /////////////////////////////////////////////////////////////////////////////// // EXECUTION /////////////////////////////////////////////////////////////////////////////// RunTarget(target); ////////////////////////////////////////////////////////////////////// // HELPER FUNCTIONS ////////////////////////////////////////////////////////////////////// string ToolsExePath(string exeFileName) { var exePath = System.IO.Directory.GetFiles(@".\Tools", exeFileName, SearchOption.AllDirectories).FirstOrDefault(); return exePath; } ================================================ FILE: build.ps1 ================================================ <# .SYNOPSIS This is a Powershell script to bootstrap a Cake build. .DESCRIPTION This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) and execute your Cake build script with the parameters you provide. .PARAMETER Script The build script to execute. .PARAMETER Target The build script target to run. .PARAMETER Configuration The build configuration to use. .PARAMETER Verbosity Specifies the amount of information to be displayed. .PARAMETER Experimental Tells Cake to use the latest Roslyn release. .PARAMETER WhatIf Performs a dry run of the build script. No tasks will be executed. .PARAMETER Mono Tells Cake to use the Mono scripting engine. .LINK http://cakebuild.net #> Param( [string]$Script = "build.cake", [string]$Target = "Default", [string]$Configuration = "Release", [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] [string]$Verbosity = "Verbose", [switch]$Experimental, [Alias("DryRun","Noop")] [switch]$WhatIf, [switch]$Mono, [switch]$SkipToolPackageRestore, [switch]$Verbose ) Write-Host "Preparing to run build script..." # Should we show verbose messages? if($Verbose.IsPresent) { $VerbosePreference = "continue" } $TOOLS_DIR = Join-Path $PSScriptRoot "tools" $NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" $CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" $PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" # Should we use mono? $UseMono = ""; if($Mono.IsPresent) { Write-Verbose -Message "Using the Mono based scripting engine." $UseMono = "-mono" } # Should we use the new Roslyn? $UseExperimental = ""; if($Experimental.IsPresent -and !($Mono.IsPresent)) { Write-Verbose -Message "Using experimental version of Roslyn." $UseExperimental = "-experimental" } # Is this a dry run? $UseDryRun = ""; if($WhatIf.IsPresent) { $UseDryRun = "-dryrun" } # Make sure tools folder exists if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { New-Item -Path $TOOLS_DIR -Type directory | out-null } # Try download NuGet.exe if not exists if (!(Test-Path $NUGET_EXE)) { Write-Verbose -Message "Downloading NuGet.exe..." Invoke-WebRequest -Uri https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile $NUGET_EXE } # Make sure NuGet exists where we expect it. if (!(Test-Path $NUGET_EXE)) { Throw "Could not find NuGet.exe" } # Save nuget.exe path to environment to be available to child processed $ENV:NUGET_EXE = $NUGET_EXE # Restore tools from NuGet? if(-Not $SkipToolPackageRestore.IsPresent) { # Restore tools from NuGet. Push-Location Set-Location $TOOLS_DIR Write-Verbose -Message "Restoring tools from NuGet..." # Restore packages if (Test-Path $PACKAGES_CONFIG) { $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion" Write-Verbose ($NuGetOutput | Out-String) } # Install just Cake if missing config else { $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install Cake -Version 0.35.0 -ExcludeVersion" Write-Verbose ($NuGetOutput | Out-String) } Pop-Location if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } } # Make sure that Cake has been installed. if (!(Test-Path $CAKE_EXE)) { Throw "Could not find Cake.exe" } # Start Cake Write-Host "Running build script..." Invoke-Expression "$CAKE_EXE `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental" exit $LASTEXITCODE ================================================ FILE: src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using Polly.Contrib.Simmy.Utilities; namespace Polly.Contrib.Simmy { internal static class AsyncMonkeyEngine { private static async Task ShouldInjectAsync( Context context, CancellationToken cancellationToken, Func> injectionRate, Func> enabled, bool continueOnCapturedContext) { // to prevent execute config delegates if token is signaled before to start. cancellationToken.ThrowIfCancellationRequested(); if (!await enabled(context, cancellationToken).ConfigureAwait(continueOnCapturedContext)) { return false; } // to prevent execute injectionRate config delegate if token is signaled on enable configuration delegate. cancellationToken.ThrowIfCancellationRequested(); double injectionThreshold = await injectionRate(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); // to prevent execute further config delegates if token is signaled on injectionRate configuration delegate. cancellationToken.ThrowIfCancellationRequested(); injectionThreshold.EnsureInjectionThreshold(); return ThreadSafeRandom_LockOncePerThread.NextDouble() < injectionThreshold; } internal static async Task InjectBehaviourImplementationAsync( Func> action, Context context, CancellationToken cancellationToken, Func injectedBehaviour, Func> injectionRate, Func> enabled, bool continueOnCapturedContext) { if (await ShouldInjectAsync(context, cancellationToken, injectionRate, enabled, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext)) { await injectedBehaviour(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); } // to prevent execute the user's action if token is signaled on injectedBehaviour delegate. cancellationToken.ThrowIfCancellationRequested(); return await action(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); } internal static async Task InjectExceptionImplementationAsync( Func> action, Context context, CancellationToken cancellationToken, Func> injectedException, Func> injectionRate, Func> enabled, bool continueOnCapturedContext) { if (await ShouldInjectAsync(context, cancellationToken, injectionRate, enabled, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext)) { Exception exception = await injectedException(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); // to prevent throws the exception if token is signaled on injectedException configuration delegate. cancellationToken.ThrowIfCancellationRequested(); if (exception != null) { throw exception; } } return await action(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); } internal static async Task InjectResultImplementationAsync( Func> action, Context context, CancellationToken cancellationToken, Func> injectedResult, Func> injectionRate, Func> enabled, bool continueOnCapturedContext) { if (await ShouldInjectAsync(context, cancellationToken, injectionRate, enabled, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext)) { return await injectedResult(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); } // to prevent inject the result if token is signaled on injectedResult delegate. cancellationToken.ThrowIfCancellationRequested(); return await action(context, cancellationToken).ConfigureAwait(continueOnCapturedContext); } } } ================================================ FILE: src/Polly.Contrib.Simmy/AsyncMonkeyPolicy.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; namespace Polly.Contrib.Simmy { /// /// Contains common functionality for policies which intentionally disrupt async executions - which monkey around with calls. /// public abstract class AsyncMonkeyPolicy : AsyncPolicy, IMonkeyPolicy { internal Func> InjectionRate { get; } internal Func> Enabled { get; } internal AsyncMonkeyPolicy(Func> injectionRate, Func> enabled) { InjectionRate = injectionRate ?? throw new ArgumentNullException(nameof(injectionRate)); Enabled = enabled ?? throw new ArgumentNullException(nameof(enabled)); } } /// /// Contains common functionality for policies which intentionally disrupt async executions returning TResult - which monkey around with calls. /// /// The type of return values this policy will handle. public abstract class AsyncMonkeyPolicy : AsyncPolicy, IMonkeyPolicy { internal Func> InjectionRate { get; } internal Func> Enabled { get; } internal AsyncMonkeyPolicy(Func> injectionRate, Func> enabled) { InjectionRate = injectionRate ?? throw new ArgumentNullException(nameof(injectionRate)); Enabled = enabled ?? throw new ArgumentNullException(nameof(enabled)); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourOptionsSyntax.cs ================================================ using System; using Polly.Contrib.Simmy.Behavior; namespace Polly.Contrib.Simmy { /// /// Fluent API for defining Monkey . /// public partial class MonkeyPolicy { /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// A callback to configure policy options. /// The policy instance. public static AsyncInjectBehaviourPolicy InjectBehaviourAsync(Action configureOptions) { var options = new InjectBehaviourAsyncOptions(); configureOptions(options); if (options.BehaviourInternal == null) throw new ArgumentNullException(nameof(options.BehaviourInternal)); if (options.InjectionRate == null) throw new ArgumentNullException(nameof(options.InjectionRate)); if (options.Enabled == null) throw new ArgumentNullException(nameof(options.Enabled)); return new AsyncInjectBehaviourPolicy(options); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourPolicy.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; namespace Polly.Contrib.Simmy.Behavior { /// /// A policy that injects any custom behaviour before the execution of delegates. /// public class AsyncInjectBehaviourPolicy : AsyncMonkeyPolicy { private readonly Func _behaviour; [Obsolete] internal AsyncInjectBehaviourPolicy(Func behaviour, Func> injectionRate, Func> enabled) : base(injectionRate, enabled) { _behaviour = behaviour ?? throw new ArgumentNullException(nameof(behaviour)); } internal AsyncInjectBehaviourPolicy(InjectBehaviourAsyncOptions options) : base(options.InjectionRate, options.Enabled) { _behaviour = options.BehaviourInternal ?? throw new ArgumentNullException(nameof(options.BehaviourInternal)); } /// protected override Task ImplementationAsync(Func> action, Context context, CancellationToken cancellationToken, bool continueOnCapturedContext) { return AsyncMonkeyEngine.InjectBehaviourImplementationAsync( action, context, cancellationToken, _behaviour, InjectionRate, Enabled, continueOnCapturedContext); } } /// /// A policy that injects any custom behaviour before the execution of delegates returning . /// /// The type of return values this policy will handle. public class AsyncInjectBehaviourPolicy : AsyncMonkeyPolicy { private readonly Func _behaviour; [Obsolete] internal AsyncInjectBehaviourPolicy(Func behaviour, Func> injectionRate, Func> enabled) : base(injectionRate, enabled) { _behaviour = behaviour ?? throw new ArgumentNullException(nameof(behaviour)); } internal AsyncInjectBehaviourPolicy(InjectBehaviourAsyncOptions options) : base(options.InjectionRate, options.Enabled) { _behaviour = options.BehaviourInternal ?? throw new ArgumentNullException(nameof(options.BehaviourInternal)); } /// protected override Task ImplementationAsync(Func> action, Context context, CancellationToken cancellationToken, bool continueOnCapturedContext) { return AsyncMonkeyEngine.InjectBehaviourImplementationAsync( action, context, cancellationToken, _behaviour, InjectionRate, Enabled, continueOnCapturedContext); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourSyntax.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using Polly.Contrib.Simmy.Behavior; namespace Polly.Contrib.Simmy { /// /// Fluent API for defining Monkey . /// public partial class MonkeyPolicy { /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed without context /// The injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free mode /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( Func behaviour, Double injectionRate, Func> enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task BehaviourLambda(Context _, CancellationToken __) => behaviour(); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); Task EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviourAsync(BehaviourLambda, InjectionRateLambda, EnabledLambda); } /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed /// The injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free mode /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( Func behaviour, Double injectionRate, Func> enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); Task EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviourAsync(behaviour, InjectionRateLambda, EnabledLambda); } /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed /// The injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( Func behaviour, Double injectionRate, Func> enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); return InjectBehaviourAsync(behaviour, InjectionRateLambda, enabled); } /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( Func behaviour, Func> injectionRate, Func> enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new AsyncInjectBehaviourPolicy( behaviour, injectionRate, enabled); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourTResultOptionsSyntax.cs ================================================ using System; using Polly.Contrib.Simmy.Behavior; namespace Polly.Contrib.Simmy { /// /// Fluent API for defining Monkey . /// public partial class MonkeyPolicy { /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// A callback to configure policy options. /// The policy instance. public static AsyncInjectBehaviourPolicy InjectBehaviourAsync(Action configureOptions) { var options = new InjectBehaviourAsyncOptions(); configureOptions(options); if (options.BehaviourInternal == null) throw new ArgumentNullException(nameof(options.BehaviourInternal)); if (options.InjectionRate == null) throw new ArgumentNullException(nameof(options.InjectionRate)); if (options.Enabled == null) throw new ArgumentNullException(nameof(options.Enabled)); return new AsyncInjectBehaviourPolicy(options); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourTResultSyntax.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using Polly.Contrib.Simmy.Behavior; namespace Polly.Contrib.Simmy { /// /// Fluent API for defining Monkey . /// public partial class MonkeyPolicy { /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed without context /// The injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free mode /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( Func behaviour, Double injectionRate, Func> enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task BehaviourLambda(Context _, CancellationToken __) => behaviour(); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); Task EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviourAsync(BehaviourLambda, InjectionRateLambda, EnabledLambda); } /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed /// The injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free mode /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( Func behaviour, Double injectionRate, Func> enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); Task EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviourAsync(behaviour, InjectionRateLambda, EnabledLambda); } /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed /// The injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( Func behaviour, Double injectionRate, Func> enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); return InjectBehaviourAsync(behaviour, InjectionRateLambda, enabled); } /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectBehaviourPolicy InjectBehaviourAsync( Func behaviour, Func> injectionRate, Func> enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new AsyncInjectBehaviourPolicy( behaviour, injectionRate, enabled); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Behavior/InjectBehaviourAsyncOptions.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; namespace Polly.Contrib.Simmy.Behavior { /// /// Options used to configure an /// public class InjectBehaviourAsyncOptions : InjectOptionsAsyncBase { /// /// Behaviour Delegate to be executed /// internal Func BehaviourInternal { get; set; } } } ================================================ FILE: src/Polly.Contrib.Simmy/Behavior/InjectBehaviourAsyncOptionsExtensions.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; namespace Polly.Contrib.Simmy.Behavior { /// /// Allows configuration of behaviour for asynchronous monkey behaviour-injection policies. /// public static class InjectBehaviourAsyncOptionsExtensions { /// /// Configure behaviour to inject with the monkey policy. /// /// The configuration object. /// A delegate representing the behaviour to inject. public static InjectBehaviourAsyncOptions Behaviour(this InjectBehaviourAsyncOptions options, Func behaviour) => Behaviour(options, (_, __) => behaviour()); /// /// Configure behaviour to inject with the monkey policy. /// /// The configuration object. /// A delegate representing the behaviour to inject. public static InjectBehaviourAsyncOptions Behaviour(this InjectBehaviourAsyncOptions options, Func behaviour) { options.BehaviourInternal = behaviour; return options; } } } ================================================ FILE: src/Polly.Contrib.Simmy/Behavior/InjectBehaviourOptions.cs ================================================ using System; using System.Threading; namespace Polly.Contrib.Simmy.Behavior { /// /// Options used to configure an /// public class InjectBehaviourOptions : InjectOptionsBase { /// /// Behaviour Delegate to be executed /// internal Action BehaviourInternal { get; set; } } } ================================================ FILE: src/Polly.Contrib.Simmy/Behavior/InjectBehaviourOptionsExtensions.cs ================================================ using System; using System.Threading; namespace Polly.Contrib.Simmy.Behavior { /// /// Allows configuration of behaviour for synchronous monkey behaviour-injection policies. /// public static class InjectBehaviourOptionsExtensions { /// /// Configure behaviour to inject with the monkey policy. /// /// The configuration object. /// A delegate representing the behaviour to inject. public static InjectBehaviourOptions Behaviour(this InjectBehaviourOptions options, Action behaviour) => Behaviour(options, (_, __) => behaviour()); /// /// Configure behaviour to inject with the monkey policy. /// /// The configuration object. /// A delegate representing the behaviour to inject. public static InjectBehaviourOptions Behaviour(this InjectBehaviourOptions options, Action behaviour) { options.BehaviourInternal = behaviour; return options; } } } ================================================ FILE: src/Polly.Contrib.Simmy/Behavior/InjectBehaviourOptionsSyntax.cs ================================================ using System; using Polly.Contrib.Simmy.Behavior; namespace Polly.Contrib.Simmy { /// /// Fluent API for defining Monkey . /// public partial class MonkeyPolicy { /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// A callback to configure policy options. /// The policy instance. public static InjectBehaviourPolicy InjectBehaviour(Action configureOptions) { var options = new InjectBehaviourOptions(); configureOptions(options); if (options.BehaviourInternal == null) throw new ArgumentNullException(nameof(options.BehaviourInternal)); if (options.InjectionRate == null) throw new ArgumentNullException(nameof(options.InjectionRate)); if (options.Enabled == null) throw new ArgumentNullException(nameof(options.Enabled)); return new InjectBehaviourPolicy(options); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Behavior/InjectBehaviourPolicy.cs ================================================ using System; using System.Threading; namespace Polly.Contrib.Simmy.Behavior { /// /// A policy that injects any custom behaviour before the execution of delegates. /// public class InjectBehaviourPolicy : Simmy.MonkeyPolicy { private readonly Action _behaviour; [Obsolete] internal InjectBehaviourPolicy(Action behaviour, Func injectionRate, Func enabled) : base(injectionRate, enabled) { _behaviour = behaviour ?? throw new ArgumentNullException(nameof(behaviour)); } internal InjectBehaviourPolicy(InjectBehaviourOptions options) : base(options.InjectionRate, options.Enabled) { _behaviour = options.BehaviourInternal ?? throw new ArgumentNullException(nameof(options.BehaviourInternal)); } /// protected override TResult Implementation(Func action, Context context, CancellationToken cancellationToken) { return MonkeyEngine.InjectBehaviourImplementation( action, context, cancellationToken, (ctx, ct) => _behaviour(ctx, ct), InjectionRate, Enabled); } } /// /// A policy that injects any custom behaviour before the execution of delegates returning . /// /// The type of return values this policy will handle. public class InjectBehaviourPolicy : MonkeyPolicy { private readonly Action _behaviour; [Obsolete] internal InjectBehaviourPolicy(Action behaviour, Func injectionRate, Func enabled) : base(injectionRate, enabled) { _behaviour = behaviour ?? throw new ArgumentNullException(nameof(behaviour)); } internal InjectBehaviourPolicy(InjectBehaviourOptions options) : base(options.InjectionRate, options.Enabled) { _behaviour = options.BehaviourInternal ?? throw new ArgumentNullException(nameof(options.BehaviourInternal)); } /// protected override TResult Implementation(Func action, Context context, CancellationToken cancellationToken) { return MonkeyEngine.InjectBehaviourImplementation( action, context, cancellationToken, (ctx, ct) => _behaviour(ctx, ct), InjectionRate, Enabled); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Behavior/InjectBehaviourSyntax.cs ================================================ using System; using System.Threading; using Polly.Contrib.Simmy.Behavior; namespace Polly.Contrib.Simmy { /// /// Fluent API for defining Monkey . /// public partial class MonkeyPolicy { /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed without context /// The injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free mode /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectBehaviourPolicy InjectBehaviour( Action behaviour, Double injectionRate, Func enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); void BehaviourLambda(Context _, CancellationToken __) => behaviour(); double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; bool EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviour(BehaviourLambda, InjectionRateLambda, EnabledLambda); } /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed /// The injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free mode /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectBehaviourPolicy InjectBehaviour( Action behaviour, Double injectionRate, Func enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; bool EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviour(behaviour, InjectionRateLambda, EnabledLambda); } /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed /// The injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectBehaviourPolicy InjectBehaviour( Action behaviour, Double injectionRate, Func enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; return InjectBehaviour(behaviour, (Func)InjectionRateLambda, enabled); } /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectBehaviourPolicy InjectBehaviour( Action behaviour, Func injectionRate, Func enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new InjectBehaviourPolicy( behaviour, injectionRate, enabled); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Behavior/InjectBehaviourTResultOptionsSyntax.cs ================================================ using System; using Polly.Contrib.Simmy.Behavior; namespace Polly.Contrib.Simmy { /// /// Fluent API for defining Monkey . /// public partial class MonkeyPolicy { /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// A callback to configure policy options. /// The policy instance. public static InjectBehaviourPolicy InjectBehaviour(Action configureOptions) { var options = new InjectBehaviourOptions(); configureOptions(options); if (options.BehaviourInternal == null) throw new ArgumentNullException(nameof(options.BehaviourInternal)); if (options.InjectionRate == null) throw new ArgumentNullException(nameof(options.InjectionRate)); if (options.Enabled == null) throw new ArgumentNullException(nameof(options.Enabled)); return new InjectBehaviourPolicy(options); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Behavior/InjectBehaviourTResultSyntax.cs ================================================ using System; using System.Threading; using Polly.Contrib.Simmy.Behavior; namespace Polly.Contrib.Simmy { /// /// Fluent API for defining Monkey . /// public partial class MonkeyPolicy { /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed without context /// The injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free mode /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectBehaviourPolicy InjectBehaviour( Action behaviour, Double injectionRate, Func enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); void BehaviourLambda(Context _, CancellationToken __) => behaviour(); double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; bool EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviour(BehaviourLambda, InjectionRateLambda, EnabledLambda); } /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed /// The injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free mode /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectBehaviourPolicy InjectBehaviour( Action behaviour, Double injectionRate, Func enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; bool EnabledLambda(Context _, CancellationToken __) => enabled(); return InjectBehaviour(behaviour, InjectionRateLambda, EnabledLambda); } /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed /// The injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectBehaviourPolicy InjectBehaviour( Action behaviour, Double injectionRate, Func enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; return InjectBehaviour(behaviour, InjectionRateLambda, enabled); } /// /// Builds a which executes a behaviour if returns true and /// a random number is within range of . /// /// Behaviour Delegate to be executed /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectBehaviourPolicy InjectBehaviour( Action behaviour, Func injectionRate, Func enabled) { if (behaviour == null) throw new ArgumentNullException(nameof(behaviour)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new InjectBehaviourPolicy( behaviour, injectionRate, enabled); } } } ================================================ FILE: src/Polly.Contrib.Simmy/IMonkeyPolicy.cs ================================================ namespace Polly.Contrib.Simmy { /// /// Defines properties and methods common to all Monkey policies. /// public interface IMonkeyPolicy : IsPolicy { } /// /// Defines properties and methods common to all Monkey policies generic-typed for executions returning results of type . /// public interface IMonkeyPolicy : IMonkeyPolicy { } } ================================================ FILE: src/Polly.Contrib.Simmy/InjectOptionsAsyncBase.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; namespace Polly.Contrib.Simmy { /// /// Options used to configure a /// public abstract class InjectOptionsAsyncBase { /// /// Lambda to get injection rate between [0, 1] /// internal Func> InjectionRate { get; set; } /// /// Lambda to check if this policy is enabled in current context /// internal Func> Enabled { get; set; } } } ================================================ FILE: src/Polly.Contrib.Simmy/InjectOptionsAsyncBaseExtensions.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using Polly.Contrib.Simmy.Utilities; namespace Polly.Contrib.Simmy { /// /// Allows configuration of when and how often chaos behaviour is injected, for asynchronous monkey policies. /// public static class InjectOptionsAsyncBaseExtensions { /// /// Configure that this monkey policy is enabled. /// /// The configuration object. public static InjectOptionsAsyncBase Enabled(this InjectOptionsAsyncBase options) => Enabled(options, true); /// /// Configure whether this monkey policy is enabled. /// /// The configuration object. /// A boolean value indicating whether the monkey policy is enabled. public static InjectOptionsAsyncBase Enabled(this InjectOptionsAsyncBase options, bool enabled) { options.Enabled = (_, __) => Task.FromResult(enabled); return options; } /// /// Configure when this monkey policy is enabled. /// /// The configuration object. /// A delegate which can be executed to determine whether the monkey policy should be enabled. public static InjectOptionsAsyncBase EnabledWhen(this InjectOptionsAsyncBase options, Func> enabledWhen) { options.Enabled = enabledWhen; return options; } /// /// Configure the rate at which this monkey policy should inject chaos. /// /// The configuration object. /// The injection rate between [0, 1] public static InjectOptionsAsyncBase InjectionRate(this InjectOptionsAsyncBase options, Double injectionRate) { injectionRate.EnsureInjectionThreshold(); options.InjectionRate = (_, __) => Task.FromResult(injectionRate); return options; } /// /// Configure the rate at which this monkey policy should inject chaos. /// /// The configuration object. /// A delegate returning the current rate at which this monkey policy should inject chaos, expressed as double between [0, 1] public static InjectOptionsAsyncBase InjectionRate(this InjectOptionsAsyncBase options, Func> injectionRateProvider) { options.InjectionRate = injectionRateProvider; return options; } } } ================================================ FILE: src/Polly.Contrib.Simmy/InjectOptionsBase.cs ================================================ using System; using System.Threading; namespace Polly.Contrib.Simmy { /// /// Options used to configure a /// public abstract class InjectOptionsBase { /// /// Lambda to get injection rate between [0, 1] /// internal Func InjectionRate { get; set; } /// /// Lambda to check if this policy is enabled in current context /// internal Func Enabled { get; set; } } } ================================================ FILE: src/Polly.Contrib.Simmy/InjectOptionsBaseExtensions.cs ================================================ using Polly.Contrib.Simmy.Utilities; using System; using System.Threading; namespace Polly.Contrib.Simmy { /// /// Allows configuration of when and how often chaos behaviour is injected, for synchronous monkey policies. /// public static class InjectOptionsBaseExtensions { /// /// Configure that this monkey policy is enabled. /// /// The configuration object. public static InjectOptionsBase Enabled(this InjectOptionsBase options) => Enabled(options, true); /// /// Configure whether this monkey policy is enabled. /// /// The configuration object. /// A boolean value indicating whether the monkey policy is enabled. public static InjectOptionsBase Enabled(this InjectOptionsBase options, bool enabled) { options.Enabled = (_, __) => enabled; return options; } /// /// Configure when this monkey policy is enabled. /// /// The configuration object. /// A delegate which can be executed to determine whether the monkey policy should be enabled. public static InjectOptionsBase EnabledWhen(this InjectOptionsBase options, Func enabledWhen) { options.Enabled = enabledWhen; return options; } /// /// Configure the rate at which this monkey policy should inject chaos. /// /// The configuration object. /// The injection rate between [0, 1] public static InjectOptionsBase InjectionRate(this InjectOptionsBase options, Double injectionRate) { injectionRate.EnsureInjectionThreshold(); options.InjectionRate = (_, __) => injectionRate; return options; } /// /// Configure the rate at which this monkey policy should inject chaos. /// /// The configuration object. /// A delegate returning the current rate at which this monkey policy should inject chaos, expressed as double between [0, 1] public static InjectOptionsBase InjectionRate(this InjectOptionsBase options, Func injectionRateProvider) { options.InjectionRate = injectionRateProvider; return options; } } } ================================================ FILE: src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyOptionsSyntax.cs ================================================ using System; using Polly.Contrib.Simmy.Latency; namespace Polly.Contrib.Simmy { /// /// Fluent API for defining Monkey . /// public partial class MonkeyPolicy { /// /// Builds a which injects latency if returns true and /// a random number is within range of . /// /// A callback to configure policy options. /// The policy instance. public static AsyncInjectLatencyPolicy InjectLatencyAsync(Action configureOptions) { var options = new InjectLatencyAsyncOptions(); configureOptions(options); if (options.LatencyInternal == null) throw new ArgumentNullException(nameof(options.LatencyInternal)); if (options.InjectionRate == null) throw new ArgumentNullException(nameof(options.InjectionRate)); if (options.Enabled == null) throw new ArgumentNullException(nameof(options.Enabled)); return new AsyncInjectLatencyPolicy(options); } /// /// Builds a which injects latency if returns true and /// a random number is within range of . /// /// A callback to configure policy options. /// The policy instance. public static AsyncInjectLatencyPolicy InjectLatencyAsync(Action configureOptions) { var options = new InjectLatencyAsyncOptions(); configureOptions(options); if (options.LatencyInternal == null) throw new ArgumentNullException(nameof(options.LatencyInternal)); if (options.InjectionRate == null) throw new ArgumentNullException(nameof(options.InjectionRate)); if (options.Enabled == null) throw new ArgumentNullException(nameof(options.Enabled)); return new AsyncInjectLatencyPolicy(options); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using Polly.Utilities; namespace Polly.Contrib.Simmy.Latency { /// /// A policy that injects latency before the execution of delegates. /// public class AsyncInjectLatencyPolicy : AsyncMonkeyPolicy { private readonly Func> _latencyProvider; [Obsolete] internal AsyncInjectLatencyPolicy( Func> latencyProvider, Func> injectionRate, Func> enabled) : base(injectionRate, enabled) { _latencyProvider = latencyProvider ?? throw new ArgumentNullException(nameof(latencyProvider)); } internal AsyncInjectLatencyPolicy(InjectLatencyAsyncOptions options) : base(options.InjectionRate, options.Enabled) { _latencyProvider = options.LatencyInternal ?? throw new ArgumentNullException(nameof(options.LatencyInternal)); } /// protected override Task ImplementationAsync( Func> action, Context context, CancellationToken cancellationToken, bool continueOnCapturedContext) { return AsyncMonkeyEngine.InjectBehaviourImplementationAsync( action, context, cancellationToken, async (ctx, ct) => { var latency = await _latencyProvider(ctx, cancellationToken).ConfigureAwait(continueOnCapturedContext); // to prevent inject latency if token was signaled on latency configuration delegate. cancellationToken.ThrowIfCancellationRequested(); await SystemClock.SleepAsync( latency, cancellationToken) .ConfigureAwait(continueOnCapturedContext); }, InjectionRate, Enabled, continueOnCapturedContext); } } /// /// A policy that injects latency before the execution of delegates. /// public class AsyncInjectLatencyPolicy : AsyncMonkeyPolicy { private readonly Func> _latencyProvider; [Obsolete] internal AsyncInjectLatencyPolicy( Func> latencyProvider, Func> injectionRate, Func> enabled) : base(injectionRate, enabled) { _latencyProvider = latencyProvider ?? throw new ArgumentNullException(nameof(latencyProvider)); } internal AsyncInjectLatencyPolicy(InjectLatencyAsyncOptions options) : base(options.InjectionRate, options.Enabled) { _latencyProvider = options.LatencyInternal ?? throw new ArgumentNullException(nameof(options.LatencyInternal)); } /// protected override Task ImplementationAsync( Func> action, Context context, CancellationToken cancellationToken, bool continueOnCapturedContext) { return AsyncMonkeyEngine.InjectBehaviourImplementationAsync( action, context, cancellationToken, async (ctx, ct) => { var latency = await _latencyProvider(ctx, cancellationToken).ConfigureAwait(continueOnCapturedContext); // to prevent inject latency if token was signaled on latency configuration delegate. cancellationToken.ThrowIfCancellationRequested(); await SystemClock.SleepAsync( latency, cancellationToken) .ConfigureAwait(continueOnCapturedContext); }, InjectionRate, Enabled, continueOnCapturedContext); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencySyntax.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using Polly.Contrib.Simmy.Latency; namespace Polly.Contrib.Simmy { public partial class MonkeyPolicy { /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// The latency to inject /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free fashion /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectLatencyPolicy InjectLatencyAsync( TimeSpan latency, Double injectionRate, Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task LatencyProvider(Context _, CancellationToken __) => Task.FromResult(latency); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); Task EnabledLambda(Context _, CancellationToken __) => Task.FromResult(enabled()); return new AsyncInjectLatencyPolicy(LatencyProvider, InjectionRateLambda, EnabledLambda); } /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// The latency to inject /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectLatencyPolicy InjectLatencyAsync( TimeSpan latency, Double injectionRate, Func> enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task LatencyProvider(Context _, CancellationToken __) => Task.FromResult(latency); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); return new AsyncInjectLatencyPolicy(LatencyProvider, InjectionRateLambda, enabled); } /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// lambda to get the latency object /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectLatencyPolicy InjectLatencyAsync( TimeSpan latency, Func> injectionRate, Func> enabled) { if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task LatencyProvider(Context _, CancellationToken __) => Task.FromResult(latency); return new AsyncInjectLatencyPolicy(LatencyProvider, injectionRate, enabled); } /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// lambda to get the latency object /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectLatencyPolicy InjectLatencyAsync( Func> latency, Func> injectionRate, Func> enabled) { if (latency == null) throw new ArgumentNullException(nameof(latency)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new AsyncInjectLatencyPolicy(latency, injectionRate, enabled); } } public partial class MonkeyPolicy { /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// lambda to get the latency object /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free fashion /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectLatencyPolicy InjectLatencyAsync( TimeSpan latency, Double injectionRate, Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task LatencyProvider(Context _, CancellationToken __) => Task.FromResult(latency); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); Task EnabledLambda(Context _, CancellationToken __) => Task.FromResult(enabled()); return new AsyncInjectLatencyPolicy(LatencyProvider, InjectionRateLambda, EnabledLambda); } /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// lambda to get the latency object /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectLatencyPolicy InjectLatencyAsync( TimeSpan latency, Double injectionRate, Func> enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task LatencyProvider(Context _, CancellationToken __) => Task.FromResult(latency); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); return new AsyncInjectLatencyPolicy(LatencyProvider, InjectionRateLambda, enabled); } /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// lambda to get the latency object /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectLatencyPolicy InjectLatencyAsync( TimeSpan latency, Func> injectionRate, Func> enabled) { if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task LatencyProvider(Context _, CancellationToken __) => Task.FromResult(latency); return new AsyncInjectLatencyPolicy(LatencyProvider, injectionRate, enabled); } /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// lambda to get the latency object /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectLatencyPolicy InjectLatencyAsync( Func> latency, Func> injectionRate, Func> enabled) { if (latency == null) throw new ArgumentNullException(nameof(latency)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new AsyncInjectLatencyPolicy(latency, injectionRate, enabled); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Latency/InjectLatencyAsyncOptions.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; namespace Polly.Contrib.Simmy.Latency { /// /// Options used to configure an /// public class InjectLatencyAsyncOptions : InjectOptionsAsyncBase { /// /// Latency Delegate to be executed /// internal Func> LatencyInternal { get; set; } } } ================================================ FILE: src/Polly.Contrib.Simmy/Latency/InjectLatencyAsyncOptionsExtensions.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; namespace Polly.Contrib.Simmy.Latency { /// /// Allows configuration of behaviour for asynchronous monkey behaviour-injection policies. /// public static class InjectLatencyAsyncOptionsExtensions { /// /// Configure behaviour to inject with the monkey policy. /// /// The configuration object. /// The latency to inject. public static InjectLatencyAsyncOptions Latency(this InjectLatencyAsyncOptions options, TimeSpan latency) => Latency(options, (_, __) => Task.FromResult(latency)); /// /// Configure behaviour to inject with the monkey policy. /// /// The configuration object. /// A delegate representing the latency to inject. public static InjectLatencyAsyncOptions Latency(this InjectLatencyAsyncOptions options, Func> latency) { options.LatencyInternal = latency; return options; } } } ================================================ FILE: src/Polly.Contrib.Simmy/Latency/InjectLatencyOptions.cs ================================================ using System; using System.Threading; namespace Polly.Contrib.Simmy.Latency { /// /// Options used to configure an /// public class InjectLatencyOptions : InjectOptionsBase { /// /// Latency Delegate to be executed /// internal Func LatencyInternal { get; set; } } } ================================================ FILE: src/Polly.Contrib.Simmy/Latency/InjectLatencyOptionsExtensions.cs ================================================ using System; using System.Threading; namespace Polly.Contrib.Simmy.Latency { /// /// Allows configuration of behaviour for asynchronous monkey behaviour-injection policies. /// public static class InjectLatencyOptionsExtensions { /// /// Configure behaviour to inject with the monkey policy. /// /// The configuration object. /// The latency to inject. public static InjectLatencyOptions Latency(this InjectLatencyOptions options, TimeSpan latency) => Latency(options, (_, __) => latency); /// /// Configure behaviour to inject with the monkey policy. /// /// The configuration object. /// A delegate representing the latency to inject. public static InjectLatencyOptions Latency(this InjectLatencyOptions options, Func latency) { options.LatencyInternal = latency; return options; } } } ================================================ FILE: src/Polly.Contrib.Simmy/Latency/InjectLatencyOptionsSyntax.cs ================================================ using System; using Polly.Contrib.Simmy.Latency; namespace Polly.Contrib.Simmy { /// /// Fluent API for defining Monkey . /// public partial class MonkeyPolicy { /// /// Builds a which injects latency if returns true and /// a random number is within range of . /// /// A callback to configure policy options. /// The policy instance. public static InjectLatencyPolicy InjectLatency(Action configureOptions) { var options = new InjectLatencyOptions(); configureOptions(options); if (options.LatencyInternal == null) throw new ArgumentNullException(nameof(options.LatencyInternal)); if (options.InjectionRate == null) throw new ArgumentNullException(nameof(options.InjectionRate)); if (options.Enabled == null) throw new ArgumentNullException(nameof(options.Enabled)); return new InjectLatencyPolicy(options); } /// /// Builds a which injects latency if returns true and /// a random number is within range of . /// /// A callback to configure policy options. /// The policy instance. public static InjectLatencyPolicy InjectLatency(Action configureOptions) { var options = new InjectLatencyOptions(); configureOptions(options); if (options.LatencyInternal == null) throw new ArgumentNullException(nameof(options.LatencyInternal)); if (options.InjectionRate == null) throw new ArgumentNullException(nameof(options.InjectionRate)); if (options.Enabled == null) throw new ArgumentNullException(nameof(options.Enabled)); return new InjectLatencyPolicy(options); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Latency/InjectLatencyPolicy.cs ================================================ using System; using System.Threading; using Polly.Utilities; namespace Polly.Contrib.Simmy.Latency { /// /// A policy that injects latency before the execution of delegates. /// public class InjectLatencyPolicy : MonkeyPolicy { private readonly Func _latencyProvider; [Obsolete] internal InjectLatencyPolicy( Func latencyProvider, Func injectionRate, Func enabled) : base(injectionRate, enabled) { _latencyProvider = latencyProvider ?? throw new ArgumentNullException(nameof(latencyProvider)); } internal InjectLatencyPolicy(InjectLatencyOptions options) : base(options.InjectionRate, options.Enabled) { _latencyProvider = options.LatencyInternal ?? throw new ArgumentNullException(nameof(options.LatencyInternal)); } /// protected override TResult Implementation(Func action, Context context, CancellationToken cancellationToken) { return MonkeyEngine.InjectBehaviourImplementation( action, context, cancellationToken, (ctx, ct) => { var latency = _latencyProvider(ctx, ct); // to prevent inject latency if token was signaled on latency configuration delegate. cancellationToken.ThrowIfCancellationRequested(); SystemClock.Sleep(latency, cancellationToken); }, InjectionRate, Enabled); } } /// /// A policy that injects latency before the execution of delegates. /// /// The type of return values this policy will handle. public class InjectLatencyPolicy : MonkeyPolicy { private readonly Func _latencyProvider; [Obsolete] internal InjectLatencyPolicy( Func latencyProvider, Func injectionRate, Func enabled) : base(injectionRate, enabled) { _latencyProvider = latencyProvider ?? throw new ArgumentNullException(nameof(latencyProvider)); } internal InjectLatencyPolicy(InjectLatencyOptions options) : base(options.InjectionRate, options.Enabled) { _latencyProvider = options.LatencyInternal ?? throw new ArgumentNullException(nameof(options.LatencyInternal)); } /// protected override TResult Implementation(Func action, Context context, CancellationToken cancellationToken) { return MonkeyEngine.InjectBehaviourImplementation( action, context, cancellationToken, (ctx, ct) => { var latency = _latencyProvider(ctx, ct); // to prevent inject latency if token was signaled on latency configuration delegate. cancellationToken.ThrowIfCancellationRequested(); SystemClock.Sleep(latency, cancellationToken); }, InjectionRate, Enabled); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Latency/InjectLatencySyntax.cs ================================================ using System; using System.Threading; using Polly.Contrib.Simmy.Latency; namespace Polly.Contrib.Simmy { public partial class MonkeyPolicy { /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// The latency to inject /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free fashion /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectLatencyPolicy InjectLatency( TimeSpan latency, Double injectionRate, Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new InjectLatencyPolicy((_, __) => latency, (_, __) => injectionRate, (_, __) => enabled()); } /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// The latency to inject /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectLatencyPolicy InjectLatency( TimeSpan latency, Double injectionRate, Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new InjectLatencyPolicy((_, __) => latency, (_, __) => injectionRate, enabled); } /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// lambda to get the latency object /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectLatencyPolicy InjectLatency( TimeSpan latency, Func injectionRate, Func enabled) { if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new InjectLatencyPolicy((_, __) => latency, injectionRate, enabled); } /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// lambda to get the latency object /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectLatencyPolicy InjectLatency( Func latency, Func injectionRate, Func enabled) { if (latency == null) throw new ArgumentNullException(nameof(latency)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new InjectLatencyPolicy(latency, injectionRate, enabled); } } public partial class MonkeyPolicy { /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// lambda to get the latency object /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free fashion /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectLatencyPolicy InjectLatency( TimeSpan latency, Double injectionRate, Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new InjectLatencyPolicy((_, __) => latency, (_, __) => injectionRate, (_, __) => enabled()); } /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// lambda to get the latency object /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectLatencyPolicy InjectLatency( TimeSpan latency, Double injectionRate, Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new InjectLatencyPolicy((_, __) => latency, (_, __) => injectionRate, enabled); } /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// lambda to get the latency object /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectLatencyPolicy InjectLatency( TimeSpan latency, Func injectionRate, Func enabled) { if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new InjectLatencyPolicy((_, __) => latency, injectionRate, enabled); } /// /// Builds an which injects latency if returns true and /// a random number is within range of . /// /// lambda to get the latency object /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectLatencyPolicy InjectLatency( Func latency, Func injectionRate, Func enabled) { if (latency == null) throw new ArgumentNullException(nameof(latency)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new InjectLatencyPolicy(latency, injectionRate, enabled); } } } ================================================ FILE: src/Polly.Contrib.Simmy/MonkeyEngine.cs ================================================ using System; using System.Threading; using Polly.Contrib.Simmy.Utilities; namespace Polly.Contrib.Simmy { internal static class MonkeyEngine { private static bool ShouldInject(Context context, CancellationToken cancellationToken, Func injectionRate, Func enabled) { // to prevent execute config delegates if token is signaled before to start. cancellationToken.ThrowIfCancellationRequested(); if (!enabled(context, cancellationToken)) { return false; } // to prevent execute injectionRate config delegate if token is signaled on enable configuration delegate. cancellationToken.ThrowIfCancellationRequested(); double injectionThreshold = injectionRate(context, cancellationToken); // to prevent execute further config delegates if token is signaled on injectionRate configuration delegate. cancellationToken.ThrowIfCancellationRequested(); injectionThreshold.EnsureInjectionThreshold(); return ThreadSafeRandom_LockOncePerThread.NextDouble() < injectionThreshold; } internal static TResult InjectBehaviourImplementation( Func action, Context context, CancellationToken cancellationToken, Action injectedBehaviour, Func injectionRate, Func enabled) { if (ShouldInject(context, cancellationToken, injectionRate, enabled)) { injectedBehaviour(context, cancellationToken); } // to prevent execute the user's action if token is signaled on injectedBehaviour delegate. cancellationToken.ThrowIfCancellationRequested(); return action(context, cancellationToken); } internal static TResult InjectExceptionImplementation( Func action, Context context, CancellationToken cancellationToken, Func injectedException, Func injectionRate, Func enabled) { if (ShouldInject(context, cancellationToken, injectionRate, enabled)) { Exception exception = injectedException(context, cancellationToken); // to prevent throws the exception if token is signaled on injectedException configuration delegate. cancellationToken.ThrowIfCancellationRequested(); if (exception != null) { throw exception; } } return action(context, cancellationToken); } internal static TResult InjectResultImplementation( Func action, Context context, CancellationToken cancellationToken, Func injectedResult, Func injectionRate, Func enabled) { if (ShouldInject(context, cancellationToken, injectionRate, enabled)) { return injectedResult(context, cancellationToken); } // to prevent inject the result if token is signaled on injectedResult delegate. cancellationToken.ThrowIfCancellationRequested(); return action(context, cancellationToken); } } } ================================================ FILE: src/Polly.Contrib.Simmy/MonkeyPolicy.cs ================================================ using System; using System.Threading; namespace Polly.Contrib.Simmy { /// /// Contains common functionality for policies which intentionally disrupt sync executions - which monkey around with calls. /// public abstract partial class MonkeyPolicy : Policy, IMonkeyPolicy { internal Func InjectionRate { get; } internal Func Enabled { get; } internal MonkeyPolicy(Func injectionRate, Func enabled) { InjectionRate = injectionRate ?? throw new ArgumentNullException(nameof(injectionRate)); Enabled = enabled ?? throw new ArgumentNullException(nameof(enabled)); } } /// /// Contains common functionality for policies which intentionally disrupt sync executions returning TResult - which monkey around with calls. /// /// The type of return values this policy will handle. public abstract class MonkeyPolicy : Policy, IMonkeyPolicy { internal Func InjectionRate { get; } internal Func Enabled { get; } internal MonkeyPolicy(Func injectionRate, Func enabled) { InjectionRate = injectionRate ?? throw new ArgumentNullException(nameof(injectionRate)); Enabled = enabled ?? throw new ArgumentNullException(nameof(enabled)); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Outcomes/AsyncInjectOutcomeOptionsSyntax.cs ================================================ using System; using Polly.Contrib.Simmy.Outcomes; namespace Polly.Contrib.Simmy { /// /// Fluent API for defining Monkey . /// public partial class MonkeyPolicy { /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// A callback to configure policy options. /// The policy instance. public static AsyncInjectOutcomePolicy InjectExceptionAsync(Action> configureOptions) { var options = new InjectOutcomeAsyncOptions(); configureOptions(options); if (options.Outcome == null) throw new ArgumentNullException(nameof(options.Outcome)); if (options.InjectionRate == null) throw new ArgumentNullException(nameof(options.InjectionRate)); if (options.Enabled == null) throw new ArgumentNullException(nameof(options.Enabled)); return new AsyncInjectOutcomePolicy(options); } /// /// Builds an which injects a result if returns true and /// a random number is within range of . /// /// A callback to configure policy options. /// The policy instance. public static AsyncInjectOutcomePolicy InjectResultAsync(Action> configureOptions) { var options = new InjectOutcomeAsyncOptions(); configureOptions(options); if (options.Outcome == null) throw new ArgumentNullException(nameof(options.Outcome)); if (options.InjectionRate == null) throw new ArgumentNullException(nameof(options.InjectionRate)); if (options.Enabled == null) throw new ArgumentNullException(nameof(options.Enabled)); return new AsyncInjectOutcomePolicy(options); } /// /// Builds an which injects a fault as result if returns true and /// a random number is within range of . /// /// A callback to configure policy options. /// The policy instance. public static AsyncInjectOutcomePolicy InjectResultAsync(Action> configureOptions) { var options = new InjectOutcomeAsyncOptions(); configureOptions(options); if (options.Outcome == null) throw new ArgumentNullException(nameof(options.Outcome)); if (options.InjectionRate == null) throw new ArgumentNullException(nameof(options.InjectionRate)); if (options.Enabled == null) throw new ArgumentNullException(nameof(options.Enabled)); return new AsyncInjectOutcomePolicy(options); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Outcomes/AsyncInjectOutcomePolicy.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; namespace Polly.Contrib.Simmy.Outcomes { /// /// A policy that throws an exception in place of executing the passed delegate. /// The policy can also be configured to return null in place of the exception, to explicitly fake that no exception is thrown. /// public class AsyncInjectOutcomePolicy : AsyncMonkeyPolicy { private readonly Func> _faultProvider; [Obsolete] internal AsyncInjectOutcomePolicy(Func> faultProvider, Func> injectionRate, Func> enabled) : base(injectionRate, enabled) { _faultProvider = faultProvider ?? throw new ArgumentNullException(nameof(faultProvider)); } internal AsyncInjectOutcomePolicy(InjectOutcomeAsyncOptions options) : base(options.InjectionRate, options.Enabled) { _faultProvider = options.Outcome ?? throw new ArgumentNullException(nameof(options.Outcome)); } /// protected override Task ImplementationAsync(Func> action, Context context, CancellationToken cancellationToken, bool continueOnCapturedContext) { return AsyncMonkeyEngine.InjectExceptionImplementationAsync( action, context, cancellationToken, _faultProvider, InjectionRate, Enabled, continueOnCapturedContext); } } /// /// A policy that injects an outcome (throws an exception or returns a specific result), in place of executing the passed delegate. /// public class AsyncInjectOutcomePolicy : AsyncMonkeyPolicy { private readonly Func> _faultProvider; private readonly Func> _resultProvider; [Obsolete] internal AsyncInjectOutcomePolicy(Func> faultProvider, Func> injectionRate, Func> enabled) : base(injectionRate, enabled) { _faultProvider = faultProvider ?? throw new ArgumentNullException(nameof(faultProvider)); } [Obsolete] internal AsyncInjectOutcomePolicy(Func> resultProvider, Func> injectionRate, Func> enabled) : base(injectionRate, enabled) { _resultProvider = resultProvider ?? throw new ArgumentNullException(nameof(resultProvider)); } internal AsyncInjectOutcomePolicy(InjectOutcomeAsyncOptions options) : base(options.InjectionRate, options.Enabled) { _faultProvider = options.Outcome ?? throw new ArgumentNullException(nameof(options.Outcome)); } internal AsyncInjectOutcomePolicy(InjectOutcomeAsyncOptions options) : base(options.InjectionRate, options.Enabled) { _resultProvider = options.Outcome ?? throw new ArgumentNullException(nameof(options.Outcome)); } /// protected override async Task ImplementationAsync(Func> action, Context context, CancellationToken cancellationToken, bool continueOnCapturedContext) { if (_faultProvider != null) { return await AsyncMonkeyEngine.InjectExceptionImplementationAsync( action, context, cancellationToken, _faultProvider, InjectionRate, Enabled, continueOnCapturedContext); } else if (_resultProvider != null) { return await AsyncMonkeyEngine.InjectResultImplementationAsync( action, context, cancellationToken, _resultProvider, InjectionRate, Enabled, continueOnCapturedContext); } else { throw new InvalidOperationException("Either a fault or fake result to inject must be defined."); } } } } ================================================ FILE: src/Polly.Contrib.Simmy/Outcomes/AsyncInjectOutcomeSyntax.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using Polly.Contrib.Simmy.Outcomes; namespace Polly.Contrib.Simmy { public partial class MonkeyPolicy { /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// The fault exception object to throw /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free fashion /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectOutcomePolicy InjectFaultAsync( Exception fault, Double injectionRate, Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task FaultLambda(Context _, CancellationToken __) => Task.FromResult(fault); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); Task EnabledLambda(Context _, CancellationToken __) => Task.FromResult(enabled()); return new AsyncInjectOutcomePolicy(FaultLambda, InjectionRateLambda, EnabledLambda); } /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// The fault exception object to throw /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectOutcomePolicy InjectFaultAsync( Exception fault, Double injectionRate, Func> enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task FaultLambda(Context _, CancellationToken __) => Task.FromResult(fault); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); return new AsyncInjectOutcomePolicy(FaultLambda, InjectionRateLambda, enabled); } /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// lambda to get the fault exception object /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectOutcomePolicy InjectFaultAsync( Func> faultProvider, Func> injectionRate, Func> enabled) { if (faultProvider == null) throw new ArgumentNullException(nameof(faultProvider)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new AsyncInjectOutcomePolicy(faultProvider, injectionRate, enabled); } } public partial class MonkeyPolicy { #region Exception Based Faults /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// The fault exception object to throw /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free fashion /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectOutcomePolicy InjectFaultAsync( Exception fault, Double injectionRate, Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task FaultLambda(Context _, CancellationToken __) => Task.FromResult(fault); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); Task EnabledLambda(Context _, CancellationToken __) => Task.FromResult(enabled()); return new AsyncInjectOutcomePolicy((Func>)FaultLambda, InjectionRateLambda, EnabledLambda); } /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// The fault exception object to throw /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectOutcomePolicy InjectFaultAsync( Exception fault, Double injectionRate, Func> enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task FaultLambda(Context _, CancellationToken __) => Task.FromResult(fault); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); return new AsyncInjectOutcomePolicy((Func>)FaultLambda, InjectionRateLambda, enabled); } /// /// Builds an which executes a fault if returns true and /// a random number is within range of . /// /// lambda to get the fault exception object /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectOutcomePolicy InjectFaultAsync( Func> faultProvider, Func> injectionRate, Func> enabled) { if (faultProvider == null) throw new ArgumentNullException(nameof(faultProvider)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new AsyncInjectOutcomePolicy(faultProvider, injectionRate, enabled); } #endregion #region TResult based Faults /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// The fault exception object to throw /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free fashion /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectOutcomePolicy InjectFaultAsync( TResult fault, Double injectionRate, Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task FaultLambda(Context _, CancellationToken __) => Task.FromResult(fault); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); Task EnabledLambda(Context _, CancellationToken __) { return Task.FromResult(enabled()); } return new AsyncInjectOutcomePolicy((Func>)FaultLambda, InjectionRateLambda, EnabledLambda); } /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// The fault exception object to throw /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectOutcomePolicy InjectFaultAsync( TResult fault, Double injectionRate, Func> enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Task FaultLambda(Context _, CancellationToken __) => Task.FromResult(fault); Task InjectionRateLambda(Context _, CancellationToken __) => Task.FromResult(injectionRate); return new AsyncInjectOutcomePolicy((Func>)FaultLambda, InjectionRateLambda, enabled); } /// /// Builds an which executes a fault if returns true and /// a random number is within range of . /// /// lambda to get the fault exception object /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static AsyncInjectOutcomePolicy InjectFaultAsync( Func> fault, Func> injectionRate, Func> enabled) { if (fault == null) throw new ArgumentNullException(nameof(fault)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new AsyncInjectOutcomePolicy(fault, injectionRate, enabled); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy/Outcomes/InjectOutcomeAsyncOptions.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; namespace Polly.Contrib.Simmy.Outcomes { /// /// Options used to configure an /// public class InjectOutcomeAsyncOptions : InjectOptionsAsyncBase { /// /// Outcome Delegate to be executed /// internal Func> Outcome { get; set; } } } ================================================ FILE: src/Polly.Contrib.Simmy/Outcomes/InjectOutcomeAsyncOptionsExtensions.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; namespace Polly.Contrib.Simmy.Outcomes { /// /// Allows configuration of fault for asynchronous monkey fault-injection policies. /// public static class InjectOutcomeAsyncOptionsExtensions { /// /// Configure fault to inject with the monkey policy. /// /// The configuration object. /// The exception to inject. public static InjectOutcomeAsyncOptions Fault(this InjectOutcomeAsyncOptions options, Exception fault) => Fault(options, (_, __) => Task.FromResult(fault)); /// /// Configure fault to inject with the monkey policy. /// /// The configuration object. /// A delegate representing the fault to inject. public static InjectOutcomeAsyncOptions Fault(this InjectOutcomeAsyncOptions options, Func> fault) { options.Outcome = fault; return options; } /// /// Configure fault to inject with the monkey policy. /// /// The configuration object. /// The result to inject public static InjectOutcomeAsyncOptions Fault(this InjectOutcomeAsyncOptions options, Exception fault) => Fault(options, (_, __) => Task.FromResult(fault)); /// /// Configure fault to inject with the monkey policy. /// /// The configuration object. /// A delegate representing the result to inject. public static InjectOutcomeAsyncOptions Fault(this InjectOutcomeAsyncOptions options, Func> fault) { options.Outcome = fault; return options; } /// /// Configure result to inject with the monkey policy. /// /// The configuration object. /// The result to inject public static InjectOutcomeAsyncOptions Result(this InjectOutcomeAsyncOptions options, TResult result) => Result(options, (_, __) => Task.FromResult(result)); /// /// Configure result to inject with the monkey policy. /// /// The configuration object. /// A delegate representing the result to inject. public static InjectOutcomeAsyncOptions Result(this InjectOutcomeAsyncOptions options, Func> result) { options.Outcome = result; return options; } } } ================================================ FILE: src/Polly.Contrib.Simmy/Outcomes/InjectOutcomeOptions.cs ================================================ using System; using System.Threading; namespace Polly.Contrib.Simmy.Outcomes { /// /// Options used to configure an /// public class InjectOutcomeOptions : InjectOptionsBase { /// /// Outcome Delegate to be executed /// internal Func OutcomeInternal { get; set; } } } ================================================ FILE: src/Polly.Contrib.Simmy/Outcomes/InjectOutcomeOptionsExtensions.cs ================================================ using System; using System.Threading; namespace Polly.Contrib.Simmy.Outcomes { /// /// Allows configuration of fault for synchronous monkey fault-injection policies. /// public static class InjectOutcomeOptionsExtensions { /// /// Configure fault to inject with the monkey policy. /// /// The configuration object. /// The exception to inject. public static InjectOutcomeOptions Fault(this InjectOutcomeOptions options, Exception fault) => Fault(options, (_, __) => fault); /// /// Configure fault to inject with the monkey policy. /// /// The configuration object. /// A delegate representing the fault to inject. public static InjectOutcomeOptions Fault(this InjectOutcomeOptions options, Func fault) { options.OutcomeInternal = fault; return options; } /// /// Configure fault to inject with the monkey policy. /// /// The configuration object. /// The result to inject public static InjectOutcomeOptions Fault(this InjectOutcomeOptions options, Exception fault) => Fault(options, (_, __) => fault); /// /// Configure fault to inject with the monkey policy. /// /// The configuration object. /// A delegate representing the result to inject. public static InjectOutcomeOptions Fault(this InjectOutcomeOptions options, Func fault) { options.OutcomeInternal = fault; return options; } /// /// Configure result to inject with the monkey policy. /// /// The configuration object. /// The result to inject public static InjectOutcomeOptions Result(this InjectOutcomeOptions options, TResult result) => Result(options, (_, __) => result); /// /// Configure result to inject with the monkey policy. /// /// The configuration object. /// A delegate representing the result to inject. public static InjectOutcomeOptions Result(this InjectOutcomeOptions options, Func result) { options.OutcomeInternal = result; return options; } } } ================================================ FILE: src/Polly.Contrib.Simmy/Outcomes/InjectOutcomeOptionsSyntax.cs ================================================ using System; using Polly.Contrib.Simmy.Outcomes; namespace Polly.Contrib.Simmy { /// /// Fluent API for defining Monkey . /// public partial class MonkeyPolicy { /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// A callback to configure policy options. /// The policy instance. public static InjectOutcomePolicy InjectException(Action> configureOptions) { var options = new InjectOutcomeOptions(); configureOptions(options); if (options.OutcomeInternal == null) throw new ArgumentNullException(nameof(options.OutcomeInternal)); if (options.InjectionRate == null) throw new ArgumentNullException(nameof(options.InjectionRate)); if (options.Enabled == null) throw new ArgumentNullException(nameof(options.Enabled)); return new InjectOutcomePolicy(options); } /// /// Builds an which injects a result if returns true and /// a random number is within range of . /// /// A callback to configure policy options. /// The policy instance. public static InjectOutcomePolicy InjectResult(Action> configureOptions) { var options = new InjectOutcomeOptions(); configureOptions(options); if (options.OutcomeInternal == null) throw new ArgumentNullException(nameof(options.OutcomeInternal)); if (options.InjectionRate == null) throw new ArgumentNullException(nameof(options.InjectionRate)); if (options.Enabled == null) throw new ArgumentNullException(nameof(options.Enabled)); return new InjectOutcomePolicy(options); } /// /// Builds an which injects a fault as result if returns true and /// a random number is within range of . /// /// A callback to configure policy options. /// The policy instance. public static InjectOutcomePolicy InjectResult(Action> configureOptions) { var options = new InjectOutcomeOptions(); configureOptions(options); if (options.OutcomeInternal == null) throw new ArgumentNullException(nameof(options.OutcomeInternal)); if (options.InjectionRate == null) throw new ArgumentNullException(nameof(options.InjectionRate)); if (options.Enabled == null) throw new ArgumentNullException(nameof(options.Enabled)); return new InjectOutcomePolicy(options); } } } ================================================ FILE: src/Polly.Contrib.Simmy/Outcomes/InjectOutcomePolicy.cs ================================================ using System; using System.Threading; namespace Polly.Contrib.Simmy.Outcomes { /// /// A policy that throws an exception in place of executing the passed delegate. /// The policy can also be configured to return null in place of the exception, to explicitly fake that no exception is thrown. /// public class InjectOutcomePolicy : MonkeyPolicy { private readonly Func _faultProvider; [Obsolete] internal InjectOutcomePolicy(Func faultProvider, Func injectionRate, Func enabled) : base(injectionRate, enabled) { _faultProvider = faultProvider ?? throw new ArgumentNullException(nameof(faultProvider)); } internal InjectOutcomePolicy(InjectOutcomeOptions options) : base(options.InjectionRate, options.Enabled) { _faultProvider = options.OutcomeInternal ?? throw new ArgumentNullException(nameof(options.OutcomeInternal)); } /// protected override TResult Implementation(Func action, Context context, CancellationToken cancellationToken) { return MonkeyEngine.InjectExceptionImplementation( action, context, cancellationToken, _faultProvider, InjectionRate, Enabled); } } /// /// A policy that injects an outcome (throws an exception or returns a specific result), in place of executing the passed delegate. /// public class InjectOutcomePolicy : MonkeyPolicy { private readonly Func _faultProvider; private readonly Func _resultProvider; [Obsolete] internal InjectOutcomePolicy(Func faultProvider, Func injectionRate, Func enabled) : base(injectionRate, enabled) { _faultProvider = faultProvider ?? throw new ArgumentNullException(nameof(faultProvider)); } [Obsolete] internal InjectOutcomePolicy(Func resultProvider, Func injectionRate, Func enabled) : base(injectionRate, enabled) { _resultProvider = resultProvider ?? throw new ArgumentNullException(nameof(resultProvider)); } internal InjectOutcomePolicy(InjectOutcomeOptions options) : base(options.InjectionRate, options.Enabled) { _faultProvider = options.OutcomeInternal ?? throw new ArgumentNullException(nameof(options.OutcomeInternal)); } internal InjectOutcomePolicy(InjectOutcomeOptions options) : base(options.InjectionRate, options.Enabled) { _resultProvider = options.OutcomeInternal ?? throw new ArgumentNullException(nameof(options.OutcomeInternal)); } /// protected override TResult Implementation(Func action, Context context, CancellationToken cancellationToken) { if (_faultProvider != null) { return MonkeyEngine.InjectExceptionImplementation( action, context, cancellationToken, _faultProvider, InjectionRate, Enabled); } else if (_resultProvider != null) { return MonkeyEngine.InjectResultImplementation( action, context, cancellationToken, _resultProvider, InjectionRate, Enabled); } else { throw new InvalidOperationException("Either a fault or fake result to inject must be defined."); } } } } ================================================ FILE: src/Polly.Contrib.Simmy/Outcomes/InjectOutcomeSyntax.cs ================================================ using System; using System.Threading; using Polly.Contrib.Simmy.Outcomes; namespace Polly.Contrib.Simmy { public partial class MonkeyPolicy { /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// The fault exception object to throw /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free fashion /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectOutcomePolicy InjectFault( Exception fault, Double injectionRate, Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Exception FaultLambda(Context _, CancellationToken __) => fault; double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; bool EnabledLambda(Context _, CancellationToken __) => enabled(); return new InjectOutcomePolicy(FaultLambda, InjectionRateLambda, EnabledLambda); } /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// The fault exception object to throw /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectOutcomePolicy InjectFault( Exception fault, Double injectionRate, Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Exception FaultLambda(Context _, CancellationToken __) => fault; double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; return new InjectOutcomePolicy(FaultLambda, InjectionRateLambda, enabled); } /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// lambda to get the fault exception object /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectOutcomePolicy InjectFault( Func faultProvider, Func injectionRate, Func enabled) { if (faultProvider == null) throw new ArgumentNullException(nameof(faultProvider)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new InjectOutcomePolicy(faultProvider, injectionRate, enabled); } } public partial class MonkeyPolicy { #region Exception Based Faults /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// The fault exception object to throw /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free fashion /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectOutcomePolicy InjectFault( Exception fault, Double injectionRate, Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Exception FaultLambda(Context _, CancellationToken __) => fault; double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; bool EnabledLambda(Context _, CancellationToken __) => enabled(); return new InjectOutcomePolicy((Func)FaultLambda, InjectionRateLambda, EnabledLambda); } /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// The fault exception object to throw /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectOutcomePolicy InjectFault( Exception fault, Double injectionRate, Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); Exception FaultLambda(Context _, CancellationToken __) => fault; double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; return new InjectOutcomePolicy((Func)FaultLambda, InjectionRateLambda, enabled); } /// /// Builds an which executes a fault if returns true and /// a random number is within range of . /// /// lambda to get the fault exception object /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectOutcomePolicy InjectFault( Func faultProvider, Func injectionRate, Func enabled) { if (faultProvider == null) throw new ArgumentNullException(nameof(faultProvider)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new InjectOutcomePolicy(faultProvider, injectionRate, enabled); } #endregion #region TResult Based Faults /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// The fault result object to inject /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in context free fashion /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectOutcomePolicy InjectFault( TResult fault, Double injectionRate, Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); TResult FaultLambda(Context _, CancellationToken __) => fault; double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; bool EnabledLambda(Context _, CancellationToken __) => enabled(); return new InjectOutcomePolicy((Func)FaultLambda, InjectionRateLambda, EnabledLambda); } /// /// Builds an which injects a fault if returns true and /// a random number is within range of . /// /// The fault result object to inject /// injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectOutcomePolicy InjectFault( TResult fault, Double injectionRate, Func enabled) { if (enabled == null) throw new ArgumentNullException(nameof(enabled)); TResult FaultLambda(Context _, CancellationToken __) => fault; double InjectionRateLambda(Context _, CancellationToken __) => injectionRate; return new InjectOutcomePolicy((Func)FaultLambda, InjectionRateLambda, enabled); } /// /// Builds an which executes a fault if returns true and /// a random number is within range of . /// /// The fault result object to inject /// lambda to get injection rate between [0, 1] /// Lambda to check if this policy is enabled in current context /// The policy instance. [Obsolete("This overload is going to be deprecated, use the overload which takes Action instead.")] public static InjectOutcomePolicy InjectFault( Func faultProvider, Func injectionRate, Func enabled) { if (faultProvider == null) throw new ArgumentNullException(nameof(faultProvider)); if (injectionRate == null) throw new ArgumentNullException(nameof(injectionRate)); if (enabled == null) throw new ArgumentNullException(nameof(enabled)); return new InjectOutcomePolicy(faultProvider, injectionRate, enabled); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy/Polly.Contrib.Simmy.csproj ================================================  netstandard1.1;netstandard2.0;netstandard2.1 Polly.Contrib.Simmy Polly.Contrib.Simmy 0.1.0 0.0.0.0 0.1.0.0 0.1.0.0 0.1.0 App vNext Copyright (c) 2019, App vNext Simmy is a chaos-engineering and fault-injection tool, integrating with the Polly resilience project for .NET en-US true true App vNext full pdbonly true 1.6.1 <_Parameter1>Simmy.Specs ================================================ FILE: src/Polly.Contrib.Simmy/Utilities/GuardExtensions.cs ================================================ using System; namespace Polly.Contrib.Simmy.Utilities { internal static class GuardExtensions { public static void EnsureInjectionThreshold(this double injectionThreshold) { if (injectionThreshold < 0) { throw new ArgumentOutOfRangeException(nameof(injectionThreshold), "Injection rate/threshold in Monkey policies should always be a double between [0, 1]; never a negative number."); } if (injectionThreshold > 1) { throw new ArgumentOutOfRangeException(nameof(injectionThreshold), "Injection rate/threshold in Monkey policies should always be a double between [0, 1]; never a number greater than 1."); } } } } ================================================ FILE: src/Polly.Contrib.Simmy/Utilities/ThreadSafeRandom_LockOncePerThread.cs ================================================ using System; using System.Threading; // Note: Given the current .NET Core (2.1) and .NET Framework (4.7.2) implementations of Random, // this implementation intentionally sacrifices a (marginal) degree of randomness in favour of speed, by locking only once-per-thread. // An implementation locking once-per-random-number-generated will generate a marginally more random sequence. // This implementation intentionally favours execution speed over that marginal extra degree of randomness. // We choose that trade-off where the random number generator is used to select executions for chaos/fault injection, where we want minimum extra drag per execution. // In other scenarios such as randomizing for jitter in retries, the Polly project may (with a different implementation) favour randomness at the expense of locking once-per-random-number-generated. // References: // - https://stackoverflow.com/a/25448166/ // - https://docs.microsoft.com/en-us/dotnet/api/system.random?view=netframework-4.7.2#the-systemrandom-class-and-thread-safety // - https://stackoverflow.com/questions/25390301/ // - https://github.com/App-vNext/Polly/issues/530#issuecomment-439680613 namespace Polly.Contrib.Simmy.Utilities { /// /// An implementation of a Random Number Generator that is thread-safe for generating random numbers concurrently on multiple threads. /// Thread-safety without locking-per-random-number-generated is achieved simply by storing and using a ThreadStatic instance of per thread. /// public class ThreadSafeRandom_LockOncePerThread { /// /// Initializes the static , setting the method to a thread-safe implementation which locks only once-per-thread. /// static ThreadSafeRandom_LockOncePerThread() { Reset(); } private static readonly Random s_globalRandom = new Random(); private static readonly ThreadLocal t_threadRandom = new ThreadLocal(InitializeThreadRandom); private static Random InitializeThreadRandom() { int seed; // We must lock minimally once-per-thread. If the instance s_globalRandom is accessed on multiple threads concurrently, // the current .NET Framework and .NET Core implementations of Random have a liability to return sequences of zeros. // See the articles referenced at the head of this class. lock (s_globalRandom) { seed = s_globalRandom.Next(); } return new Random(seed); } /// /// Returns a random floating point number that is greater than or equal to 0.0, and less than 1.0. /// public static Func NextDouble; /// /// Method to reset the random generator. /// public static void Reset() { NextDouble = () => t_threadRandom.Value.NextDouble(); } } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Behavior/InjectBehaviourAsyncSpecs.cs ================================================ using System; using System.Threading.Tasks; using FluentAssertions; using Polly.Contrib.Simmy.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Behavior { [Collection(Helpers.Constants.AmbientContextDependentTestCollection)] [Obsolete] public class InjectBehaviourAsyncSpecs : IDisposable { public InjectBehaviourAsyncSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } [Fact] public void Given_not_enabled_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(() => { injectedBehaviourExecuted = true; return Task.CompletedTask; }, 0.6, () => Task.FromResult(false)); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.CompletedTask; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Given_enabled_and_randomly_within_threshold_should_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(() => { injectedBehaviourExecuted = true; return Task.CompletedTask; }, 0.6, () => Task.FromResult(true)); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.CompletedTask; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } [Fact] public void Given_enabled_and_randomly_not_within_threshold_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(() => { injectedBehaviourExecuted = true; return Task.CompletedTask; }, 0.4, () => Task.FromResult(true)); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.CompletedTask; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Should_inject_behaviour_before_executing_user_delegate() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(() => { userDelegateExecuted.Should().BeFalse(); // Not yet executed at the time the injected behaviour runs. injectedBehaviourExecuted = true; return Task.CompletedTask; }, 0.6, () => Task.FromResult(true)); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.CompletedTask; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Behavior/InjectBehaviourAsyncWithOptionsSpecs.cs ================================================ using FluentAssertions; using Polly.Contrib.Simmy.Utilities; using System; using System.Threading.Tasks; using Polly.Contrib.Simmy.Behavior; using Xunit; namespace Polly.Contrib.Simmy.Specs.Behavior { [Collection(Helpers.Constants.AmbientContextDependentTestCollection)] public class InjectBehaviourAsyncWithOptionsSpecs : IDisposable { public InjectBehaviourAsyncWithOptionsSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } [Fact] public void Given_not_enabled_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => { injectedBehaviourExecuted = true; return Task.CompletedTask; }) .InjectionRate(0.6) .Enabled(false) ); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.CompletedTask; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Given_enabled_and_randomly_within_threshold_should_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => { injectedBehaviourExecuted = true; return Task.CompletedTask; }) .InjectionRate(0.6) .Enabled() ); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.CompletedTask; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } [Fact] public void Given_enabled_and_randomly_not_within_threshold_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => { injectedBehaviourExecuted = true; return Task.CompletedTask; }) .InjectionRate(0.4) .Enabled(false) ); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.CompletedTask; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Should_inject_behaviour_before_executing_user_delegate() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => { userDelegateExecuted.Should().BeFalse(); // Not yet executed at the time the injected behaviour runs. injectedBehaviourExecuted = true; return Task.CompletedTask; }) .InjectionRate(0.6) .Enabled() ); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.CompletedTask; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } #region invalid threshold on configuration and execution time [Fact] public void Should_throw_error_on_configuration_time_when_threshold_is_negative() { Action act = () => MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => Task.CompletedTask) .Enabled() .InjectionRate(-1) ); act.ShouldThrow(); } [Fact] public void Should_throw_error_on_configuration_time_when_threshold_is_greater_than_one() { Action act = () => MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => Task.CompletedTask) .Enabled() .InjectionRate(1.1) ); act.ShouldThrow(); } [Fact] public void Should_throw_error_on_execution_time_when_threshold_is_is_negative() { var policy = MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => Task.CompletedTask) .Enabled() .InjectionRate((_, __) => Task.FromResult(-1d)) ); policy.Awaiting(async x => await x.ExecuteAsync(() => Task.CompletedTask)) .ShouldThrow(); } [Fact] public void Should_throw_error_on_execution_time_when_threshold_is_greater_than_one() { var policy = MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => Task.CompletedTask) .Enabled() .InjectionRate((_, __) => Task.FromResult(1.1)) ); policy.Awaiting(async x => await x.ExecuteAsync(() => Task.CompletedTask)) .ShouldThrow(); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Behavior/InjectBehaviourSpecs.cs ================================================ using System; using FluentAssertions; using Polly.Contrib.Simmy.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Behavior { [Collection(Helpers.Constants.AmbientContextDependentTestCollection)] [Obsolete] public class InjectBehaviourSpecs : IDisposable { public InjectBehaviourSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } [Fact] public void Given_not_enabled_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(() => { injectedBehaviourExecuted = true; }, 0.6, () => false); policy.Execute(() => { userDelegateExecuted = true; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Given_enabled_and_randomly_within_threshold_should_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(() => { injectedBehaviourExecuted = true; }, 0.6, () => true); policy.Execute(() => { userDelegateExecuted = true; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } [Fact] public void Given_enabled_and_randomly_not_within_threshold_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(() => { injectedBehaviourExecuted = true; }, 0.4, () => true); policy.Execute(() => { userDelegateExecuted = true; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Should_inject_behaviour_before_executing_user_delegate() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(() => { userDelegateExecuted.Should().BeFalse(); // Not yet executed at the time the injected behaviour runs. injectedBehaviourExecuted = true; }, 0.6, () => true); policy.Execute(() => { userDelegateExecuted = true; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Behavior/InjectBehaviourTResultAsyncSpecs.cs ================================================ using System; using System.Threading.Tasks; using FluentAssertions; using Polly.Contrib.Simmy.Specs.Helpers; using Polly.Contrib.Simmy.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Behavior { [Collection(Constants.AmbientContextDependentTestCollection)] [Obsolete] public class InjectBehaviourTResultAsyncSpecs : IDisposable { public InjectBehaviourTResultAsyncSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } [Fact] public void Given_not_enabled_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(() => { injectedBehaviourExecuted = true; return Task.CompletedTask; }, 0.6, () => Task.FromResult(false)); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.FromResult(ResultPrimitive.Good); }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Given_enabled_and_randomly_within_threshold_should_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(() => { injectedBehaviourExecuted = true; return Task.CompletedTask; }, 0.6, () => Task.FromResult(true)); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.FromResult(ResultPrimitive.Good); }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } [Fact] public void Given_enabled_and_randomly_not_within_threshold_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(() => { injectedBehaviourExecuted = true; return Task.CompletedTask; }, 0.4, () => Task.FromResult(true)); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.FromResult(ResultPrimitive.Good); }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Should_inject_behaviour_before_executing_user_delegate() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(() => { userDelegateExecuted.Should().BeFalse(); // Not yet executed at the time the injected behaviour runs. injectedBehaviourExecuted = true; return Task.CompletedTask; }, 0.6, () => Task.FromResult(true)); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.FromResult(ResultPrimitive.Good); }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Behavior/InjectBehaviourTResultAsyncWithOptionsSpecs.cs ================================================ using System; using System.Threading.Tasks; using Polly.Contrib.Simmy.Behavior; using Polly.Contrib.Simmy.Specs.Helpers; using Polly.Contrib.Simmy.Utilities; using Xunit; using FluentAssertions; namespace Polly.Contrib.Simmy.Specs.Behavior { [Collection(Helpers.Constants.AmbientContextDependentTestCollection)] public class InjectBehaviourTResultAsyncWithOptionsSpecs : IDisposable { public InjectBehaviourTResultAsyncWithOptionsSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } [Fact] public void Given_not_enabled_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => { injectedBehaviourExecuted = true; return Task.CompletedTask; }) .InjectionRate(0.6) .Enabled(false) ); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.FromResult(ResultPrimitive.Good); }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Given_enabled_and_randomly_within_threshold_should_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => { injectedBehaviourExecuted = true; return Task.CompletedTask; }) .InjectionRate(0.6) .Enabled() ); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.FromResult(ResultPrimitive.Good); }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } [Fact] public void Given_enabled_and_randomly_not_within_threshold_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => { injectedBehaviourExecuted = true; return Task.CompletedTask; }) .InjectionRate(0.4) .Enabled(false) ); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.FromResult(ResultPrimitive.Good); }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Should_inject_behaviour_before_executing_user_delegate() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => { userDelegateExecuted.Should().BeFalse(); // Not yet executed at the time the injected behaviour runs. injectedBehaviourExecuted = true; return Task.CompletedTask; }) .InjectionRate(0.6) .Enabled() ); policy.ExecuteAsync(() => { userDelegateExecuted = true; return Task.FromResult(ResultPrimitive.Good); }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } #region invalid threshold on configuration and execution time [Fact] public void Should_throw_error_on_configuration_time_when_threshold_is_negative() { Action act = () => MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => Task.CompletedTask) .Enabled() .InjectionRate(-1) ); act.ShouldThrow(); } [Fact] public void Should_throw_error_on_configuration_time_when_threshold_is_greater_than_one() { Action act = () => MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => Task.CompletedTask) .Enabled() .InjectionRate(1.1) ); act.ShouldThrow(); } [Fact] public void Should_throw_error_on_execution_time_when_threshold_is_is_negative() { var policy = MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => Task.CompletedTask) .Enabled() .InjectionRate((_, __) => Task.FromResult(-1d)) ); policy.Awaiting(async x => await x.ExecuteAsync(() => Task.FromResult(ResultPrimitive.Good))) .ShouldThrow(); } [Fact] public void Should_throw_error_on_execution_time_when_threshold_is_greater_than_one() { var policy = MonkeyPolicy.InjectBehaviourAsync(with => with.Behaviour(() => Task.CompletedTask) .Enabled() .InjectionRate((_, __) => Task.FromResult(1.1)) ); policy.Awaiting(async x => await x.ExecuteAsync(() => Task.FromResult(ResultPrimitive.Good))) .ShouldThrow(); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Behavior/InjectBehaviourTResultSpecs.cs ================================================ using System; using FluentAssertions; using Polly.Contrib.Simmy.Specs.Helpers; using Polly.Contrib.Simmy.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Behavior { [Collection(Constants.AmbientContextDependentTestCollection)] [Obsolete] public class InjectBehaviourTResultSpecs : IDisposable { public InjectBehaviourTResultSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } [Fact] public void Given_not_enabled_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(() => { injectedBehaviourExecuted = true; }, 0.6, () => false); policy.Execute(() => { userDelegateExecuted = true; return ResultPrimitive.Good; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Given_enabled_and_randomly_within_threshold_should_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(() => { injectedBehaviourExecuted = true; }, 0.6, () => true); policy.Execute(() => { userDelegateExecuted = true; return ResultPrimitive.Good; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } [Fact] public void Given_enabled_and_randomly_not_within_threshold_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(() => { injectedBehaviourExecuted = true; }, 0.4, () => true); policy.Execute(() => { userDelegateExecuted = true; return ResultPrimitive.Good; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Should_inject_behaviour_before_executing_user_delegate() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(() => { userDelegateExecuted.Should().BeFalse(); // Not yet executed at the time the injected behaviour runs. injectedBehaviourExecuted = true; }, 0.6, () => true); policy.Execute(() => { userDelegateExecuted = true; return ResultPrimitive.Good; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Behavior/InjectBehaviourTResultWithOptionsSpecs.cs ================================================ using System; using FluentAssertions; using Polly.Contrib.Simmy.Behavior; using Polly.Contrib.Simmy.Specs.Helpers; using Polly.Contrib.Simmy.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Behavior { [Collection(Helpers.Constants.AmbientContextDependentTestCollection)] public class InjectBehaviourTResultWithOptionsSpecs : IDisposable { public InjectBehaviourTResultWithOptionsSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } [Fact] public void Given_not_enabled_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { injectedBehaviourExecuted = true; }) .InjectionRate(0.6) .Enabled(false)); policy.Execute(() => { userDelegateExecuted = true; return ResultPrimitive.Good; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Given_enabled_and_randomly_within_threshold_should_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { injectedBehaviourExecuted = true; }) .InjectionRate(0.6) .Enabled()); policy.Execute(() => { userDelegateExecuted = true; return ResultPrimitive.Good; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } [Fact] public void Given_enabled_and_randomly_not_within_threshold_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { injectedBehaviourExecuted = true; }) .InjectionRate(0.4) .Enabled()); policy.Execute(() => { userDelegateExecuted = true; return ResultPrimitive.Good; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Should_inject_behaviour_before_executing_user_delegate() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { userDelegateExecuted.Should().BeFalse(); // Not yet executed at the time the injected behaviour runs. injectedBehaviourExecuted = true; }) .InjectionRate(0.6) .Enabled()); policy.Execute(() => { userDelegateExecuted = true; return ResultPrimitive.Good; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } #region invalid threshold on configuration and execution time [Fact] public void Should_throw_error_on_configuration_time_when_threshold_is_negative() { Action act = () => MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { }) .Enabled() .InjectionRate(-1) ); act.ShouldThrow(); } [Fact] public void Should_throw_error_on_configuration_time_when_threshold_is_greater_than_one() { Action act = () => MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { }) .Enabled() .InjectionRate(1.1) ); act.ShouldThrow(); } [Fact] public void Should_throw_error_on_execution_time_when_threshold_is_is_negative() { var policy = MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { }) .Enabled() .InjectionRate((_, __) => -1d) ); policy.Invoking(x => x.Execute(() => ResultPrimitive.Good)) .ShouldThrow(); } [Fact] public void Should_throw_error_on_execution_time_when_threshold_is_greater_than_one() { var policy = MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { }) .Enabled() .InjectionRate((_, __) => 1.1) ); policy.Invoking(x => x.Execute(() => ResultPrimitive.Good)) .ShouldThrow(); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Behavior/InjectBehaviourWithOptionsSpecs.cs ================================================ using System; using FluentAssertions; using Polly.Contrib.Simmy.Behavior; using Polly.Contrib.Simmy.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Behavior { [Collection(Helpers.Constants.AmbientContextDependentTestCollection)] public class InjectBehaviourWithOptionsSpecs : IDisposable { public InjectBehaviourWithOptionsSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } [Fact] public void Given_not_enabled_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { injectedBehaviourExecuted = true; }) .InjectionRate(0.6) .Enabled(false)); policy.Execute(() => { userDelegateExecuted = true; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Given_enabled_and_randomly_within_threshold_should_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { injectedBehaviourExecuted = true; }) .InjectionRate(0.6) .Enabled()); policy.Execute(() => { userDelegateExecuted = true; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } [Fact] public void Given_enabled_and_randomly_not_within_threshold_should_not_inject_behaviour() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { injectedBehaviourExecuted = true; }) .InjectionRate(0.4) .Enabled()); policy.Execute(() => { userDelegateExecuted = true; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeFalse(); } [Fact] public void Should_inject_behaviour_before_executing_user_delegate() { Boolean userDelegateExecuted = false; Boolean injectedBehaviourExecuted = false; var policy = MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { userDelegateExecuted.Should().BeFalse(); // Not yet executed at the time the injected behaviour runs. injectedBehaviourExecuted = true; }) .InjectionRate(0.6) .Enabled()); policy.Execute(() => { userDelegateExecuted = true; }); userDelegateExecuted.Should().BeTrue(); injectedBehaviourExecuted.Should().BeTrue(); } #region invalid threshold on configuration and execution time [Fact] public void Should_throw_error_on_configuration_time_when_threshold_is_negative() { Action act = () => MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { }) .Enabled() .InjectionRate(-1) ); act.ShouldThrow(); } [Fact] public void Should_throw_error_on_configuration_time_when_threshold_is_greater_than_one() { Action act = () => MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { }) .Enabled() .InjectionRate(1.1) ); act.ShouldThrow(); } [Fact] public void Should_throw_error_on_execution_time_when_threshold_is_is_negative() { var policy = MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { }) .Enabled() .InjectionRate((_, __) => -1d) ); policy.Invoking(x => x.Execute(() => { })) .ShouldThrow(); } [Fact] public void Should_throw_error_on_execution_time_when_threshold_is_greater_than_one() { var policy = MonkeyPolicy.InjectBehaviour(with => with.Behaviour(() => { }) .Enabled() .InjectionRate((_, __) => 1.1) ); policy.Invoking(x => x.Execute(() => { })) .ShouldThrow(); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Helpers/Constants.cs ================================================ namespace Polly.Contrib.Simmy.Specs.Helpers { /// /// Constants supporting tests. /// public class Constants { /// /// Used to identify an xUnit test collection dependent on manipulating some ambient context. /// Tests in such collections are not parallelized, which prevents one test polluting another when ambient context is manipulated. /// public const string AmbientContextDependentTestCollection = "AmbientContextDependentTestCollection"; } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Helpers/ResultPrimitive.cs ================================================ namespace Polly.Contrib.Simmy.Specs.Helpers { /// /// A helper class supporting tests on how Policy<TResult> policies may handle return results which are primitive types such as ints or enums. /// internal enum ResultPrimitive { Undefined, Fault, Good, FaultAgain, GoodAgain, FaultYetAgain, Substitute, WhateverButTooLate } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncSpecs.cs ================================================ using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using FluentAssertions; using Polly.Contrib.Simmy.Utilities; using Polly.Timeout; using Polly.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Latency { [Collection(Helpers.Constants.AmbientContextDependentTestCollection)] [Obsolete] public class InjectLatencyAsyncSpecs : IDisposable { private int _totalTimeSlept = 0; public InjectLatencyAsyncSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; SystemClock.SleepAsync = async (span, ct) => _totalTimeSlept += await Task.FromResult(span.Milliseconds); } public void Dispose() { _totalTimeSlept = 0; SystemClock.Reset(); ThreadSafeRandom_LockOncePerThread.Reset(); } #region Context Free [Fact] public async Task InjectLatency_Context_Free_Should_Introduce_Delay_If_Enabled() { var delay = TimeSpan.FromMilliseconds(500); var policy = MonkeyPolicy.InjectLatencyAsync(delay, 0.6, () => true); var executed = false; Func actionAsync = () => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_Dissabled() { var policy = MonkeyPolicy.InjectLatencyAsync(TimeSpan.FromMilliseconds(500), 0.6, () => false); var executed = false; Func actionAsync = () => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_Context_Free_Should_Introduce_Delay_If_InjectionRate_Is_Covered() { var delay = TimeSpan.FromMilliseconds(500); var policy = MonkeyPolicy.InjectLatencyAsync(delay, 0.6, () => true); var executed = false; Func actionAsync = () => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var policy = MonkeyPolicy.InjectLatencyAsync(delay, 0.3, () => true); var executed = false; Func actionAsync = () => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } #endregion #region With Context [Fact] public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatencyAsync(delay, 0.6, enabled); Boolean executed = false; Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = false; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatencyAsync(delay, 0.6, enabled); Boolean executed = false; Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatencyAsync(delay, 0.3, enabled); Boolean executed = false; Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; context["InjectionRate"] = 0.6; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; var policy = MonkeyPolicy.InjectLatencyAsync(delay, injectionRate, enabled); Boolean executed = false; Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; context["InjectionRate"] = 0.3; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; var policy = MonkeyPolicy.InjectLatencyAsync(delay, injectionRate, enabled); Boolean executed = false; Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); Boolean executed = false; Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = false; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); Boolean executed = false; Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.3; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); Boolean executed = false; Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } #endregion #region Cancellable scenarios [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); Func actionAsync = (_, ct) => { executed = true; return TaskHelper.EmptyTask; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; Func actionAsync = (_, ct) => { executed = true; return TaskHelper.EmptyTask; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { cts.Cancel(); return await Task.FromResult((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Boolean executed = false; Func actionAsync = (_, ct) => { executed = true; return TaskHelper.EmptyTask; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { cts.Cancel(); if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_latency_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Boolean executed = false; Func actionAsync = (_, ct) => { executed = true; return TaskHelper.EmptyTask; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Func> latencyProvider = async (ctx, ct) => { cts.Cancel(); if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Theory] [InlineData(TimeoutStrategy.Optimistic)] [InlineData(TimeoutStrategy.Pessimistic)] public void InjectLatency_With_Context_Should_not_inject_the_whole_latency_if_user_cancelationtoken_is_signaled_from_timeout(TimeoutStrategy timeoutStrategy) { SystemClock.Reset(); var timeout = TimeSpan.FromSeconds(5); var delay = TimeSpan.FromSeconds(10); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Boolean executed = false; Stopwatch watch = new Stopwatch(); using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func actionAsync = (_, ct) => { executed = true; return TaskHelper.EmptyTask; }; var policy = Policy.TimeoutAsync(timeout, timeoutStrategy) .WrapAsync(MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled)); watch.Start(); policy.Awaiting(async x => { await x.ExecuteAsync(actionAsync, context, cts.Token); }) .ShouldThrow(); watch.Stop(); } executed.Should().BeFalse(); watch.Elapsed.Should().BeCloseTo(timeout, ((int)TimeSpan.FromSeconds(3).TotalMilliseconds)); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyAsyncWithOptionsSpecs.cs ================================================ using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using FluentAssertions; using Polly.Contrib.Simmy.Latency; using Polly.Contrib.Simmy.Utilities; using Polly.Utilities; using Xunit; using Polly.Timeout; namespace Polly.Contrib.Simmy.Specs.Latency { [Collection(Helpers.Constants.AmbientContextDependentTestCollection)] public class InjectLatencyAsyncWithOptionsSpecs : IDisposable { private int _totalTimeSlept = 0; public InjectLatencyAsyncWithOptionsSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; SystemClock.SleepAsync = async (span, ct) => _totalTimeSlept += await Task.FromResult(span.Milliseconds); } public void Dispose() { _totalTimeSlept = 0; SystemClock.Reset(); ThreadSafeRandom_LockOncePerThread.Reset(); } #region Context Free [Fact] public async Task InjectLatency_Context_Free_Should_Introduce_Delay_If_Enabled() { var delay = TimeSpan.FromMilliseconds(500); var executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(0.6) .Enabled() ); Func actionAsync = () => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_Dissabled() { var executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(TimeSpan.FromMilliseconds(500)) .InjectionRate(0.6) .Enabled(false) ); Func actionAsync = () => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_Context_Free_Should_Introduce_Delay_If_InjectionRate_Is_Covered() { var delay = TimeSpan.FromMilliseconds(500); var executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(0.6) .Enabled() ); Func actionAsync = () => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(0.3) .Enabled() ); Func actionAsync = () => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } #endregion #region With Context [Fact] public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(0.6) .EnabledWhen(enabled) ); Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = false }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(0.6) .EnabledWhen(enabled) ); Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(0.3) .EnabledWhen(enabled) ); Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true, ["InjectionRate"] = 0.3 }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = false, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.3 }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } #endregion #region Cancellable scenarios [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; Func actionAsync = (_, ct) => { executed = true; return TaskHelper.EmptyTask; }; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; Func actionAsync = (_, ct) => { executed = true; return TaskHelper.EmptyTask; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { cts.Cancel(); return await Task.FromResult((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Boolean executed = false; Func actionAsync = (_, ct) => { executed = true; return TaskHelper.EmptyTask; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { cts.Cancel(); if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_latency_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Boolean executed = false; Func actionAsync = (_, ct) => { executed = true; return TaskHelper.EmptyTask; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Func> latencyProvider = async (ctx, ct) => { cts.Cancel(); if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Theory] [InlineData(TimeoutStrategy.Optimistic)] [InlineData(TimeoutStrategy.Pessimistic)] public void InjectLatency_With_Context_Should_not_inject_the_whole_latency_if_user_cancelationtoken_is_signaled_from_timeout(TimeoutStrategy timeoutStrategy) { SystemClock.Reset(); var timeout = TimeSpan.FromSeconds(5); var delay = TimeSpan.FromSeconds(10); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Boolean executed = false; Stopwatch watch = new Stopwatch(); using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func actionAsync = (_, ct) => { executed = true; return TaskHelper.EmptyTask; }; var policy = Policy.TimeoutAsync(timeout, timeoutStrategy) .WrapAsync( MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) )); watch.Start(); policy.Awaiting(async x => { await x.ExecuteAsync(actionAsync, context, cts.Token); }) .ShouldThrow(); watch.Stop(); } executed.Should().BeFalse(); watch.Elapsed.Should().BeCloseTo(timeout, ((int)TimeSpan.FromSeconds(3).TotalMilliseconds)); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencySpecs.cs ================================================ using System; using System.Diagnostics; using System.Threading; using FluentAssertions; using Polly.Contrib.Simmy.Utilities; using Polly.Timeout; using Polly.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Latency { [Collection(Helpers.Constants.AmbientContextDependentTestCollection)] [Obsolete] public class InjectLatencySpecs : IDisposable { private int _totalTimeSlept = 0; public InjectLatencySpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; SystemClock.Sleep = (span, ct) => _totalTimeSlept += span.Milliseconds; } public void Dispose() { _totalTimeSlept = 0; SystemClock.Reset(); ThreadSafeRandom_LockOncePerThread.Reset(); } #region Context Free [Fact] public void InjectLatency_Context_Free_Should_Introduce_Delay_If_Enabled() { var delay = TimeSpan.FromMilliseconds(500); var policy = MonkeyPolicy.InjectLatency(delay, 0.6, () => true); var executed = false; policy.Execute(() => { executed = true; }); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_Dissabled() { var policy = MonkeyPolicy.InjectLatency(TimeSpan.FromMilliseconds(500), 0.6, () => false); var executed = false; policy.Execute(() => { executed = true; }); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_Context_Free_Should_Introduce_Delay_If_InjectionRate_Is_Covered() { var delay = TimeSpan.FromMilliseconds(500); var policy = MonkeyPolicy.InjectLatency(delay, 0.6, () => true); var executed = false; policy.Execute(() => { executed = true; }); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var policy = MonkeyPolicy.InjectLatency(delay, 0.3, () => true); var executed = false; policy.Execute(() => { executed = true; }); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } #endregion #region With Context [Fact] public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatency(delay, 0.6, enabled); Boolean executed = false; policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = false; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatency(delay, 0.6, enabled); Boolean executed = false; policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatency(delay, 0.3, enabled); Boolean executed = false; policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; context["InjectionRate"] = 0.6; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; var policy = MonkeyPolicy.InjectLatency(delay, injectionRate, enabled); Boolean executed = false; policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; context["InjectionRate"] = 0.3; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; var policy = MonkeyPolicy.InjectLatency(delay, injectionRate, enabled); Boolean executed = false; policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_Latency_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); Boolean executed = false; policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = false; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); Boolean executed = false; policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.3; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); Boolean executed = false; policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } #endregion #region Cancellable scenarios [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { cts.Cancel(); return (bool)ctx["Enabled"]; }; var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Boolean executed = false; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { cts.Cancel(); if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_latency_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Boolean executed = false; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func latencyProvider = (ctx, ct) => { cts.Cancel(); if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Theory] [InlineData(TimeoutStrategy.Optimistic)] [InlineData(TimeoutStrategy.Pessimistic)] public void InjectLatency_With_Context_Should_not_inject_the_whole_latency_if_user_cancelationtoken_is_signaled_from_timeout(TimeoutStrategy timeoutStrategy) { SystemClock.Reset(); var timeout = TimeSpan.FromSeconds(5); var delay = TimeSpan.FromSeconds(10); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Boolean executed = false; Stopwatch watch = new Stopwatch(); using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; var policy = Policy.Timeout(timeout, timeoutStrategy) .Wrap(MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled)); watch.Start(); policy.Invoking(x => { x.Execute((ctx, ct) => { executed = true; }, context, cts.Token); }) .ShouldThrow(); watch.Stop(); } executed.Should().BeFalse(); watch.Elapsed.Should().BeCloseTo(timeout, ((int)TimeSpan.FromSeconds(3).TotalMilliseconds)); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultAsyncSpecs .cs ================================================ using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using FluentAssertions; using Polly.Utilities; using Polly.Contrib.Simmy.Specs.Helpers; using Polly.Contrib.Simmy.Utilities; using Polly.Timeout; using Xunit; namespace Polly.Contrib.Simmy.Specs.Latency { [Collection(Constants.AmbientContextDependentTestCollection)] [Obsolete] public class InjectLatencyTResultAsyncSpecs : IDisposable { private int _totalTimeSlept = 0; public InjectLatencyTResultAsyncSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; SystemClock.SleepAsync = async (span, ct) => _totalTimeSlept += await Task.FromResult(span.Milliseconds); } public void Dispose() { _totalTimeSlept = 0; SystemClock.Reset(); ThreadSafeRandom_LockOncePerThread.Reset(); } #region Context Free [Fact] public async Task InjectLatency_Context_Free_Should_Introduce_Delay_If_Enabled() { var delay = TimeSpan.FromMilliseconds(500); var policy = MonkeyPolicy.InjectLatencyAsync(delay, 0.6, () => true); var executed = false; Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_Dissabled() { var policy = MonkeyPolicy.InjectLatencyAsync(TimeSpan.FromMilliseconds(500), 0.6, () => false); var executed = false; Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_Context_Free_Should_Introduce_Delay_If_InjectionRate_Is_Covered() { var delay = TimeSpan.FromMilliseconds(500); var policy = MonkeyPolicy.InjectLatencyAsync(delay, 0.6, () => true); var executed = false; Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var policy = MonkeyPolicy.InjectLatencyAsync(delay, 0.3, () => true); var executed = false; Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } #endregion #region With Context [Fact] public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatencyAsync(delay, 0.6, enabled); Boolean executed = false; Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = false; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatencyAsync(delay, 0.6, enabled); Boolean executed = false; Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatencyAsync(delay, 0.3, enabled); Boolean executed = false; Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; context["InjectionRate"] = 0.6; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; var policy = MonkeyPolicy.InjectLatencyAsync(delay, injectionRate, enabled); Boolean executed = false; Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; context["InjectionRate"] = 0.3; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; var policy = MonkeyPolicy.InjectLatencyAsync(delay, injectionRate, enabled); Boolean executed = false; Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); Boolean executed = false; Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = false; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); Boolean executed = false; Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.3; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); Boolean executed = false; Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } #endregion #region Cancellable scenarios [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); Func> actionAsync = (_, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; Func> actionAsync = (_, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { cts.Cancel(); return await Task.FromResult((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Boolean executed = false; Func> actionAsync = (_, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { cts.Cancel(); if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_latency_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Boolean executed = false; Func> actionAsync = (_, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Func> latencyProvider = async (ctx, ct) => { cts.Cancel(); if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; var policy = MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Theory] [InlineData(TimeoutStrategy.Optimistic)] [InlineData(TimeoutStrategy.Pessimistic)] public void InjectLatency_With_Context_Should_not_inject_the_whole_latency_if_user_cancelationtoken_is_signaled_from_timeout(TimeoutStrategy timeoutStrategy) { SystemClock.Reset(); var timeout = TimeSpan.FromSeconds(5); var delay = TimeSpan.FromSeconds(10); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Boolean executed = false; Stopwatch watch = new Stopwatch(); using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> actionAsync = (_, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var policy = Policy.TimeoutAsync(timeout, timeoutStrategy) .WrapAsync(MonkeyPolicy.InjectLatencyAsync(latencyProvider, injectionRate, enabled)); watch.Start(); policy.Awaiting(async x => { await x.ExecuteAsync(actionAsync, context, cts.Token); }) .ShouldThrow(); watch.Stop(); } executed.Should().BeFalse(); watch.Elapsed.Should().BeCloseTo(timeout, ((int)TimeSpan.FromSeconds(3).TotalMilliseconds)); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultAsyncWithOptionsSpecs.cs ================================================ using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Polly.Contrib.Simmy.Specs.Helpers; using Polly.Contrib.Simmy.Utilities; using Polly.Utilities; using Xunit; using FluentAssertions; using Polly.Contrib.Simmy.Latency; using Polly.Timeout; namespace Polly.Contrib.Simmy.Specs.Latency { [Collection(Constants.AmbientContextDependentTestCollection)] public class InjectLatencyTResultAsyncWithOptionsSpecs : IDisposable { private int _totalTimeSlept = 0; public InjectLatencyTResultAsyncWithOptionsSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; SystemClock.SleepAsync = async (span, ct) => _totalTimeSlept += await Task.FromResult(span.Milliseconds); } public void Dispose() { _totalTimeSlept = 0; SystemClock.Reset(); ThreadSafeRandom_LockOncePerThread.Reset(); } #region Context Free [Fact] public async Task InjectLatency_Context_Free_Should_Introduce_Delay_If_Enabled() { var delay = TimeSpan.FromMilliseconds(500); var executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(0.6) .Enabled() ); Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_Dissabled() { var executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(TimeSpan.FromMilliseconds(500)) .InjectionRate(0.6) .Enabled(false) ); Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_Context_Free_Should_Introduce_Delay_If_InjectionRate_Is_Covered() { var delay = TimeSpan.FromMilliseconds(500); var executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(0.6) .Enabled() ); Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(0.3) .Enabled() ); Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } #endregion #region With Context [Fact] public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(0.6) .EnabledWhen(enabled) ); Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = false }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(0.6) .EnabledWhen(enabled) ); Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(0.3) .EnabledWhen(enabled) ); Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true, ["InjectionRate"] = 0.3 }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(delay) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = false, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public async Task InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.3 }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func> actionAsync = _ => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var result = await policy.ExecuteAsync(actionAsync, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } #endregion #region Cancellable scenarios [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; Func> actionAsync = (_, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Boolean executed = false; Func> actionAsync = (_, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { cts.Cancel(); return await Task.FromResult((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Boolean executed = false; Func> actionAsync = (_, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { cts.Cancel(); if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_latency_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Boolean executed = false; Func> actionAsync = (_, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Func> latencyProvider = async (ctx, ct) => { cts.Cancel(); if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; var policy = MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Theory] [InlineData(TimeoutStrategy.Optimistic)] [InlineData(TimeoutStrategy.Pessimistic)] public void InjectLatency_With_Context_Should_not_inject_the_whole_latency_if_user_cancelationtoken_is_signaled_from_timeout(TimeoutStrategy timeoutStrategy) { SystemClock.Reset(); var timeout = TimeSpan.FromSeconds(5); var delay = TimeSpan.FromSeconds(10); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Boolean executed = false; Stopwatch watch = new Stopwatch(); using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = async (ctx, ct) => { return await Task.FromResult((bool)ctx["Enabled"]); }; Func> injectionRate = async (ctx, ct) => { if (ctx["InjectionRate"] != null) { return await Task.FromResult((double)ctx["InjectionRate"]); } return await Task.FromResult(0); }; Func> latencyProvider = async (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return await Task.FromResult(delay); } return await Task.FromResult(TimeSpan.FromMilliseconds(0)); }; Func> actionAsync = (_, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var policy = Policy.TimeoutAsync(timeout, timeoutStrategy) .WrapAsync( MonkeyPolicy.InjectLatencyAsync(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) )); watch.Start(); policy.Awaiting(async x => { await x.ExecuteAsync(actionAsync, context, cts.Token); }) .ShouldThrow(); watch.Stop(); } executed.Should().BeFalse(); watch.Elapsed.Should().BeCloseTo(timeout, ((int)TimeSpan.FromSeconds(3).TotalMilliseconds)); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultSpecs.cs ================================================ using System; using System.Diagnostics; using System.Threading; using FluentAssertions; using Polly.Utilities; using Polly.Contrib.Simmy.Specs.Helpers; using Polly.Contrib.Simmy.Utilities; using Xunit; using Polly.Timeout; namespace Polly.Contrib.Simmy.Specs.Latency { [Collection(Constants.AmbientContextDependentTestCollection)] [Obsolete] public class InjectLatencyTResultSpecs : IDisposable { private int _totalTimeSlept = 0; public InjectLatencyTResultSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; SystemClock.Sleep = (span, ct) => _totalTimeSlept += span.Milliseconds; } public void Dispose() { _totalTimeSlept = 0; SystemClock.Reset(); ThreadSafeRandom_LockOncePerThread.Reset(); } #region Context Free [Fact] public void InjectLatency_Context_Free_Should_Introduce_Delay_If_Enabled() { var delay = TimeSpan.FromMilliseconds(500); var policy = MonkeyPolicy.InjectLatency(delay, 0.6, () => true); var executed = false; Func action = () => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_Dissabled() { var policy = MonkeyPolicy.InjectLatency(TimeSpan.FromMilliseconds(500), 0.6, () => false); var executed = false; Func action = () => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_Context_Free_Should_Introduce_Delay_If_InjectionRate_Is_Covered() { var delay = TimeSpan.FromMilliseconds(500); var policy = MonkeyPolicy.InjectLatency(delay, 0.6, () => true); var executed = false; Func action = () => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var policy = MonkeyPolicy.InjectLatency(delay, 0.3, () => true); var executed = false; Func action = () => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } #endregion #region With Context [Fact] public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatency(delay, 0.6, enabled); Boolean executed = false; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = false; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatency(delay, 0.6, enabled); Boolean executed = false; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; var policy = MonkeyPolicy.InjectLatency(delay, 0.3, enabled); Boolean executed = false; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; context["InjectionRate"] = 0.6; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; var policy = MonkeyPolicy.InjectLatency(delay, injectionRate, enabled); Boolean executed = false; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["Enabled"] = true; context["InjectionRate"] = 0.3; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; var policy = MonkeyPolicy.InjectLatency(delay, injectionRate, enabled); Boolean executed = false; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_Latency_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); Boolean executed = false; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = false; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); Boolean executed = false; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.3; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); Boolean executed = false; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } #endregion #region Cancellable scenarios [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Invoking(x => x.Execute(action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { cts.Cancel(); return (bool)ctx["Enabled"]; }; var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); policy.Invoking(x => x.Execute(action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Boolean executed = false; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { cts.Cancel(); if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); policy.Invoking(x => x.Execute(action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_latency_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Boolean executed = false; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func latencyProvider = (ctx, ct) => { cts.Cancel(); if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; var policy = MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled); Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; policy.Invoking(x => x.Execute(action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Theory] [InlineData(TimeoutStrategy.Optimistic)] [InlineData(TimeoutStrategy.Pessimistic)] public void InjectLatency_With_Context_Should_not_inject_the_whole_latency_if_user_cancelationtoken_is_signaled_from_timeout(TimeoutStrategy timeoutStrategy) { SystemClock.Reset(); var timeout = TimeSpan.FromSeconds(5); var delay = TimeSpan.FromSeconds(10); var context = new Context(); context["ShouldInjectLatency"] = true; context["Enabled"] = true; context["InjectionRate"] = 0.6; Boolean executed = false; Stopwatch watch = new Stopwatch(); using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; var policy = Policy.Timeout(timeout, timeoutStrategy) .Wrap(MonkeyPolicy.InjectLatency(latencyProvider, injectionRate, enabled)); watch.Start(); policy.Invoking(x => { x.Execute(action, context, cts.Token); }) .ShouldThrow(); watch.Stop(); } executed.Should().BeFalse(); watch.Elapsed.Should().BeCloseTo(timeout, ((int)TimeSpan.FromSeconds(3).TotalMilliseconds)); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyTResultWithOptionsSpecs.cs ================================================ using System; using System.Diagnostics; using FluentAssertions; using Polly.Contrib.Simmy.Specs.Helpers; using Polly.Contrib.Simmy.Utilities; using Polly.Utilities; using Xunit; using System.Threading; using Polly.Contrib.Simmy.Latency; using Polly.Timeout; namespace Polly.Contrib.Simmy.Specs.Latency { [Collection(Constants.AmbientContextDependentTestCollection)] public class InjectLatencyTResultWithOptionsSpecs : IDisposable { private int _totalTimeSlept = 0; public InjectLatencyTResultWithOptionsSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; SystemClock.Sleep = (span, ct) => _totalTimeSlept += span.Milliseconds; } public void Dispose() { _totalTimeSlept = 0; SystemClock.Reset(); ThreadSafeRandom_LockOncePerThread.Reset(); } #region Context Free [Fact] public void InjectLatency_Context_Free_Should_Introduce_Delay_If_Enabled() { var delay = TimeSpan.FromMilliseconds(500); var executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(0.6) .Enabled() ); Func action = () => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_Dissabled() { var executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(TimeSpan.FromMilliseconds(500)) .InjectionRate(0.6) .Enabled(false) ); Func action = () => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_Context_Free_Should_Introduce_Delay_If_InjectionRate_Is_Covered() { var delay = TimeSpan.FromMilliseconds(500); var executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(0.6) .Enabled() ); Func action = () => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(0.3) .Enabled() ); Func action = () => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } #endregion #region With Context [Fact] public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(0.6) .EnabledWhen(enabled) ); Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = false }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(0.6) .EnabledWhen(enabled) ); Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(0.3) .EnabledWhen(enabled) ); Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true, ["InjectionRate"] = 0.3 }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_Latency_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = false, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); result.Should().Be(ResultPrimitive.Good); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.3 }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var result = policy.Execute(action, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } #endregion #region Cancellable scenarios [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Invoking(x => x.Execute(action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { cts.Cancel(); return (bool)ctx["Enabled"]; }; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute(action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Boolean executed = false; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { cts.Cancel(); if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute(action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_latency_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Boolean executed = false; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func latencyProvider = (ctx, ct) => { cts.Cancel(); if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; policy.Invoking(x => x.Execute(action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Theory] [InlineData(TimeoutStrategy.Optimistic)] [InlineData(TimeoutStrategy.Pessimistic)] public void InjectLatency_With_Context_Should_not_inject_the_whole_latency_if_user_cancelationtoken_is_signaled_from_timeout(TimeoutStrategy timeoutStrategy) { SystemClock.Reset(); var timeout = TimeSpan.FromSeconds(5); var delay = TimeSpan.FromSeconds(10); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Boolean executed = false; Stopwatch watch = new Stopwatch(); using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; var policy = Policy.Timeout(timeout, timeoutStrategy) .Wrap( MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) )); watch.Start(); policy.Invoking(x => { x.Execute(action, context, cts.Token); }) .ShouldThrow(); watch.Stop(); } executed.Should().BeFalse(); watch.Elapsed.Should().BeCloseTo(timeout, ((int)TimeSpan.FromSeconds(3).TotalMilliseconds)); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Latency/InjectLatencyWithOptionsSpecs.cs ================================================ using System; using System.Diagnostics; using System.Threading; using FluentAssertions; using Polly.Contrib.Simmy.Latency; using Polly.Contrib.Simmy.Utilities; using Polly.Utilities; using Xunit; using Polly.Timeout; namespace Polly.Contrib.Simmy.Specs.Latency { [Collection(Helpers.Constants.AmbientContextDependentTestCollection)] public class InjectLatencyWithOptionsSpecs : IDisposable { private int _totalTimeSlept = 0; public InjectLatencyWithOptionsSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; SystemClock.Sleep = (span, ct) => _totalTimeSlept += span.Milliseconds; } public void Dispose() { _totalTimeSlept = 0; SystemClock.Reset(); ThreadSafeRandom_LockOncePerThread.Reset(); } #region Context Free [Fact] public void InjectLatency_Context_Free_Should_Introduce_Delay_If_Enabled() { var delay = TimeSpan.FromMilliseconds(500); var executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(0.6) .Enabled() ); policy.Execute(() => { executed = true; }); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_Dissabled() { var executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(TimeSpan.FromMilliseconds(500)) .InjectionRate(0.6) .Enabled(false) ); policy.Execute(() => { executed = true; }); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_Context_Free_Should_Introduce_Delay_If_InjectionRate_Is_Covered() { var delay = TimeSpan.FromMilliseconds(500); var executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(0.6) .Enabled() ); policy.Execute(() => { executed = true; }); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_Context_Free_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(0.3) .Enabled() ); policy.Execute(() => { executed = true; }); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } #endregion #region With Context [Fact] public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(0.6) .EnabledWhen(enabled) ); policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = false }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(0.6) .EnabledWhen(enabled) ); policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_Enabled_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(0.3) .EnabledWhen(enabled) ); policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_With_Context_With_InjectionRate_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["Enabled"] = true, ["InjectionRate"] = 0.3 }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(delay) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_Latency_Lambda_Should_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(delay.Milliseconds); } [Fact] public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = false, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_With_Latency_Lambda_Should_Not_Introduce_Delay_If_InjectionRate_Is_Not_Covered() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.3 }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return ((bool)ctx["Enabled"]); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Execute((ctx) => { executed = true; }, context); executed.Should().BeTrue(); _totalTimeSlept.Should().Be(0); } #endregion #region Cancellable scenarios [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Boolean executed = false; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { cts.Cancel(); return (bool)ctx["Enabled"]; }; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; Boolean executed = false; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { cts.Cancel(); if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Fact] public void InjectLatency_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_latency_config_delegate() { var delay = TimeSpan.FromMilliseconds(500); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Boolean executed = false; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func latencyProvider = (ctx, ct) => { cts.Cancel(); if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; var policy = MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); _totalTimeSlept.Should().Be(0); } [Theory] [InlineData(TimeoutStrategy.Optimistic)] [InlineData(TimeoutStrategy.Pessimistic)] public void InjectLatency_With_Context_Should_not_inject_the_whole_latency_if_user_cancelationtoken_is_signaled_from_timeout(TimeoutStrategy timeoutStrategy) { SystemClock.Reset(); var timeout = TimeSpan.FromSeconds(5); var delay = TimeSpan.FromSeconds(10); var context = new Context { ["ShouldInjectLatency"] = true, ["Enabled"] = true, ["InjectionRate"] = 0.6 }; Boolean executed = false; Stopwatch watch = new Stopwatch(); using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["Enabled"]; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func latencyProvider = (ctx, ct) => { if ((bool)ctx["ShouldInjectLatency"]) { return delay; } return TimeSpan.FromMilliseconds(0); }; var policy = Policy.Timeout(timeout, timeoutStrategy) .Wrap( MonkeyPolicy.InjectLatency(with => with.Latency(latencyProvider) .InjectionRate(injectionRate) .EnabledWhen(enabled) )); watch.Start(); policy.Invoking(x => { x.Execute((ctx, ct) => { executed = true; }, context, cts.Token); }) .ShouldThrow(); watch.Stop(); } executed.Should().BeFalse(); watch.Elapsed.Should().BeCloseTo(timeout, ((int)TimeSpan.FromSeconds(3).TotalMilliseconds)); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Outcomes/InjectFaultAsyncSpecs.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using FluentAssertions; using Polly.Contrib.Simmy.Utilities; using Polly.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Outcomes { [Collection(Helpers.Constants.AmbientContextDependentTestCollection)] [Obsolete] public class InjectFaultAsyncSpecs : IDisposable { public InjectFaultAsyncSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } #region Basic Overload, Exception, Context Free [Fact] public void InjectFault_Context_Free_Enabled_Should_not_execute_user_delegate_async() { string exceptionMessage = "exceptionMessage"; Exception fault = new Exception(exceptionMessage); var policy = MonkeyPolicy.InjectFaultAsync(fault, 0.6, () => true); Boolean executed = false; Func actionAsync = () => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync)).ShouldThrowExactly().WithMessage(exceptionMessage); executed.Should().BeFalse(); } [Fact] public void InjectFault_Context_Free_Enabled_Should_execute_user_delegate_async() { Exception fault = new Exception(); var policy = MonkeyPolicy.InjectFaultAsync(fault, 0.3, () => true); Boolean executed = false; Func actionAsync = () => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync)).ShouldNotThrow(); executed.Should().BeTrue(); } [Fact] public void InjectFault_Context_Free_Enabled_Should_execute_user_delegate_not_throw_if_injected_fault_is_permitted_null() { Exception fault = null; var policy = MonkeyPolicy.InjectFaultAsync(fault, 0.3, () => true); Boolean executed = false; Func actionAsync = () => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync)).ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Basic Overload, Exception, With Context [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_async() { Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; var policy = MonkeyPolicy.InjectFaultAsync( new Exception("test"), 0.6, async (ctx, ct) => { return await Task.FromResult((bool)ctx["ShouldFail"]); }); Func actionAsync = (_) => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)).ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_execute_user_delegate_async() { Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; var policy = MonkeyPolicy.InjectFaultAsync( new Exception("test"), 0.4, async (ctx, ct) => { return await Task.FromResult((bool)ctx["ShouldFail"]); }); Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)).ShouldNotThrow(); executed.Should().BeTrue(); } [Fact] public void InjectFault_With_Context_Should_execute_user_delegate_async_with_enabled_lambda_false() { Boolean executed = false; Context context = new Context(); context["ShouldFail"] = false; var policy = MonkeyPolicy.InjectFaultAsync( new Exception("test"), 0.6, async (ctx, ct) => { return await Task.FromResult((bool)ctx["ShouldFail"]); }); Func actionAsync = (_) => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)).ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Overload, All based on context [Fact] public void InjectFault_should_throw_if_injection_rate_is_out_of_range_too_low() { Boolean executed = false; Context context = new Context(); Func> fault = (ctx, cts) => Task.FromResult(new Exception()); Func> enabled = (ctx, ct) => Task.FromResult(true); Func> injectionRate = (ctx, ct) => Task.FromResult(-0.1); var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); Func actionAsync = (_) => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)).ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_should_throw_if_injection_rate_is_out_of_range_too_high() { Boolean executed = false; Context context = new Context(); Func> fault = (ctx, cts) => Task.FromResult(new Exception()); Func> enabled = (ctx, ct) => Task.FromResult(true); Func> injectionRate = (ctx, ct) => Task.FromResult(1.01); var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); Func actionAsync = (_) => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)).ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_async_basic() { Boolean executed = false; Context context = new Context(); Func> fault = (ctx, cts) => Task.FromResult(new Exception()); Func> enabled = (ctx, ct) => Task.FromResult(true); Func> injectionRate = (ctx, ct) => Task.FromResult(0.6); var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); Func actionAsync = (_) => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)).ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_async_with_all_context_set() { Boolean executed = false; Context context = new Context(); string failureMessage = "Failure Message"; context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); Func actionAsync = (_) => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)) .ShouldThrowExactly() .WithMessage(failureMessage); executed.Should().BeFalse(); } #endregion #region Cancellable scenarios [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func actionAsync = (ctx, ct) => { executed = true; return TaskHelper.EmptyTask; }; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func actionAsync = (ctx, ct) => { executed = true; return TaskHelper.EmptyTask; }; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = (ctx, ct) => { cts.Cancel(); return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func actionAsync = (ctx, ct) => { executed = true; return TaskHelper.EmptyTask; }; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } cts.Cancel(); return Task.FromResult(rate); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_fault_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func actionAsync = (ctx, ct) => { executed = true; return TaskHelper.EmptyTask; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> fault = (ctx, ct) => { cts.Cancel(); if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Outcomes/InjectFaultAsyncWithOptionsSpecs.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using FluentAssertions; using Polly.Contrib.Simmy.Outcomes; using Polly.Contrib.Simmy.Utilities; using Polly.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Outcomes { [Collection(Helpers.Constants.AmbientContextDependentTestCollection)] public class InjectFaultAsyncWithOptionsSpecs : IDisposable { public InjectFaultAsyncWithOptionsSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } #region Basic Overload, Exception, Context Free [Fact] public void InjectFault_Context_Free_Enabled_Should_not_execute_user_delegate_async() { string exceptionMessage = "exceptionMessage"; Exception fault = new Exception(exceptionMessage); var policy = MonkeyPolicy.InjectExceptionAsync(with => with.Fault(fault) .InjectionRate(0.6) .Enabled() ); Boolean executed = false; Func actionAsync = () => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync)).ShouldThrowExactly().WithMessage(exceptionMessage); executed.Should().BeFalse(); } [Fact] public void InjectFault_Context_Free_Enabled_Should_execute_user_delegate_async() { Exception fault = new Exception(); var policy = MonkeyPolicy.InjectExceptionAsync(with => with.Fault(fault) .InjectionRate(0.3) .Enabled() ); Boolean executed = false; Func actionAsync = () => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync)).ShouldNotThrow(); executed.Should().BeTrue(); } [Fact] public void InjectFault_Context_Free_Enabled_Should_execute_user_delegate_not_throw_if_injected_fault_is_permitted_null() { Exception fault = null; var policy = MonkeyPolicy.InjectExceptionAsync(with => with.Fault(fault) .InjectionRate(0.3) .Enabled() ); Boolean executed = false; Func actionAsync = () => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync)).ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Basic Overload, Exception, With Context [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_async() { Boolean executed = false; Context context = new Context { ["ShouldFail"] = true }; var policy = MonkeyPolicy.InjectExceptionAsync(with => with.Fault(new Exception("test")) .InjectionRate(0.6) .EnabledWhen(async (ctx, ct) => await Task.FromResult((bool)ctx["ShouldFail"])) ); Func actionAsync = (_) => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)).ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_execute_user_delegate_async() { Boolean executed = false; Context context = new Context { ["ShouldFail"] = true }; var policy = MonkeyPolicy.InjectExceptionAsync(with => with.Fault(new Exception("test")) .InjectionRate(0.4) .EnabledWhen(async (ctx, ct) => await Task.FromResult((bool)ctx["ShouldFail"])) ); Func actionAsync = _ => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)).ShouldNotThrow(); executed.Should().BeTrue(); } [Fact] public void InjectFault_With_Context_Should_execute_user_delegate_async_with_enabled_lambda_false() { Boolean executed = false; Context context = new Context { ["ShouldFail"] = false }; var policy = MonkeyPolicy.InjectExceptionAsync(with => with.Fault(new Exception("test")) .InjectionRate(0.6) .EnabledWhen(async (ctx, ct) => await Task.FromResult((bool)ctx["ShouldFail"])) ); Func actionAsync = (_) => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)).ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Overload, All based on context [Fact] public void InjectFault_should_throw_if_injection_rate_is_out_of_range_too_low() { Boolean executed = false; Context context = new Context(); Func> fault = (ctx, cts) => Task.FromResult(new Exception()); Func> enabled = (ctx, ct) => Task.FromResult(true); Func> injectionRate = (ctx, ct) => Task.FromResult(-0.1); var policy = MonkeyPolicy.InjectExceptionAsync(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func actionAsync = (_) => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)).ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_should_throw_if_injection_rate_is_out_of_range_too_high() { Boolean executed = false; Context context = new Context(); Func> fault = (ctx, cts) => Task.FromResult(new Exception()); Func> enabled = (ctx, ct) => Task.FromResult(true); Func> injectionRate = (ctx, ct) => Task.FromResult(1.01); var policy = MonkeyPolicy.InjectExceptionAsync(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func actionAsync = (_) => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)).ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_async_basic() { Boolean executed = false; Context context = new Context(); Func> fault = (ctx, cts) => Task.FromResult(new Exception()); Func> enabled = (ctx, ct) => Task.FromResult(true); Func> injectionRate = (ctx, ct) => Task.FromResult(0.6); var policy = MonkeyPolicy.InjectExceptionAsync(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func actionAsync = (_) => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)).ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_async_with_all_context_set() { Boolean executed = false; Context context = new Context(); string failureMessage = "Failure Message"; context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectExceptionAsync(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); Func actionAsync = (_) => { executed = true; return TaskHelper.EmptyTask; }; policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)) .ShouldThrowExactly() .WithMessage(failureMessage); executed.Should().BeFalse(); } #endregion #region Cancellable scenarios [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func actionAsync = (ctx, ct) => { executed = true; return TaskHelper.EmptyTask; }; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectExceptionAsync(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func actionAsync = (ctx, ct) => { executed = true; return TaskHelper.EmptyTask; }; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = (ctx, ct) => { cts.Cancel(); return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectExceptionAsync(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func actionAsync = (ctx, ct) => { executed = true; return TaskHelper.EmptyTask; }; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } cts.Cancel(); return Task.FromResult(rate); }; var policy = MonkeyPolicy.InjectExceptionAsync(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_fault_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func actionAsync = (ctx, ct) => { executed = true; return TaskHelper.EmptyTask; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> fault = (ctx, ct) => { cts.Cancel(); if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; var policy = MonkeyPolicy.InjectExceptionAsync(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Outcomes/InjectFaultSpecs.cs ================================================ using System; using System.Threading; using FluentAssertions; using Polly.Contrib.Simmy.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Outcomes { [Collection(Helpers.Constants.AmbientContextDependentTestCollection)] [Obsolete] public class InjectFaultSpecs : IDisposable { public InjectFaultSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } #region Basic Overload, Exception, Context Free [Fact] public void InjectFault_Context_Free_Enabled_Should_not_execute_user_delegate() { string exceptionMessage = "exceptionMessage"; Exception fault = new Exception(exceptionMessage); var policy = MonkeyPolicy.InjectFault(fault, 0.6, () => true); Boolean executed = false; policy.Invoking(x => x.Execute(() => { executed = true; })) .ShouldThrowExactly().WithMessage(exceptionMessage); executed.Should().BeFalse(); } [Fact] public void InjectFault_Context_Free_Enabled_Should_execute_user_delegate() { Exception fault = new Exception("test"); var policy = MonkeyPolicy.InjectFault(fault, 0.3, () => true); Boolean executed = false; policy.Invoking(x => x.Execute(() => { executed = true; })) .ShouldNotThrow(); executed.Should().BeTrue(); } [Fact] public void InjectFault_Context_Free_Enabled_Should_execute_user_delegate_not_throw_if_injected_fault_is_permitted_null() { Exception fault = null; var policy = MonkeyPolicy.InjectFault(fault, 0.6, () => true); Boolean executed = false; policy.Invoking(x => x.Execute(() => { executed = true; })) .ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Basic Overload, Exception, With Context [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate() { Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; var policy = MonkeyPolicy.InjectFault( new Exception("test"), 0.6, (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_execute_user_delegate() { Exception fault = new Exception("test"); Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; var policy = MonkeyPolicy.InjectFault( new Exception("test"), 0.4, (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldNotThrow(); executed.Should().BeTrue(); } [Fact] public void InjectFault_With_Context_Should_execute_user_delegate_with_enabled_lambda_return_false() { Exception fault = new Exception("test"); Boolean executed = false; Context context = new Context(); context["ShouldFail"] = false; var policy = MonkeyPolicy.InjectFault( new Exception("test"), 0.6, (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Overload, All based on context [Fact] public void InjectFault_should_throw_if_injection_rate_is_out_of_range_too_low() { Boolean executed = false; Context context = new Context(); Func fault = (ctx, ct) => new Exception(); Func injectionRate = (ctx, ct) => -0.1; Func enabled = (ctx, ct) => true; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_should_throw_if_injection_rate_is_out_of_range_too_high() { Boolean executed = false; Context context = new Context(); Func fault = (ctx, ct) => new Exception(); Func injectionRate = (ctx, ct) => 1.01; Func enabled = (ctx, ct) => true; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_with_default_context() { Boolean executed = false; Context context = new Context(); Func fault = (ctx, ct) => new Exception(); Func injectionRate = (ctx, ct) => 0.6; Func enabled = (ctx, ct) => true; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_with_all_context_set() { Boolean executed = false; Context context = new Context(); string failureMessage = "Failure Message"; context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func fault = (ctx, ct) => { if (ctx["Message"] != null) { return new InvalidOperationException(ctx["Message"].ToString()); } return new Exception(); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func enabled = (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldThrowExactly().WithMessage(failureMessage); executed.Should().BeFalse(); } #endregion #region Cancellable scenarios [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return rate; }; Func enabled = (ctx, ct) => { return (bool)ctx["ShouldFail"]; }; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return rate; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { cts.Cancel(); return (bool)ctx["ShouldFail"]; }; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["ShouldFail"]; }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } cts.Cancel(); return rate; }; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_fault_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["ShouldFail"]; }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return rate; }; Func fault = (ctx, ct) => { cts.Cancel(); if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Outcomes/InjectFaultTResultAsyncSpecs.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using FluentAssertions; using Polly.Contrib.Simmy.Specs.Helpers; using Polly.Contrib.Simmy.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Outcomes { [Collection(Constants.AmbientContextDependentTestCollection)] [Obsolete] public class InjectFaultTResultAsyncSpecs : IDisposable { public InjectFaultTResultAsyncSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } #region Basic Overload, Exception, Context Free [Fact] public void InjectFault_Context_Free_Enabled_Should_not_execute_user_delegate_async() { string exceptionMessage = "exceptionMessage"; Exception fault = new Exception(exceptionMessage); Boolean executed = false; Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, 0.6, () => true); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync)) .ShouldThrowExactly() .WithMessage(exceptionMessage); executed.Should().BeFalse(); } [Fact] public void InjectFault_Context_Free_Enabled_Should_execute_user_delegate_async() { string exceptionMessage = "exceptionMessage"; Exception fault = new Exception(exceptionMessage); Boolean executed = false; Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, 0.3, () => true); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync)) .ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Basic Overload, Exception, With Context [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_async() { Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var policy = MonkeyPolicy.InjectFaultAsync( new Exception(), 0.6, async (ctx, ct) => { return await Task.FromResult((bool)ctx["ShouldFail"]); }); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)).ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_execute_user_delegate_async() { Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var policy = MonkeyPolicy.InjectFaultAsync( new Exception(), 0.4, async (ctx, ct) => { return await Task.FromResult((bool)ctx["ShouldFail"]); }); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)) .ShouldNotThrow(); executed.Should().BeTrue(); } [Fact] public void InjectFault_With_Context_Should_execute_user_delegate_async_with_enabled_lambda_return_false() { Boolean executed = false; Context context = new Context(); context["ShouldFail"] = false; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var policy = MonkeyPolicy.InjectFaultAsync( new Exception(), 0.4, async (ctx, ct) => { return await Task.FromResult((bool)ctx["ShouldFail"]); }); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)) .ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Overload, All based on context [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_async_with_default_values() { Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; Func> fault = (ctx, cts) => Task.FromResult(new Exception()); Func> injectionRate = (ctx, ct) => Task.FromResult(0.6); Func> enabled = (ctx, ct) => Task.FromResult(true); var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_async_with_all_values_set() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } #endregion #region TResult Based Monkey Policies [Fact] public async Task InjectFault_Context_Free_Should_Return_Fault_async() { Boolean executed = false; Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; ResultPrimitive fault = ResultPrimitive.Fault; var policy = MonkeyPolicy.InjectFaultAsync(fault, 0.6, () => true); ResultPrimitive response = await policy.ExecuteAsync(actionAsync); response.Should().Be(ResultPrimitive.Fault); executed.Should().BeFalse(); } [Fact] public async Task InjectFault_Context_Free_Should_Not_Return_Fault_async() { Boolean executed = false; Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; ResultPrimitive fault = ResultPrimitive.Fault; var policy = MonkeyPolicy.InjectFaultAsync(fault, 0.4, () => true); ResultPrimitive response = await policy.ExecuteAsync(actionAsync); response.Should().Be(ResultPrimitive.Good); executed.Should().BeTrue(); } [Fact] public async Task InjectFault_With_Context_Enabled_Should_Return_Fault_async() { Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; ResultPrimitive fault = ResultPrimitive.Fault; Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, 0.6, enabled); ResultPrimitive response = await policy.ExecuteAsync(actionAsync, context); response.Should().Be(ResultPrimitive.Fault); executed.Should().BeFalse(); } [Fact] public async Task InjectFault_With_Context_Enabled_Should_Not_Return_Fault_async() { Boolean executed = false; Context context = new Context(); context["ShouldFail"] = false; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; ResultPrimitive fault = ResultPrimitive.Fault; Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, 0.6, enabled); ResultPrimitive response = await policy.ExecuteAsync(actionAsync, context); response.Should().Be(ResultPrimitive.Good); executed.Should().BeTrue(); } [Fact] public async Task InjectFault_With_Context_InjectionRate_Should_Return_Fault_async() { Boolean executed = false; Context context = new Context(); context["InjectionRate"] = 0.6; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; Func> fault = (ctx, cts) => { return Task.FromResult(ResultPrimitive.Fault); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> enabled = (ctx, ct) => { return Task.FromResult(true); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); ResultPrimitive response = await policy.ExecuteAsync(actionAsync, context); response.Should().Be(ResultPrimitive.Fault); executed.Should().BeFalse(); } [Fact] public async Task InjectFault_With_Context_InjectionRate_Should_Not_Return_Fault_async() { Boolean executed = false; Context context = new Context(); context["InjectionRate"] = 0.4; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; Func> fault = (ctx, cts) => { return Task.FromResult(ResultPrimitive.Fault); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> enabled = (ctx, ct) => { return Task.FromResult(true); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); ResultPrimitive response = await policy.ExecuteAsync(actionAsync, context); response.Should().Be(ResultPrimitive.Good); executed.Should().BeTrue(); } #endregion #region Cancellable scenarios [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func> actionAsync = (ctx, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func> actionAsync = (ctx, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = (ctx, ct) => { cts.Cancel(); return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func> actionAsync = (ctx, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } cts.Cancel(); return Task.FromResult(rate); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_fault_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func> actionAsync = (ctx, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> fault = (ctx, ct) => { cts.Cancel(); if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; var policy = MonkeyPolicy.InjectFaultAsync(fault, injectionRate, enabled); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Outcomes/InjectFaultTResultAsyncWithOptionsSpecs.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using FluentAssertions; using Polly.Contrib.Simmy.Outcomes; using Polly.Contrib.Simmy.Specs.Helpers; using Polly.Contrib.Simmy.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Outcomes { [Collection(Helpers.Constants.AmbientContextDependentTestCollection)] public class InjectFaultTResultAsyncWithOptionsSpecs : IDisposable { public InjectFaultTResultAsyncWithOptionsSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } #region Basic Overload, Exception, Context Free [Fact] public void InjectFault_Context_Free_Enabled_Should_not_execute_user_delegate_async() { string exceptionMessage = "exceptionMessage"; Exception fault = new Exception(exceptionMessage); Boolean executed = false; Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var policy = MonkeyPolicy.InjectResultAsync(with => with.Fault(fault) .InjectionRate(0.6) .Enabled() ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync)) .ShouldThrowExactly() .WithMessage(exceptionMessage); executed.Should().BeFalse(); } [Fact] public void InjectFault_Context_Free_Enabled_Should_execute_user_delegate_async() { string exceptionMessage = "exceptionMessage"; Exception fault = new Exception(exceptionMessage); Boolean executed = false; Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var policy = MonkeyPolicy.InjectResultAsync(with => with.Fault(fault) .InjectionRate(0.3) .Enabled() ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync)) .ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Basic Overload, Exception, With Context [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_async() { Boolean executed = false; Context context = new Context { ["ShouldFail"] = true }; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var policy = MonkeyPolicy.InjectResultAsync(with => with.Fault(new Exception()) .InjectionRate(0.6) .EnabledWhen(async (ctx, ct) => await Task.FromResult((bool)ctx["ShouldFail"])) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)).ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_execute_user_delegate_async() { Boolean executed = false; Context context = new Context { ["ShouldFail"] = true }; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var policy = MonkeyPolicy.InjectResultAsync(with => with.Fault(new Exception()) .InjectionRate(0.4) .EnabledWhen(async (ctx, ct) => await Task.FromResult((bool)ctx["ShouldFail"])) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)) .ShouldNotThrow(); executed.Should().BeTrue(); } [Fact] public void InjectFault_With_Context_Should_execute_user_delegate_async_with_enabled_lambda_return_false() { Boolean executed = false; Context context = new Context { ["ShouldFail"] = false }; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; var policy = MonkeyPolicy.InjectResultAsync(with => with.Fault(new Exception()) .InjectionRate(0.4) .EnabledWhen(async (ctx, ct) => await Task.FromResult((bool)ctx["ShouldFail"])) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)) .ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Overload, All based on context [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_async_with_default_values() { Boolean executed = false; Context context = new Context { ["ShouldFail"] = true }; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; Func> fault = (ctx, cts) => Task.FromResult(new Exception()); Func> injectionRate = (ctx, ct) => Task.FromResult(0.6); Func> enabled = (ctx, ct) => Task.FromResult(true); var policy = MonkeyPolicy.InjectResultAsync(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_async_with_all_values_set() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context { ["ShouldFail"] = true, ["Message"] = failureMessage, ["InjectionRate"] = 0.6 }; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectResultAsync(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } #endregion #region TResult Based Monkey Policies [Fact] public async Task InjectFault_Context_Free_Should_Return_Fault_async() { Boolean executed = false; Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; ResultPrimitive fault = ResultPrimitive.Fault; var policy = MonkeyPolicy.InjectResultAsync(with => with.Result(fault) .InjectionRate(0.6) .Enabled() ); ResultPrimitive response = await policy.ExecuteAsync(actionAsync); response.Should().Be(ResultPrimitive.Fault); executed.Should().BeFalse(); } [Fact] public async Task InjectFault_Context_Free_Should_Not_Return_Fault_async() { Boolean executed = false; Func> actionAsync = () => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; ResultPrimitive fault = ResultPrimitive.Fault; var policy = MonkeyPolicy.InjectResultAsync(with => with.Result(fault) .InjectionRate(0.4) .Enabled() ); ResultPrimitive response = await policy.ExecuteAsync(actionAsync); response.Should().Be(ResultPrimitive.Good); executed.Should().BeTrue(); } [Fact] public async Task InjectFault_With_Context_Enabled_Should_Return_Fault_async() { Boolean executed = false; Context context = new Context { ["ShouldFail"] = true }; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; ResultPrimitive fault = ResultPrimitive.Fault; Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectResultAsync(with => with.Result(fault) .InjectionRate(0.6) .EnabledWhen(enabled) ); ResultPrimitive response = await policy.ExecuteAsync(actionAsync, context); response.Should().Be(ResultPrimitive.Fault); executed.Should().BeFalse(); } [Fact] public async Task InjectFault_With_Context_Enabled_Should_Not_Return_Fault_async() { Boolean executed = false; Context context = new Context { ["ShouldFail"] = false }; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; ResultPrimitive fault = ResultPrimitive.Fault; Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectResultAsync(with => with.Result(fault) .InjectionRate(0.6) .EnabledWhen(enabled) ); ResultPrimitive response = await policy.ExecuteAsync(actionAsync, context); response.Should().Be(ResultPrimitive.Good); executed.Should().BeTrue(); } [Fact] public async Task InjectFault_With_Context_InjectionRate_Should_Return_Fault_async() { Boolean executed = false; Context context = new Context { ["InjectionRate"] = 0.6 }; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; Func> fault = (ctx, cts) => { return Task.FromResult(ResultPrimitive.Fault); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> enabled = (ctx, ct) => { return Task.FromResult(true); }; var policy = MonkeyPolicy.InjectResultAsync(with => with.Result(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); ResultPrimitive response = await policy.ExecuteAsync(actionAsync, context); response.Should().Be(ResultPrimitive.Fault); executed.Should().BeFalse(); } [Fact] public async Task InjectFault_With_Context_InjectionRate_Should_Not_Return_Fault_async() { Boolean executed = false; Context context = new Context(); context["InjectionRate"] = 0.4; Func> actionAsync = (ctx) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; Func> fault = (ctx, cts) => { return Task.FromResult(ResultPrimitive.Fault); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> enabled = (ctx, ct) => { return Task.FromResult(true); }; var policy = MonkeyPolicy.InjectResultAsync(with => with.Result(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); ResultPrimitive response = await policy.ExecuteAsync(actionAsync, context); response.Should().Be(ResultPrimitive.Good); executed.Should().BeTrue(); } #endregion #region Cancellable scenarios [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func> actionAsync = (ctx, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectResultAsync(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func> actionAsync = (ctx, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = (ctx, ct) => { cts.Cancel(); return Task.FromResult((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectResultAsync(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func> actionAsync = (ctx, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; Func> fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } cts.Cancel(); return Task.FromResult(rate); }; var policy = MonkeyPolicy.InjectResultAsync(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_fault_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func> actionAsync = (ctx, ct) => { executed = true; return Task.FromResult(ResultPrimitive.Good); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func> enabled = (ctx, ct) => { return Task.FromResult((bool)ctx["ShouldFail"]); }; Func> injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return Task.FromResult(rate); }; Func> fault = (ctx, ct) => { cts.Cancel(); if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return Task.FromResult(ex); } return Task.FromResult(new Exception()); }; var policy = MonkeyPolicy.InjectResultAsync(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Awaiting(async x => await x.ExecuteAsync(actionAsync, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Outcomes/InjectFaultTResultSpecs.cs ================================================ using System; using System.Threading; using FluentAssertions; using Polly.Contrib.Simmy.Specs.Helpers; using Polly.Contrib.Simmy.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Outcomes { [Collection(Constants.AmbientContextDependentTestCollection)] [Obsolete] public class InjectFaultTResultSpecs : IDisposable { public InjectFaultTResultSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } #region Basic Overload, Exception, Context Free [Fact] public void InjectFaultContext_Free_Enabled_Should_not_execute_user_delegate() { string exceptionMessage = "exceptionMessage"; Exception fault = new Exception(exceptionMessage); Boolean executed = false; Func action = () => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectFault(fault, 0.6, () => true); policy.Invoking(x => x.Execute(action)) .ShouldThrowExactly().WithMessage(exceptionMessage); executed.Should().BeFalse(); } [Fact] public void InjectFaultContext_Free_Enabled_Should_execute_user_delegate() { string exceptionMessage = "exceptionMessage"; Exception fault = new Exception(exceptionMessage); Boolean executed = false; Func action = () => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectFault(fault, 0.3, () => true); policy.Invoking(x => x.Execute(action)) .ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Basic Overload, Exception, With Context [Fact] public void InjectFaultWith_Context_Should_not_execute_user_delegate() { Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectFault( new Exception(), 0.6, (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }); policy.Invoking(x => x.Execute(action, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFaultWith_Context_Should_execute_user_delegate() { Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectFault( new Exception(), 0.4, (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }); policy.Invoking(x => x.Execute(action, context)) .ShouldNotThrow(); executed.Should().BeTrue(); } [Fact] public void InjectFaultWith_Context_Should_execute_user_delegate_with_enabled_lambda_disabled() { Boolean executed = false; Context context = new Context(); context["ShouldFail"] = false; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectFault( new Exception(), 0.6, (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }); policy.Invoking(x => x.Execute(action, context)) .ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Overload, All based on context [Fact] public void InjectFaultWith_Context_Should_not_execute_user_delegate_default_context() { Boolean executed = false; Context context = new Context(); Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; Func fault = (ctx, ct) => new Exception(); Func injectionRate = (ctx, ct) => 0.6; Func enabled = (ctx, ct) => true; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute(action, context)) .ShouldThrow(); executed.Should().BeFalse(); } [Fact] public void InjectFaultWith_Context_Should_not_execute_user_delegate_full_context() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; Func fault = (ctx, ct) => { if (ctx["Message"] != null) { return new InvalidOperationException(ctx["Message"].ToString()); } return new Exception(); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func enabled = (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute(action, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } #endregion #region TResult Based Monkey Policies [Fact] public void InjectFaultContext_Free_Should_Return_Fault() { Boolean executed = false; Func action = () => { executed = true; return ResultPrimitive.Good; }; ResultPrimitive fault = ResultPrimitive.Fault; var policy = MonkeyPolicy.InjectFault(fault, 0.6, () => true); ResultPrimitive response = policy.Execute(action); response.Should().Be(ResultPrimitive.Fault); executed.Should().BeFalse(); } [Fact] public void InjectFaultContext_Free_Should_Not_Return_Fault() { Boolean executed = false; Func action = () => { executed = true; return ResultPrimitive.Good; }; ResultPrimitive fault = ResultPrimitive.Fault; var policy = MonkeyPolicy.InjectFault(fault, 0.4, () => true); ResultPrimitive response = policy.Execute(action); response.Should().Be(ResultPrimitive.Good); executed.Should().BeTrue(); } [Fact] public void InjectFaultWith_Context_Enabled_Should_Return_Fault() { Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; ResultPrimitive fault = ResultPrimitive.Fault; Func enabled = (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectFault(fault, 0.6, enabled); ResultPrimitive response = policy.Execute(action, context); response.Should().Be(ResultPrimitive.Fault); executed.Should().BeFalse(); } [Fact] public void InjectFaultWith_Context_Enabled_Should_Not_Return_Fault() { Boolean executed = false; Context context = new Context(); context["ShouldFail"] = false; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; ResultPrimitive fault = ResultPrimitive.Fault; Func enabled = (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectFault(fault, 0.6, enabled); ResultPrimitive response = policy.Execute(action, context); response.Should().Be(ResultPrimitive.Good); executed.Should().BeTrue(); } [Fact] public void InjectFaultWith_Context_InjectionRate_Should_Return_Fault() { Boolean executed = false; Context context = new Context(); context["InjectionRate"] = 0.6; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; Func fault = (ctx, ct) => { return ResultPrimitive.Fault; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func enabled = (ctx, ct) => { return true; }; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); ResultPrimitive response = policy.Execute(action, context); response.Should().Be(ResultPrimitive.Fault); executed.Should().BeFalse(); } [Fact] public void InjectFaultWith_Context_InjectionRate_Should_Not_Return_Fault() { Boolean executed = false; Context context = new Context(); context["InjectionRate"] = 0.4; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; Func fault = (ctx, ct) => { return ResultPrimitive.Fault; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func enabled = (ctx, ct) => { return true; }; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); ResultPrimitive response = policy.Execute(action, context); response.Should().Be(ResultPrimitive.Good); executed.Should().BeTrue(); } #endregion #region Cancellable scenarios [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return rate; }; Func enabled = (ctx, ct) => { return (bool)ctx["ShouldFail"]; }; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Invoking(x => x.Execute((ctx, ct) => action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return rate; }; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { cts.Cancel(); return (bool)ctx["ShouldFail"]; }; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute(action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; Func fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["ShouldFail"]; }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } cts.Cancel(); return rate; }; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute(action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_fault_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context(); context["ShouldFail"] = true; context["Message"] = failureMessage; context["InjectionRate"] = 0.6; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["ShouldFail"]; }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return rate; }; Func fault = (ctx, ct) => { cts.Cancel(); if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectFault(fault, injectionRate, enabled); policy.Invoking(x => x.Execute(action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Outcomes/InjectFaultTResultWithOptionsSpecs.cs ================================================ using System; using System.Threading; using FluentAssertions; using Polly.Contrib.Simmy.Outcomes; using Polly.Contrib.Simmy.Specs.Helpers; using Polly.Contrib.Simmy.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Outcomes { [Collection(Constants.AmbientContextDependentTestCollection)] public class InjectFaultTResultWithOptionsSpecs : IDisposable { public InjectFaultTResultWithOptionsSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } #region Basic Overload, Result as Fault, Context Free [Fact] public void InjectFaultContext_Free_Enabled_Should_not_execute_user_delegate() { string exceptionMessage = "exceptionMessage"; Exception fault = new Exception(exceptionMessage); Boolean executed = false; Func action = () => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectResult(with => with.Fault(fault) .InjectionRate(0.6) .Enabled() ); policy.Invoking(x => x.Execute(action)) .ShouldThrowExactly().WithMessage(exceptionMessage); executed.Should().BeFalse(); } [Fact] public void InjectFaultContext_Free_Enabled_Should_execute_user_delegate() { string exceptionMessage = "exceptionMessage"; Exception fault = new Exception(exceptionMessage); Boolean executed = false; Func action = () => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectResult(with => with.Fault(fault) .InjectionRate(0.3) .Enabled() ); policy.Invoking(x => x.Execute(action)) .ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Basic Overload, Result as Fault, With Context [Fact] public void InjectFaultWith_Context_Should_not_execute_user_delegate() { Boolean executed = false; Context context = new Context { ["ShouldFail"] = true }; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectResult(with => with.Fault(new Exception()) .InjectionRate(0.6) .EnabledWhen((ctx, ct) => ((bool)ctx["ShouldFail"])) ); policy.Invoking(x => x.Execute(action, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFaultWith_Context_Should_execute_user_delegate() { Boolean executed = false; Context context = new Context { ["ShouldFail"] = true }; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectResult(with => with.Fault(new Exception()) .InjectionRate(0.4) .EnabledWhen((ctx, ct) => ((bool)ctx["ShouldFail"])) ); policy.Invoking(x => x.Execute(action, context)) .ShouldNotThrow(); executed.Should().BeTrue(); } [Fact] public void InjectFaultWith_Context_Should_execute_user_delegate_with_enabled_lambda_disabled() { Boolean executed = false; Context context = new Context { ["ShouldFail"] = false }; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectResult(with => with.Fault(new Exception()) .InjectionRate(0.6) .EnabledWhen((ctx, ct) => ((bool)ctx["ShouldFail"])) ); policy.Invoking(x => x.Execute(action, context)) .ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Overload, All based on context [Fact] public void InjectFaultWith_Context_Should_not_execute_user_delegate_default_context() { Boolean executed = false; Context context = new Context(); Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; Func fault = (ctx, ct) => new Exception(); Func injectionRate = (ctx, ct) => 0.6; Func enabled = (ctx, ct) => true; var policy = MonkeyPolicy.InjectResult(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute(action, context)) .ShouldThrow(); executed.Should().BeFalse(); } [Fact] public void InjectFaultWith_Context_Should_not_execute_user_delegate_full_context() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context { ["ShouldFail"] = true, ["Message"] = failureMessage, ["InjectionRate"] = 0.6 }; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; Func fault = (ctx, ct) => { if (ctx["Message"] != null) { return new InvalidOperationException(ctx["Message"].ToString()); } return new Exception(); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func enabled = (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectResult(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute(action, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } #endregion #region TResult Based Monkey Policies [Fact] public void InjectFaultContext_Free_Should_Return_Fault() { Boolean executed = false; Func action = () => { executed = true; return ResultPrimitive.Good; }; ResultPrimitive fault = ResultPrimitive.Fault; var policy = MonkeyPolicy.InjectResult(with => with.Result(fault) .InjectionRate(0.6) .Enabled() ); ResultPrimitive response = policy.Execute(action); response.Should().Be(ResultPrimitive.Fault); executed.Should().BeFalse(); } [Fact] public void InjectFaultContext_Free_Should_Not_Return_Fault() { Boolean executed = false; Func action = () => { executed = true; return ResultPrimitive.Good; }; ResultPrimitive fault = ResultPrimitive.Fault; var policy = MonkeyPolicy.InjectResult(with => with.Result(fault) .InjectionRate(0.4) .Enabled() ); ResultPrimitive response = policy.Execute(action); response.Should().Be(ResultPrimitive.Good); executed.Should().BeTrue(); } [Fact] public void InjectFaultWith_Context_Enabled_Should_Return_Fault() { Boolean executed = false; Context context = new Context { ["ShouldFail"] = true }; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; ResultPrimitive fault = ResultPrimitive.Fault; Func enabled = (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectResult(with => with.Result(fault) .InjectionRate(0.6) .EnabledWhen(enabled) ); ResultPrimitive response = policy.Execute(action, context); response.Should().Be(ResultPrimitive.Fault); executed.Should().BeFalse(); } [Fact] public void InjectFaultWith_Context_Enabled_Should_Not_Return_Fault() { Boolean executed = false; Context context = new Context { ["ShouldFail"] = false }; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; ResultPrimitive fault = ResultPrimitive.Fault; Func enabled = (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectResult(with => with.Result(fault) .InjectionRate(0.4) .EnabledWhen(enabled) ); ResultPrimitive response = policy.Execute(action, context); response.Should().Be(ResultPrimitive.Good); executed.Should().BeTrue(); } [Fact] public void InjectFaultWith_Context_InjectionRate_Should_Return_Fault() { Boolean executed = false; Context context = new Context { ["InjectionRate"] = 0.6 }; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; Func fault = (ctx, ct) => { return ResultPrimitive.Fault; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func enabled = (ctx, ct) => { return true; }; var policy = MonkeyPolicy.InjectResult(with => with.Result(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); ResultPrimitive response = policy.Execute(action, context); response.Should().Be(ResultPrimitive.Fault); executed.Should().BeFalse(); } [Fact] public void InjectFaultWith_Context_InjectionRate_Should_Not_Return_Fault() { Boolean executed = false; Context context = new Context(); context["InjectionRate"] = 0.4; Func action = (ctx) => { executed = true; return ResultPrimitive.Good; }; Func fault = (ctx, ct) => { return ResultPrimitive.Fault; }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func enabled = (ctx, ct) => { return true; }; var policy = MonkeyPolicy.InjectResult(with => with.Result(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); ResultPrimitive response = policy.Execute(action, context); response.Should().Be(ResultPrimitive.Good); executed.Should().BeTrue(); } #endregion #region Cancellable scenarios [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context { ["ShouldFail"] = true, ["Message"] = failureMessage, ["InjectionRate"] = 0.6 }; Func fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return rate; }; Func enabled = (ctx, ct) => { return (bool)ctx["ShouldFail"]; }; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Invoking(x => x.Execute((ctx, ct) => action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context { ["ShouldFail"] = true, ["Message"] = failureMessage, ["InjectionRate"] = 0.6 }; Func fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return rate; }; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { cts.Cancel(); return (bool)ctx["ShouldFail"]; }; var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute(action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context { ["ShouldFail"] = true, ["Message"] = failureMessage, ["InjectionRate"] = 0.6 }; Func fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["ShouldFail"]; }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } cts.Cancel(); return rate; }; var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute(action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_fault_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context { ["ShouldFail"] = true, ["Message"] = failureMessage, ["InjectionRate"] = 0.6 }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["ShouldFail"]; }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return rate; }; Func fault = (ctx, ct) => { cts.Cancel(); if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; Func action = (ctx, ct) => { executed = true; return ResultPrimitive.Good; }; var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute(action, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Outcomes/InjectFaultWithOptionsSpecs.cs ================================================ using System; using System.Threading; using FluentAssertions; using Polly.Contrib.Simmy.Outcomes; using Polly.Contrib.Simmy.Utilities; using Xunit; namespace Polly.Contrib.Simmy.Specs.Outcomes { [Collection(Helpers.Constants.AmbientContextDependentTestCollection)] public class InjectFaultWithOptionsSpecs : IDisposable { public InjectFaultWithOptionsSpecs() { ThreadSafeRandom_LockOncePerThread.NextDouble = () => 0.5; } public void Dispose() { ThreadSafeRandom_LockOncePerThread.Reset(); } #region Basic Overload, Exception, Context Free [Fact] public void InjectFault_Context_Free_Enabled_Should_not_execute_user_delegate() { string exceptionMessage = "exceptionMessage"; Exception fault = new Exception(exceptionMessage); var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(0.6) .Enabled() ); Boolean executed = false; policy.Invoking(x => x.Execute(() => { executed = true; })) .ShouldThrowExactly().WithMessage(exceptionMessage); executed.Should().BeFalse(); } [Fact] public void InjectFault_Context_Free_Enabled_Should_execute_user_delegate() { Exception fault = new Exception("test"); var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(0.3) .Enabled() ); Boolean executed = false; policy.Invoking(x => x.Execute(() => { executed = true; })) .ShouldNotThrow(); executed.Should().BeTrue(); } [Fact] public void InjectFault_Context_Free_Enabled_Should_execute_user_delegate_not_throw_if_injected_fault_is_permitted_null() { Exception fault = null; var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(0.6) .Enabled() ); Boolean executed = false; policy.Invoking(x => x.Execute(() => { executed = true; })) .ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Basic Overload, Exception, With Context [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate() { Boolean executed = false; Context context = new Context { ["ShouldFail"] = true }; var policy = MonkeyPolicy.InjectException(with => with.Fault(new Exception("test")) .InjectionRate(0.6) .EnabledWhen((ctx, ct) => ((bool)ctx["ShouldFail"])) ); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_execute_user_delegate() { Exception fault = new Exception("test"); Boolean executed = false; Context context = new Context { ["ShouldFail"] = true }; var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(0.4) .EnabledWhen((ctx, ct) => ((bool)ctx["ShouldFail"])) ); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldNotThrow(); executed.Should().BeTrue(); } [Fact] public void InjectFault_With_Context_Should_execute_user_delegate_with_enabled_lambda_return_false() { Exception fault = new Exception("test"); Boolean executed = false; Context context = new Context { ["ShouldFail"] = false }; var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(0.6) .EnabledWhen((ctx, ct) => ((bool)ctx["ShouldFail"])) ); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldNotThrow(); executed.Should().BeTrue(); } #endregion #region Overload, All based on context [Fact] public void InjectFault_should_throw_if_injection_rate_is_out_of_range_too_low() { Boolean executed = false; Context context = new Context(); Func fault = (ctx, ct) => new Exception(); Func injectionRate = (ctx, ct) => -0.1; Func enabled = (ctx, ct) => true; var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_should_throw_if_injection_rate_is_out_of_range_too_high() { Boolean executed = false; Context context = new Context(); Func fault = (ctx, ct) => new Exception(); Func injectionRate = (ctx, ct) => 1.01; Func enabled = (ctx, ct) => true; var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_with_default_context() { Boolean executed = false; Context context = new Context(); Func fault = (ctx, ct) => new Exception(); Func injectionRate = (ctx, ct) => 0.6; Func enabled = (ctx, ct) => true; var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldThrowExactly(); executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_with_all_context_set() { Boolean executed = false; string failureMessage = "Failure Message"; Context context = new Context { ["ShouldFail"] = true, ["Message"] = failureMessage, ["InjectionRate"] = 0.6 }; Func fault = (ctx, ct) => { if (ctx["Message"] != null) { return new InvalidOperationException(ctx["Message"].ToString()); } return new Exception(); }; Func injectionRate = (ctx, ct) => { if (ctx["InjectionRate"] != null) { return (double)ctx["InjectionRate"]; } return 0; }; Func enabled = (ctx, ct) => { return ((bool)ctx["ShouldFail"]); }; var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute((ctx) => { executed = true; }, context)) .ShouldThrowExactly().WithMessage(failureMessage); executed.Should().BeFalse(); } #endregion #region Cancellable scenarios [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_before_to_start_execution() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context { ["ShouldFail"] = true, ["Message"] = failureMessage, ["InjectionRate"] = 0.6 }; Func fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return rate; }; Func enabled = (ctx, ct) => { return (bool)ctx["ShouldFail"]; }; var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.Cancel(); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_enabled_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context { ["ShouldFail"] = true, ["Message"] = failureMessage, ["InjectionRate"] = 0.6 }; Func fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return rate; }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { cts.Cancel(); return (bool)ctx["ShouldFail"]; }; var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_injectionrate_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context { ["ShouldFail"] = true, ["Message"] = failureMessage, ["InjectionRate"] = 0.6 }; Func fault = (ctx, cts) => { if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["ShouldFail"]; }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } cts.Cancel(); return rate; }; var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } [Fact] public void InjectFault_With_Context_Should_not_execute_user_delegate_if_user_cancelationtoken_cancelled_on_fault_config_delegate() { string failureMessage = "Failure Message"; Boolean executed = false; Context context = new Context { ["ShouldFail"] = true, ["Message"] = failureMessage, ["InjectionRate"] = 0.6 }; using (CancellationTokenSource cts = new CancellationTokenSource()) { Func enabled = (ctx, ct) => { return (bool)ctx["ShouldFail"]; }; Func injectionRate = (ctx, ct) => { double rate = 0; if (ctx["InjectionRate"] != null) { rate = (double)ctx["InjectionRate"]; } return rate; }; Func fault = (ctx, ct) => { cts.Cancel(); if (ctx["Message"] != null) { Exception ex = new InvalidOperationException(ctx["Message"].ToString()); return ex; } return new Exception(); }; var policy = MonkeyPolicy.InjectException(with => with.Fault(fault) .InjectionRate(injectionRate) .EnabledWhen(enabled) ); policy.Invoking(x => x.Execute((ctx, ct) => { executed = true; }, context, cts.Token)) .ShouldThrow(); } executed.Should().BeFalse(); } #endregion } } ================================================ FILE: src/Polly.Contrib.Simmy.Specs/Polly.Contrib.Simmy.Specs.csproj ================================================  netcoreapp1.1;netcoreapp2.1;netcoreapp3.0;net462;net472 ================================================ FILE: src/Polly.Contrib.Simmy.nuspec ================================================  App vNext App vNext Simmy is a chaos-engineering and fault-injection tool, integrating with the Polly resilience project for .NET en-US BSD-3-Clause https://raw.github.com/App-vNext/Simmy/master/Simmy.png https://github.com/App-vNext/Simmy Resilience Chaos-engineering Fault-injection Copyright © 2019, App vNext 0.3.0 --------------------- - Add a new Fluent-builder syntax - Add intuitive syntax for result stubbing, for use in unit-tests or in other systems on how those systems handle faults - Compiles on mac and linux - Add support for .NET Standard 2.1 - Validates constant `injectionRate` at Policy configuration time 0.2.0 --------------------- - Makes InjectLatency policies cancellable (both sync and async) - Add support for cancellation on async configuration-providing delegates 0.1.0 --------------------- - Initial launch ================================================ FILE: src/Polly.Contrib.Simmy.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.28307.168 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Polly.Contrib.Simmy", "Polly.Contrib.Simmy\Polly.Contrib.Simmy.csproj", "{B7730E1D-0796-4151-95C3-1C6E7DE897B9}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Polly.Contrib.Simmy.Specs", "Polly.Contrib.Simmy.Specs\Polly.Contrib.Simmy.Specs.csproj", "{ED587F9F-BBE6-4715-9FAA-BAAEFE288A73}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {B7730E1D-0796-4151-95C3-1C6E7DE897B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B7730E1D-0796-4151-95C3-1C6E7DE897B9}.Debug|Any CPU.Build.0 = Debug|Any CPU {B7730E1D-0796-4151-95C3-1C6E7DE897B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {B7730E1D-0796-4151-95C3-1C6E7DE897B9}.Release|Any CPU.Build.0 = Release|Any CPU {ED587F9F-BBE6-4715-9FAA-BAAEFE288A73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ED587F9F-BBE6-4715-9FAA-BAAEFE288A73}.Debug|Any CPU.Build.0 = Debug|Any CPU {ED587F9F-BBE6-4715-9FAA-BAAEFE288A73}.Release|Any CPU.ActiveCfg = Release|Any CPU {ED587F9F-BBE6-4715-9FAA-BAAEFE288A73}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {509DA380-9907-48F4-BB77-DD6F8C4EF207} EndGlobalSection EndGlobal