<commit_before>﻿namespace Fixie.Tests.Cases
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Threading.Tasks;
    using Assertions;
    using static Utility;

    public class ParameterizedCaseTests
    {
        class ParameterizedExecution : Execution
        {
            readonly Func<MethodInfo, IEnumerable<object?[]>> parameterSource;

            public ParameterizedExecution(Func<MethodInfo, IEnumerable<object?[]>> parameterSource)
                => this.parameterSource = parameterSource;

            public async Task RunAsync(TestAssembly testAssembly)
            {
                foreach (var test in testAssembly.Tests)
                {
                    try
                    {
                        if (test.HasParameters)
                        {
                            foreach (var parameters in parameterSource(test.Method))
                                await test.RunAsync(parameters);
                        }
                        else
                        {
                            await test.RunAsync();
                        }
                    }
                    catch (Exception exception)
                    {
                        await test.FailAsync(exception);
                    }
                }
            }
        }

        class FailFastParameterizedExecution : Execution
        {
            readonly Func<MethodInfo, IEnumerable<object?[]>> parameterSource;

            public FailFastParameterizedExecution(Func<MethodInfo, IEnumerable<object?[]>> parameterSource)
                => this.parameterSource = parameterSource;

            public async Task RunAsync(TestAssembly testAssembly)
            {
                foreach (var test in testAssembly.Tests)
                {
                    if (test.HasParameters)
                    {
                        foreach (var parameters in parameterSource(test.Method))
                            await test.RunAsync(parameters);
                    }
                    else
                    {
                        await test.RunAsync();
                    }
                }
            }
        }

        public async Task ShouldAllowRunningTestsMultipleTimesWithVaryingInputParameters()
        {
            var execution = new ParameterizedExecution(InputAttributeOrDefaultParameterSource);
            (await RunAsync<ParameterizedTestClass>(execution))
                .ShouldBe(
                    For<ParameterizedTestClass>(
                        ".IntArg(0) passed",
                        ".MultipleCasesFromAttributes(1, 1, 2) passed",
                        ".MultipleCasesFromAttributes(1, 2, 3) passed",
                        ".MultipleCasesFromAttributes(5, 5, 11) failed: Expected sum of 11 but was 10.",
                        ".ZeroArgs passed"));
        }

        public async Task ShouldFailWithClearExplanationWhenParameterCountsAreMismatched()
        {
            var execution = new ParameterizedExecution(method => new[]
            {
                new object[] { },
                new object[] {0},
                new object[] {0, 1},
                new object[] {0, 1, 2},
                new object[] {0, 1, 2, 3}
            });
            (await RunAsync<ParameterizedTestClass>(execution))
                .ShouldBe(
                    For<ParameterizedTestClass>(
                        ".IntArg failed: Parameter count mismatch.",
                        ".IntArg(0) passed",
                        ".IntArg(0, 1) failed: Parameter count mismatch.",
                        ".IntArg(0, 1, 2) failed: Parameter count mismatch.",
                        ".IntArg(0, 1, 2, 3) failed: Parameter count mismatch.",

                        ".MultipleCasesFromAttributes failed: Parameter count mismatch.",
                        ".MultipleCasesFromAttributes(0) failed: Parameter count mismatch.",
                        ".MultipleCasesFromAttributes(0, 1) failed: Parameter count mismatch.",
                        ".MultipleCasesFromAttributes(0, 1, 2) failed: Expected sum of 2 but was 1.",
                        ".MultipleCasesFromAttributes(0, 1, 2, 3) failed: Parameter count mismatch.",

                        ".ZeroArgs passed"));
        }

        public async Task ShouldFailWithClearExplanationWhenParameterGenerationThrows()
        {
            var execution = new ParameterizedExecution(BuggyParameterSource);
            (await RunAsync<ParameterizedTestClass>(execution))
                .ShouldBe(
                    For<ParameterizedTestClass>(
                        ".IntArg(0) passed",
                        ".IntArg(1) failed: Expected 0, but was 1",
                        ".IntArg failed: Exception thrown while attempting to yield input parameters for method: IntArg",

                        ".MultipleCasesFromAttributes(0) failed: Parameter count mismatch.",
                        ".MultipleCasesFromAttributes(1) failed: Parameter count mismatch.",
                        ".MultipleCasesFromAttributes failed: Exception thrown while attempting to yield input parameters for method: MultipleCasesFromAttributes",

                        ".ZeroArgs passed"));
        }

        public async Task ShouldSupportIsolatingFailuresToTheAffectedTestMethodWhenParameterGenerationThrows()
        {
            //Because the ParameterizedExecution convention explicitly catches and
            //handles these exceptions at an appropriate granularity, it demonstrates
            //how a failure can be isolated to the affected test method. A user may
            //wish to do this to isolate a problematic parameter generation scheme
            //while keeping as much of their build passing as possible.

            //Because the FailFastParameterizedExecution convention does not attempt
            //to handle these exceptions, the convention exits early and the runner
            //acknowledges that fact with failure and skip results on the elided tests.
            //A user may wish to do this to 'fail fast' when their testing infrastructure
            //is clearly in an untrustworthy state to begin with.

            var isolatedExecution = new ParameterizedExecution(BuggyParameterSource);
            (await RunAsync<ParameterizedTestClass>(isolatedExecution))
                .ShouldBe(
                    For<ParameterizedTestClass>(
                        ".IntArg(0) passed",
                        ".IntArg(1) failed: Expected 0, but was 1",
                        ".IntArg failed: Exception thrown while attempting to yield input parameters for method: IntArg",

                        ".MultipleCasesFromAttributes(0) failed: Parameter count mismatch.",
                        ".MultipleCasesFromAttributes(1) failed: Parameter count mismatch.",
                        ".MultipleCasesFromAttributes failed: Exception thrown while attempting to yield input parameters for method: MultipleCasesFromAttributes",

                        ".ZeroArgs passed"));

            var failFastExecution = new FailFastParameterizedExecution(BuggyParameterSource);
            (await RunAsync<ParameterizedTestClass>(failFastExecution))
                .ShouldBe(
                    For<ParameterizedTestClass>(
                        ".IntArg(0) passed",
                        ".IntArg(1) failed: Expected 0, but was 1",
                        ".IntArg failed: Exception thrown while attempting to yield input parameters for method: IntArg",

                        ".MultipleCasesFromAttributes failed: Exception thrown while attempting to yield input parameters for method: IntArg",
                        ".MultipleCasesFromAttributes skipped: This test did not run.",
                        
                        ".ZeroArgs failed: Exception thrown while attempting to yield input parameters for method: IntArg",
                        ".ZeroArgs skipped: This test did not run."));
        }

        public async Task ShouldFailWithClearExplanationWhenParameterGenerationExceptionPreventsGenericTypeParametersFromBeingResolvable()
        {
            var execution = new ParameterizedExecution(BuggyParameterSource);
            (await RunAsync<ConstrainedGenericTestClass>(execution))
                .ShouldBe(
                    For<ConstrainedGenericTestClass>(
                        ".ConstrainedGeneric<System.Int32>(0) passed",
                        ".ConstrainedGeneric<System.Int32>(1) passed",
                        ".ConstrainedGeneric<T> failed: Exception thrown while attempting to yield input parameters for method: ConstrainedGeneric",
                        ".UnconstrainedGeneric<System.Int32>(0) passed",
                        ".UnconstrainedGeneric<System.Int32>(1) passed",<commit_msg>Resolve duplication between test cases.<commit_after>﻿namespace Fixie.Tests.Cases
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Threading.Tasks;
    using Assertions;
    using static Utility;

    public class ParameterizedCaseTests
    {
        class ParameterizedExecution : Execution
        {
            readonly Func<MethodInfo, IEnumerable<object?[]>> parameterSource;

            public ParameterizedExecution(Func<MethodInfo, IEnumerable<object?[]>> parameterSource)
                => this.parameterSource = parameterSource;

            public async Task RunAsync(TestAssembly testAssembly)
            {
                foreach (var test in testAssembly.Tests)
                {
                    try
                    {
                        if (test.HasParameters)
                        {
                            foreach (var parameters in parameterSource(test.Method))
                                await test.RunAsync(parameters);
                        }
                        else
                        {
                            await test.RunAsync();
                        }
                    }
                    catch (Exception exception)
                    {
                        await test.FailAsync(exception);
                    }
                }
            }
        }

        class FailFastParameterizedExecution : Execution
        {
            readonly Func<MethodInfo, IEnumerable<object?[]>> parameterSource;

            public FailFastParameterizedExecution(Func<MethodInfo, IEnumerable<object?[]>> parameterSource)
                => this.parameterSource = parameterSource;

            public async Task RunAsync(TestAssembly testAssembly)
            {
                foreach (var test in testAssembly.Tests)
                {
                    if (test.HasParameters)
                    {
                        foreach (var parameters in parameterSource(test.Method))
                            await test.RunAsync(parameters);
                    }
                    else
                    {
                        await test.RunAsync();
                    }
                }
            }
        }

        public async Task ShouldAllowRunningTestsMultipleTimesWithVaryingInputParameters()
        {
            var execution = new ParameterizedExecution(InputAttributeOrDefaultParameterSource);
            (await RunAsync<ParameterizedTestClass>(execution))
                .ShouldBe(
                    For<ParameterizedTestClass>(
                        ".IntArg(0) passed",
                        ".MultipleCasesFromAttributes(1, 1, 2) passed",
                        ".MultipleCasesFromAttributes(1, 2, 3) passed",
                        ".MultipleCasesFromAttributes(5, 5, 11) failed: Expected sum of 11 but was 10.",
                        ".ZeroArgs passed"));
        }

        public async Task ShouldFailWithClearExplanationWhenParameterCountsAreMismatched()
        {
            var execution = new ParameterizedExecution(method => new[]
            {
                new object[] { },
                new object[] {0},
                new object[] {0, 1},
                new object[] {0, 1, 2},
                new object[] {0, 1, 2, 3}
            });
            (await RunAsync<ParameterizedTestClass>(execution))
                .ShouldBe(
                    For<ParameterizedTestClass>(
                        ".IntArg failed: Parameter count mismatch.",
                        ".IntArg(0) passed",
                        ".IntArg(0, 1) failed: Parameter count mismatch.",
                        ".IntArg(0, 1, 2) failed: Parameter count mismatch.",
                        ".IntArg(0, 1, 2, 3) failed: Parameter count mismatch.",

                        ".MultipleCasesFromAttributes failed: Parameter count mismatch.",
                        ".MultipleCasesFromAttributes(0) failed: Parameter count mismatch.",
                        ".MultipleCasesFromAttributes(0, 1) failed: Parameter count mismatch.",
                        ".MultipleCasesFromAttributes(0, 1, 2) failed: Expected sum of 2 but was 1.",
                        ".MultipleCasesFromAttributes(0, 1, 2, 3) failed: Parameter count mismatch.",

                        ".ZeroArgs passed"));
        }

        public async Task ShouldSupportIsolatingFailuresToTheAffectedTestMethodWhenParameterGenerationThrows()
        {
            var isolatedExecution = new ParameterizedExecution(BuggyParameterSource);
            (await RunAsync<ParameterizedTestClass>(isolatedExecution))
                .ShouldBe(
                    For<ParameterizedTestClass>(
                        ".IntArg(0) passed",
                        ".IntArg(1) failed: Expected 0, but was 1",
                        ".IntArg failed: Exception thrown while attempting to yield input parameters for method: IntArg",

                        ".MultipleCasesFromAttributes(0) failed: Parameter count mismatch.",
                        ".MultipleCasesFromAttributes(1) failed: Parameter count mismatch.",
                        ".MultipleCasesFromAttributes failed: Exception thrown while attempting to yield input parameters for method: MultipleCasesFromAttributes",

                        ".ZeroArgs passed"));
        }

        public async Task ShouldSupportEndingTheRunEarlyWhenParameterGenerationThrows()
        {
            var failFastExecution = new FailFastParameterizedExecution(BuggyParameterSource);
            (await RunAsync<ParameterizedTestClass>(failFastExecution))
                .ShouldBe(
                    For<ParameterizedTestClass>(
                        ".IntArg(0) passed",
                        ".IntArg(1) failed: Expected 0, but was 1",
                        ".IntArg failed: Exception thrown while attempting to yield input parameters for method: IntArg",

                        ".MultipleCasesFromAttributes failed: Exception thrown while attempting to yield input parameters for method: IntArg",
                        ".MultipleCasesFromAttributes skipped: This test did not run.",
                        
                        ".ZeroArgs failed: Exception thrown while attempting to yield input parameters for method: IntArg",
                        ".ZeroArgs skipped: This test did not run."));
        }

        public async Task ShouldFailWithClearExplanationWhenParameterGenerationExceptionPreventsGenericTypeParametersFromBeingResolvable()
        {
            var execution = new ParameterizedExecution(BuggyParameterSource);
            (await RunAsync<ConstrainedGenericTestClass>(execution))
                .ShouldBe(
                    For<ConstrainedGenericTestClass>(
                        ".ConstrainedGeneric<System.Int32>(0) passed",
                        ".ConstrainedGeneric<System.Int32>(1) passed",
                        ".ConstrainedGeneric<T> failed: Exception thrown while attempting to yield input parameters for method: ConstrainedGeneric",
                        ".UnconstrainedGeneric<System.Int32>(0) passed",
                        ".UnconstrainedGeneric<System.Int32>(1) passed",
{'fraction_non_alphanumeric': 0.07285138070121006, 'fraction_numerical': 0.008935774123487434, 'mean_word_length': 1.5980976946638723, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1683662', 'n_tokens_mistral': 3599, 'n_tokens_neox': 3268, 'n_words': 952}