/// <summary>
        /// Invokes the Steps given Background, Scenario, and TearDown steps.
        /// </summary>
        /// <param name="backgroundSteps"></param>
        /// <param name="scenarioSteps"></param>
        /// <param name="tearDownSteps"></param>
        /// <returns></returns>
        private async Task <RunSummary> InvokeStepsAsync(ICollection <IStepDefinition> backgroundSteps
                                                         , ICollection <IStepDefinition> scenarioSteps, ICollection <IStepDefinition> tearDownSteps)
        {
            var scenarioTypeInfo = this._scenarioClass.GetTypeInfo();
            var filters          = scenarioTypeInfo.Assembly.GetCustomAttributes(typeof(Attribute))
                                   .Concat(scenarioTypeInfo.GetCustomAttributes(typeof(Attribute)))
                                   .Concat(this._scenarioMethod.GetCustomAttributes(typeof(Attribute)))
                                   .OfType <IFilter <IStepDefinition> >();

            var    summary           = new RunSummary();
            string skipReason        = null;
            var    scenarioRollbacks = new List <(StepContext context, Func <IStepContext, Task> callback)>();
            var    stepNumber        = 0;

            foreach (var stepDefinition in filters.Aggregate(backgroundSteps.Concat(scenarioSteps).Concat(tearDownSteps)
                                                             , (current, filter) => filter.Filter(current)))
            {
                stepDefinition.SkipReason = stepDefinition.SkipReason ?? skipReason;

                var stepDisplayName = GetStepDisplayName(this._scenario.DisplayName
                                                         , ++stepNumber
                                                         , stepDefinition.OnDisplayText?.Invoke(
                                                             stepDefinition.Text, stepDefinition.StepDefinitionType)
                                                         );

                var step = new StepTest(this._scenario, stepDisplayName);

                // #1 MWP 2020-07-01 11:56:45 AM: After testing, this should be fine, and better behaved we think.
                using (var interceptingBus = new DelegatingMessageBus(
                           this._messageBus
                           , message =>
                {
                    if (message is ITestFailed && stepDefinition.FailureBehavior == RemainingSteps.Skip)
                    {
                        skipReason = $"Failed to execute preceding step: {step.DisplayName}";
                    }
                })
                       )
                {
                    var stepContext = new StepContext(step);

                    // TODO: TBD: #1 MWP 2020-07-01 11:57:26 AM: it is an xUnit thing, could possibly be IDisposable itself...
                    // TODO: TBD: including assumed ownership of the IDisposable messageBus, for instance, TODO: TBD: but that is outside the scope of xWellBehaved...
                    var stepRunner = new StepTestRunner(
                        stepContext
                        , stepDefinition.Body
                        , step
                        , interceptingBus
                        , this._scenarioClass
                        , this._constructorArguments
                        , this._scenarioMethod
                        , this._scenarioMethodArguments
                        , stepDefinition.SkipReason
                        , Array.Empty <BeforeAfterTestAttribute>()
                        , new ExceptionAggregator(this._aggregator)
                        , this._cancellationTokenSource);

                    summary.Aggregate(await stepRunner.RunAsync());

                    // TODO: TBD: could we use disposable?.Dispose() here?
                    var stepRollbacks = stepContext.Disposables
                                        .Where(disposable => disposable != null)
                                        .Select((Func <IDisposable, Func <IStepContext, Task> >)(disposable => context =>
                    {
                        disposable.Dispose();
                        return(Task.FromResult(0));
                    }))
                                        .Concat(stepDefinition.Rollbacks)
                                        .Where(onRollback => onRollback != null)
                                        .Select(onRollback => (stepContext, onRollback));

                    scenarioRollbacks.AddRange(stepRollbacks);
                }
            }

            if (scenarioRollbacks.Any())
            {
                scenarioRollbacks.Reverse();
                var rollbackTimer      = new ExecutionTimer();
                var rollbackAggregator = new ExceptionAggregator();

                // "Teardowns" not to be confused with TearDown versus Background.
                foreach (var(context, onRollback) in scenarioRollbacks)
                {
                    await Invoker.Invoke(() => onRollback.Invoke(context), rollbackAggregator, rollbackTimer);
                }

                summary.Time += rollbackTimer.Total;

                if (rollbackAggregator.HasExceptions)
                {
                    summary.Failed++;
                    summary.Total++;

                    var stepDisplayName = GetStepDisplayName(this._scenario.DisplayName, ++stepNumber, $"({StepType.Rollback})");

                    this._messageBus.Queue(new StepTest(this._scenario, stepDisplayName)
                                           , test => new TestFailed(test, rollbackTimer.Total, null, rollbackAggregator.ToException())
                                           , this._cancellationTokenSource);
                }
            }

            return(summary);
        }
        private async Task <RunSummary> InvokeStepsAsync(
            ICollection <IStepDefinition> backGroundStepDefinitions, ICollection <IStepDefinition> scenarioStepDefinitions)
        {
            var scenarioTypeInfo = this.scenarioClass.GetTypeInfo();
            var filters          = scenarioTypeInfo.Assembly.GetCustomAttributes(typeof(Attribute))
                                   .Concat(scenarioTypeInfo.GetCustomAttributes(typeof(Attribute)))
                                   .Concat(this.scenarioMethod.GetCustomAttributes(typeof(Attribute)))
                                   .OfType <IFilter <IStepDefinition> >();

            var    summary           = new RunSummary();
            string skipReason        = null;
            var    scenarioTeardowns = new List <Tuple <StepContext, Func <IStepContext, Task> > >();
            var    stepNumber        = 0;

            foreach (var stepDefinition in filters.Aggregate(
                         backGroundStepDefinitions.Concat(scenarioStepDefinitions),
                         (current, filter) => filter.Filter(current)))
            {
                stepDefinition.SkipReason = stepDefinition.SkipReason ?? skipReason;

                var stepDisplayName = GetStepDisplayName(
                    this.scenario.DisplayName,
                    ++stepNumber,
                    stepDefinition.DisplayTextFunc?.Invoke(stepDefinition.Text, stepNumber <= backGroundStepDefinitions.Count));

                var step = new StepTest(this.scenario, stepDisplayName);

                using (var interceptingBus = new DelegatingMessageBus(
                           this.messageBus,
                           message =>
                {
                    if (message is ITestFailed && stepDefinition.FailureBehavior == RemainingSteps.Skip)
                    {
                        skipReason = $"Failed to execute preceding step: {step.DisplayName}";
                    }
                }))
                {
                    var stepContext = new StepContext(step);

                    var stepRunner = new StepTestRunner(
                        stepContext,
                        stepDefinition.Body,
                        step,
                        interceptingBus,
                        this.scenarioClass,
                        this.constructorArguments,
                        this.scenarioMethod,
                        this.scenarioMethodArguments,
                        stepDefinition.SkipReason,
                        new BeforeAfterTestAttribute[0],
                        new ExceptionAggregator(this.aggregator),
                        this.cancellationTokenSource);

                    summary.Aggregate(await stepRunner.RunAsync());

                    var stepTeardowns = stepContext.Disposables
                                        .Where(disposable => disposable != null)
                                        .Select((Func <IDisposable, Func <IStepContext, Task> >)(disposable =>
                                                                                                 context =>
                    {
                        disposable.Dispose();
                        return(Task.FromResult(0));
                    }))
                                        .Concat(stepDefinition.Teardowns)
                                        .Where(teardown => teardown != null)
                                        .Select(teardown => Tuple.Create(stepContext, teardown));

                    scenarioTeardowns.AddRange(stepTeardowns);
                }
            }

            if (scenarioTeardowns.Any())
            {
                scenarioTeardowns.Reverse();
                var teardownTimer      = new ExecutionTimer();
                var teardownAggregator = new ExceptionAggregator();
                foreach (var teardown in scenarioTeardowns)
                {
                    await Invoker.Invoke(() => teardown.Item2(teardown.Item1), teardownAggregator, teardownTimer);
                }

                summary.Time += teardownTimer.Total;

                if (teardownAggregator.HasExceptions)
                {
                    summary.Failed++;
                    summary.Total++;

                    var stepDisplayName = GetStepDisplayName(this.scenario.DisplayName, ++stepNumber, "(Teardown)");

                    this.messageBus.Queue(
                        new StepTest(this.scenario, stepDisplayName),
                        test => new TestFailed(test, teardownTimer.Total, null, teardownAggregator.ToException()),
                        this.cancellationTokenSource);
                }
            }

            return(summary);
        }
        private async Task <RunSummary> InvokeStepsAsync(
            ICollection <IStepDefinition> backGroundStepDefinitions, ICollection <IStepDefinition> scenarioStepDefinitions)
        {
            var filters = this.scenarioClass.Assembly.GetCustomAttributes(typeof(Attribute))
                          .Concat(this.scenarioClass.GetCustomAttributes(typeof(Attribute)))
                          .Concat(this.scenarioMethod.GetCustomAttributes(typeof(Attribute)))
                          .OfType <IFilter <IStepDefinition> >();

            var stepDefinitions = filters
                                  .Aggregate(
                backGroundStepDefinitions.Concat(scenarioStepDefinitions),
                (current, filter) => filter.Filter(current))
                                  .ToArray();

            var    summary    = new RunSummary();
            string skipReason = null;
            var    teardowns  = new List <Action>();
            var    stepNumber = 0;

            foreach (var stepDefinition in stepDefinitions)
            {
                stepDefinition.SkipReason = stepDefinition.SkipReason ?? skipReason;

                var stepDisplayName = GetStepDisplayName(
                    this.scenario.DisplayName,
                    ++stepNumber,
                    stepNumber <= backGroundStepDefinitions.Count,
                    stepDefinition.Text,
                    this.scenarioMethodArguments);

                var step = new Step(this.scenario, stepDisplayName);

                var interceptingBus = new DelegatingMessageBus(
                    this.messageBus,
                    message =>
                {
                    if (message is ITestFailed && stepDefinition.FailureBehavior == RemainingSteps.Skip)
                    {
                        skipReason = string.Format(
                            CultureInfo.InvariantCulture,
                            "Failed to execute preceding step: {0}",
                            step.DisplayName);
                    }
                });

                var stepRunner = new StepRunner(
                    step,
                    stepDefinition.Body,
                    interceptingBus,
                    this.scenarioClass,
                    this.constructorArguments,
                    this.scenarioMethod,
                    this.scenarioMethodArguments,
                    stepDefinition.SkipReason,
                    new ExceptionAggregator(this.aggregator),
                    this.cancellationTokenSource);

                summary.Aggregate(await stepRunner.RunAsync());
                teardowns.AddRange(stepRunner.Disposables.Select(disposable => (Action)disposable.Dispose)
                                   .Concat(stepDefinition.Teardowns.Where(teardown => teardown != null)).ToArray());
            }

            if (teardowns.Any())
            {
                teardowns.Reverse();
                var teardownTimer      = new ExecutionTimer();
                var teardownAggregator = new ExceptionAggregator();
                foreach (var teardown in teardowns)
                {
                    teardownTimer.Aggregate(() => teardownAggregator.Run(() => teardown()));
                }

                summary.Time += teardownTimer.Total;

                if (teardownAggregator.HasExceptions)
                {
                    summary.Failed++;
                    summary.Total++;

                    var stepDisplayName = GetStepDisplayName(
                        this.scenario.DisplayName,
                        ++stepNumber,
                        false,
                        "(Teardown)",
                        this.scenarioMethodArguments);

                    this.messageBus.Queue(
                        new Step(this.scenario, stepDisplayName),
                        test => new TestFailed(test, teardownTimer.Total, null, teardownAggregator.ToException()),
                        this.cancellationTokenSource);
                }
            }

            return(summary);
        }