private void RandomInputImpl(string inputCharsString, bool trimWhiteSpace, bool hasFieldsEnclosedInQuotes, Func <Random, char> chooseDelimiter)
        {
            // Using a hardcoded seed so our "random" tests are deterministic.
            const int seed       = 0;
            const int iterations = 1000;

            var inputChars = inputCharsString.ToArray();
            var random     = new Random(seed);

            for (var i = 0; i < iterations; i++)
            {
                var inputLength = random.Next(minValue: 1, maxValue: 1000);
                var input       = string.Join(string.Empty, Enumerable.Range(0, inputLength).Select(_ => inputChars[random.Next(0, inputChars.Length)]));
                var delimiter   = chooseDelimiter.Invoke(random);
                using (var expectedParser = CreateExpectedParser(input, trimWhiteSpace, hasFieldsEnclosedInQuotes))
                    using (var actualParser = CreateActualParser(input, trimWhiteSpace, hasFieldsEnclosedInQuotes))
                    {
                        if (delimiter != ',')
                        {
                            expectedParser.SetDelimiters(delimiter.ToString(CultureInfo.InvariantCulture));
                            actualParser.SetDelimiter(delimiter);
                        }

                        bool     endOfData;
                        int      logicalLineCounter = 0;
                        string[] previousFields     = null;
                        do
                        {
                            logicalLineCounter++;

                            bool actualEndOfData   = actualParser.EndOfData;
                            bool expectedEndOfData = expectedParser.EndOfData;
                            endOfData = actualEndOfData || expectedEndOfData;
                            CustomAssert.Equal(expectedEndOfData, actualEndOfData, $"EndOfData mismatch on iteration {i} with delimiter \"{delimiter}\" logical line {logicalLineCounter} for input: {input}");

                            var actualLineNumber   = actualParser.LineNumber;
                            var expectedLineNumber = expectedParser.LineNumber;
                            CustomAssert.Equal(expectedLineNumber, actualLineNumber, $"LineNumber mismatch on iteration {i} with delimiter \"{delimiter}\" on logical line {logicalLineCounter} for before fields: {string.Join(",", previousFields ?? Array.Empty<string>())}");

                            string[] actualFields;
                            CsvMalformedLineException actualException = null;
                            try
                            {
                                actualFields = actualParser.ReadFields();
                            }
                            catch (CsvMalformedLineException ex)
                            {
                                actualFields    = null;
                                actualException = ex;
                            }

                            string[] expectedFields;
                            MalformedLineException expectedException = null;
                            try
                            {
                                expectedFields = expectedParser.ReadFields();
                            }
                            catch (MalformedLineException ex)
                            {
                                expectedFields    = null;
                                expectedException = ex;
                            }

                            previousFields = expectedFields;

                            if (expectedException != null || actualException != null)
                            {
                                CustomAssert.NotNull(expectedException, $"Expected no exception but was {actualException?.GetType().Name} on iteration {i} with delimiter \"{delimiter}\" on logical line {logicalLineCounter}");
                                CustomAssert.NotNull(actualException, $"Expected {expectedException?.GetType().Name} but was no exception on iteration {i} with delimiter \"{delimiter}\" on logical line {logicalLineCounter}");
                                CustomAssert.Equal(expectedParser.ErrorLine, actualParser.ErrorLine, $"ErrorLine mismatch on iteration {i} with delimiter \"{delimiter}\" on logical line {logicalLineCounter}");

                                // Who know what they're doing for their line numbers.  It doesn't really matter if we exactly match probably?
                                //Assert.Equal(expectedParser.ErrorLineNumber, actualParser.ErrorLineNumber, $"ErrorLineNumber mismatch on iteration {i} on line {logicalLineCounter}");
                            }
                            CustomAssert.Equal(expectedFields, actualFields, $"ReadFields mismatch on iteration {i} with delimiter \"{delimiter}\" on logical line {logicalLineCounter} for input: {input}");
                        } while (!endOfData);
                    }
            }
        }
        public void RandomInput(
            [Values(
                 "1234567890,\n",
                 "1234567890,\n\"",
                 "2,\n\r\"",
                 "abcdefgh,\"\n\r\t ",
                 "a2/\\#,\"'\n\r\t "
                 )]
            string inputCharsString,
            [Values(true, false)] bool trimWhiteSpace,
            [Values(true, false)] bool hasFieldsEnclosedInQuotes
            )
        {
            // Using a hardcoded seed so our "random" tests are deterministic.
            const int seed       = 0;
            const int iterations = 1000;

            var inputChars = inputCharsString.ToArray();
            var random     = new Random(seed);

            for (var i = 0; i < iterations; i++)
            {
                var inputLength = random.Next(minValue: 1, maxValue: 1000);
                var input       = string.Join(string.Empty, Enumerable.Range(0, inputLength).Select(_ => inputChars[random.Next(0, inputChars.Length)]));

                using (var expectedParser = CreateExpectedParser(input, trimWhiteSpace, hasFieldsEnclosedInQuotes))
                    using (var actualParser = CreateActualParser(input, trimWhiteSpace, hasFieldsEnclosedInQuotes))
                    {
                        bool endOfData;
                        int  logicalLineCounter = 0;
                        do
                        {
                            logicalLineCounter++;

                            bool actualEndOfData   = actualParser.EndOfData;
                            bool expectedEndOfData = expectedParser.EndOfData;
                            endOfData = actualEndOfData || expectedEndOfData;
                            Assert.AreEqual(expectedEndOfData, actualEndOfData, $"EndOfData mismatch on iteration {i} logical line {logicalLineCounter} for input: {input}");

                            string[] actualFields;
                            CsvMalformedLineException actualException = null;
                            try
                            {
                                actualFields = actualParser.ReadFields();
                            }
                            catch (CsvMalformedLineException ex)
                            {
                                actualFields    = null;
                                actualException = ex;
                            }

                            string[] expectedFields;
                            MalformedLineException expectedException = null;
                            try
                            {
                                expectedFields = expectedParser.ReadFields();
                            }
                            catch (MalformedLineException ex)
                            {
                                expectedFields    = null;
                                expectedException = ex;
                            }

                            if (expectedException != null || actualException != null)
                            {
                                Assert.IsNotNull(expectedException, $"Expected no exception but was {actualException?.GetType().Name} on iteration {i} on logical line {logicalLineCounter}");
                                Assert.IsNotNull(actualException, $"Expected {expectedException?.GetType().Name} but was no exception on iteration {i} on logical line {logicalLineCounter}");
                                Assert.AreEqual(expectedParser.ErrorLine, actualParser.ErrorLine, $"ErrorLine mismatch on iteration {i} on logical line {logicalLineCounter}");
                                // Who know what they're doing for their line numbers.  It doesn't really matter if we exactly match probably?
                                //Assert.AreEqual(expectedParser.ErrorLineNumber, actualParser.ErrorLineNumber, $"ErrorLineNumber mismatch on iteration {i} on line {logicalLineCounter}");
                            }
                            CollectionAssert.AreEqual(expectedFields, actualFields, $"ReadFields mismatch on iteration {i} on logical line {logicalLineCounter} for input: {input}");
                        } while (!endOfData);
                    }
            }
        }