public async Task PerformanceTest_Valid_Feeds()
        {
            foreach (var feedFile in Directory.EnumerateFiles($@"{TestDataPath}\Valid", "*.xml"))
            {
                using (var feedStream = new FileStream(feedFile, FileMode.Open))
                    using (var reader = new StreamReader(feedStream))
                    {
                        var feedContents = reader.ReadToEnd();
                        reader.BaseStream.Position = 0;
                        var episodeCount = Regex.Matches(feedContents, "<item>").Count;

                        var feedReader = new FeedReader(reader, _logger);
                        await feedReader.SkipPreheader();

                        feedReader.ReadDocumentHeader();

                        var showXml = await feedReader.GetShowXmlAsync();

                        showXml.Should().NotBeNull();

                        XDocument episodeXml;
                        for (var episodeIndex = 0; episodeIndex < episodeCount; episodeIndex++)
                        {
                            episodeXml = await feedReader.GetNextEpisodeXmlAsync();

                            episodeXml.Should().NotBeNull("because we have {0} and there should be {1}", episodeIndex + 1, episodeCount);
                        }
                        episodeXml = await feedReader.GetNextEpisodeXmlAsync();

                        episodeXml.Should().BeNull();
                    }
            }
        }
        public async Task SkipPreheader_GarbageLong_Throws()
        {
            var input = "".PadRight(10000, 'z') + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";

            FeedReader feedReader;

            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(input)))
                using (var reader = new StreamReader(stream))
                {
                    feedReader = new FeedReader(reader, _logger);
                    Func <Task> act = async() => await feedReader.SkipPreheader();

                    await act.Should().ThrowAsync <InvalidPodcastFeedException>();
                }
        }
        public async Task SkipPreheader_Empty_Throws()
        {
            var input = "";

            FeedReader feedReader;

            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(input)))
                using (var reader = new StreamReader(stream))
                {
                    feedReader = new FeedReader(reader, _logger);
                    Func <Task> act = async() => await feedReader.SkipPreheader();

                    await act.Should().ThrowAsync <InvalidPodcastFeedException>();
                }
        }
        public async Task SkipPreheader_StartsXmlWithExpectedLength_DoesNotThrow()
        {
            var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";

            FeedReader feedReader;

            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(input)))
                using (var reader = new StreamReader(stream))
                {
                    feedReader = new FeedReader(reader, _logger);
                    Func <Task> act = async() => await feedReader.SkipPreheader();

                    await act.Should().NotThrowAsync <InvalidPodcastFeedException>();
                }
        }
        public async Task SkipPreheader_StartsRss_DoesNotThrow()
        {
            var input = "<rss version=\"2.0\"\r\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\r\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\r\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\r\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\r\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\r\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\r\n\txmlns:georss=\"http://www.georss.org/georss\" xmlns:geo=\"http://www.w3.org/2003/01/geo/wgs84_pos#\" \r\n\txmlns:itunes=\"http://www.itunes.com/dtds/podcast-1.0.dtd\"\r\nxmlns:media=\"http://search.yahoo.com/mrss/\"\r\n\t>";

            FeedReader feedReader;

            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(input)))
                using (var reader = new StreamReader(stream))
                {
                    feedReader = new FeedReader(reader, _logger);
                    Func <Task> act = async() => await feedReader.SkipPreheader();

                    await act.Should().NotThrowAsync <InvalidPodcastFeedException>();
                }
        }
        public async Task GetShowAsync_AmpersandAfterCDataContent_DoesNotThrow()
        {
            var input = File.ReadAllText($@"{TestDataRoot}Invalid\samplefeed_ampersandaftercdatacontent.xml");

            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(input)))
                using (var reader = new StreamReader(stream))
                {
                    var feedReader = new FeedReader(reader, _logger);
                    await feedReader.SkipPreheader();

                    feedReader.ReadDocumentHeader();
                    Func <Task> act = async() => await feedReader.GetShowXmlAsync();

                    await act.Should().NotThrowAsync <InvalidPodcastFeedException>();
                }
        }
        public async Task ReadDocumentMetadata_InvalidChannel_Throws()
        {
            var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\"\r\n\txmlns:content=\"http://purl.org/rss/1.0/modules/content/\"\r\n\txmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"\r\n\txmlns:dc=\"http://purl.org/dc/elements/1.1/\"\r\n\txmlns:atom=\"http://www.w3.org/2005/Atom\"\r\n\txmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"\r\n\txmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"\r\n\txmlns:georss=\"http://www.georss.org/georss\" xmlns:geo=\"http://www.w3.org/2003/01/geo/wgs84_pos#\" \r\n\txmlns:itunes=\"http://www.itunes.com/dtds/podcast-1.0.dtd\"\r\nxmlns:media=\"http://search.yahoo.com/mrss/\"\r\n\t>\r\n\r\n<flannel>";

            FeedReader feedReader;

            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(input)))
                using (var reader = new StreamReader(stream))
                {
                    feedReader = new FeedReader(reader, _logger);
                    await feedReader.SkipPreheader();

                    Action act = () => feedReader.ReadDocumentHeader();
                    act.Should().Throw <InvalidPodcastFeedException>();
                }
        }
        public async Task GetNextEpisodeAsync_OneEpisode_Returns()
        {
            var input = File.ReadAllText($@"{TestDataRoot}Invalid\samplefeed_oneepisodecontent.xml");

            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(input)))
                using (var reader = new StreamReader(stream))
                {
                    var feedReader = new FeedReader(reader, _logger);
                    await feedReader.SkipPreheader();

                    feedReader.ReadDocumentHeader();
                    await feedReader.GetShowXmlAsync();

                    var xml = await feedReader.GetNextEpisodeXmlAsync();

                    xml.Should().NotBeNull();
                }
        }
        public async Task GetShowAsync_TooLongContent_Throws()
        {
            var input = File.ReadAllText($@"{TestDataRoot}Invalid\samplefeed_toolongcontent.xml");

            FeedReader feedReader;

            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(input)))
                using (var reader = new StreamReader(stream))
                {
                    feedReader = new FeedReader(reader, _logger);
                    await feedReader.SkipPreheader();

                    feedReader.ReadDocumentHeader();
                    Func <Task> act = async() => await feedReader.GetShowXmlAsync();

                    await act.Should().ThrowAsync <InvalidPodcastFeedException>();
                }
        }
        public async Task GetShowAsync_PreChannelGarbage_DoesNotThrow()
        {
            var input = File.ReadAllText($@"{TestDataRoot}Invalid\samplefeed_prechannelgarbage.xml");

            FeedReader feedReader;

            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(input)))
                using (var reader = new StreamReader(stream))
                {
                    feedReader = new FeedReader(reader, _logger);
                    await feedReader.SkipPreheader();

                    feedReader.ReadDocumentHeader();
                    Func <Task> act = async() => await feedReader.GetShowXmlAsync();

                    await act.Should().NotThrowAsync <InvalidPodcastFeedException>();
                }
        }