Add more Syndication tests (dotnet/corefx#35107)
authorHugh Bellamy <hughbellars@gmail.com>
Tue, 5 Mar 2019 19:49:26 +0000 (19:49 +0000)
committerJiayi Yu <14067510+yujayee@users.noreply.github.com>
Tue, 5 Mar 2019 19:49:26 +0000 (11:49 -0800)
* Add more Syndication tests

Commit migrated from https://github.com/dotnet/corefx/commit/013fd82b1bb218d171300e127a487ebc39e1fc2e

src/libraries/System.ServiceModel.Syndication/tests/System.ServiceModel.Syndication.Tests.csproj
src/libraries/System.ServiceModel.Syndication/tests/System/ServiceModel/Syndication/Atom10FeedFormatterTests.cs
src/libraries/System.ServiceModel.Syndication/tests/System/ServiceModel/Syndication/Rss20FeedFormatterTests.cs [new file with mode: 0644]
src/libraries/System.ServiceModel.Syndication/tests/System/ServiceModel/Syndication/Rss20ItemFormatterTests.cs
src/libraries/System.ServiceModel.Syndication/tests/System/ServiceModel/Syndication/SyndicationFeedTests.cs
src/libraries/System.ServiceModel.Syndication/tests/System/ServiceModel/Syndication/SyndicationFeedTests.netcoreapp.cs
src/libraries/System.ServiceModel.Syndication/tests/netcoreapp/BasicScenarioTests.netcoreapp.cs

index a244d8c..d7021a0 100644 (file)
@@ -6,6 +6,7 @@
   <ItemGroup>
     <Compile Include="BasicScenarioTests.cs" />
     <Compile Include="System\ServiceModel\Syndication\Rss20ItemFormatterTests.cs" />
+    <Compile Include="System\ServiceModel\Syndication\Rss20FeedFormatterTests.cs" />
     <Compile Include="System\ServiceModel\Syndication\Atom10ItemFormatterTests.cs" />
     <Compile Include="System\ServiceModel\Syndication\Atom10FeedFormatterTests.cs" />
     <Compile Include="System\ServiceModel\Syndication\SyndicationFeedFormatterTests.cs" />
index fcb68ef..4429cb7 100644 (file)
@@ -1071,7 +1071,7 @@ namespace System.ServiceModel.Syndication.Tests
                 }
                 Assert.Equal("contributor_email", itemThirdContributor.Email);
                 Assert.Equal("contributor_name", itemThirdContributor.Name);
-                Assert.Equal("contributor_uri", thirdContributor.Uri);
+                Assert.Equal("contributor_uri", itemThirdContributor.Uri);
 
                 if (preserveAttributeExtensions)
                 {
@@ -1779,6 +1779,50 @@ namespace System.ServiceModel.Syndication.Tests
             });
         }
 
+        [Theory]
+        [InlineData(true, true)]
+        [InlineData(false, false)]
+        public void Read_CustomReadItems_ReturnsExpected(bool preserveAttributeExtensions, bool preserveElementExtensions)
+        {
+            using (var stringReader = new StringReader(@"<feed xmlns=""http://www.w3.org/2005/Atom""><entry></entry><entry></entry></feed>"))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                var formatter = new CustomAtom10FeedFormatter()
+                {
+                    PreserveAttributeExtensions = preserveAttributeExtensions,
+                    PreserveElementExtensions = preserveElementExtensions
+                };
+                formatter.ReadFrom(reader);
+
+                SyndicationFeed feed = formatter.Feed;
+                Assert.Empty(feed.AttributeExtensions);
+                Assert.Empty(feed.Authors);
+                Assert.Null(feed.BaseUri);
+                Assert.Empty(feed.Categories);
+                Assert.Empty(feed.Contributors);
+                Assert.Null(feed.Copyright);
+                Assert.Null(feed.Description);
+                Assert.Empty(feed.ElementExtensions);
+                Assert.Null(feed.Generator);
+                Assert.Null(feed.Id);
+                Assert.Null(feed.ImageUrl);
+                Assert.Single(feed.Items);
+                Assert.Null(feed.Language);
+                Assert.Equal(DateTimeOffset.MinValue, feed.LastUpdatedTime);
+                Assert.Empty(feed.Links);
+                Assert.Null(feed.Title);
+            }
+        }
+
+        private class CustomAtom10FeedFormatter : Atom10FeedFormatter
+        {
+            protected override IEnumerable<SyndicationItem> ReadItems(XmlReader reader, SyndicationFeed feed, out bool areAllItemsRead)
+            {
+                areAllItemsRead = false;
+                return new SyndicationItem[] { new SyndicationItem() };
+            }
+        }
+
         private static void VerifyRead(string xmlString, bool preserveAttributeExtensions, bool preserveElementExtensions, Action<SyndicationFeed> verifyAction)
         {
             // ReadFrom.
diff --git a/src/libraries/System.ServiceModel.Syndication/tests/System/ServiceModel/Syndication/Rss20FeedFormatterTests.cs b/src/libraries/System.ServiceModel.Syndication/tests/System/ServiceModel/Syndication/Rss20FeedFormatterTests.cs
new file mode 100644 (file)
index 0000000..a0deab1
--- /dev/null
@@ -0,0 +1,2041 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Xml;
+using System.Xml.Linq;
+using System.Xml.Serialization;
+using Xunit;
+
+namespace System.ServiceModel.Syndication.Tests
+{
+    public partial class Rss20FeedFormatterTests
+    {
+        [Fact]
+        public void Ctor_Default()
+        {
+            var formatter = new Formatter();
+            Assert.Null(formatter.Feed);
+            Assert.Equal(typeof(SyndicationFeed), formatter.FeedTypeEntryPoint);
+            Assert.True(formatter.PreserveAttributeExtensions);
+            Assert.True(formatter.PreserveElementExtensions);
+            Assert.Equal("Rss20", formatter.Version);
+        }
+
+        [Fact]
+        public void Ctor_GenericDefault()
+        {
+            var formatter = new GenericFormatter<SyndicationFeed>();
+            Assert.Null(formatter.Feed);
+            Assert.Equal(typeof(SyndicationFeed), formatter.FeedTypeEntryPoint);
+            Assert.True(formatter.PreserveAttributeExtensions);
+            Assert.True(formatter.PreserveElementExtensions);
+            Assert.Equal("Rss20", formatter.Version);
+        }
+
+        [Fact]
+        public void Ctor_SyndicationFeed()
+        {
+            var feed = new SyndicationFeed();
+            var formatter = new Formatter(feed);
+            Assert.Same(feed, formatter.Feed);
+            Assert.Equal(typeof(SyndicationFeed), formatter.FeedTypeEntryPoint);
+            Assert.True(formatter.PreserveAttributeExtensions);
+            Assert.True(formatter.PreserveElementExtensions);
+            Assert.Equal("Rss20", formatter.Version);
+        }
+
+        [Fact]
+        public void Ctor_GenericSyndicationFeed()
+        {
+            var feed = new SyndicationFeed();
+            var formatter = new GenericFormatter<SyndicationFeed>(feed);
+            Assert.Same(feed, formatter.Feed);
+            Assert.Equal(typeof(SyndicationFeed), formatter.FeedTypeEntryPoint);
+            Assert.True(formatter.PreserveAttributeExtensions);
+            Assert.True(formatter.PreserveElementExtensions);
+            Assert.Equal("Rss20", formatter.Version);
+        }
+
+        [Theory]
+        [InlineData(true)]
+        [InlineData(false)]
+        public void Ctor_SyndicationFeed_Bool(bool serializeExtensionsAsAtom)
+        {
+            var feed = new SyndicationFeed();
+            var formatter = new Formatter(feed, serializeExtensionsAsAtom);
+            Assert.Same(feed, formatter.Feed);
+            Assert.Equal(typeof(SyndicationFeed), formatter.FeedTypeEntryPoint);
+            Assert.True(formatter.PreserveAttributeExtensions);
+            Assert.True(formatter.PreserveElementExtensions);
+            Assert.Equal("Rss20", formatter.Version);
+        }
+
+        [Theory]
+        [InlineData(true)]
+        [InlineData(false)]
+        public void Ctor_GenericSyndicationFeed_Bool(bool serializeExtensionsAsAtom)
+        {
+            var feed = new SyndicationFeed();
+            var formatter = new GenericFormatter<SyndicationFeed>(feed, serializeExtensionsAsAtom);
+            Assert.Same(feed, formatter.Feed);
+            Assert.Equal(typeof(SyndicationFeed), formatter.FeedTypeEntryPoint);
+            Assert.True(formatter.PreserveAttributeExtensions);
+            Assert.True(formatter.PreserveElementExtensions);
+            Assert.Equal("Rss20", formatter.Version);
+        }
+
+        [Fact]
+        public void Ctor_NullFeedToWrite_ThrowsArgumentNullException()
+        {
+            AssertExtensions.Throws<ArgumentNullException>("feedToWrite", () => new Rss20FeedFormatter((SyndicationFeed)null));
+            AssertExtensions.Throws<ArgumentNullException>("feedToWrite", () => new Rss20FeedFormatter<SyndicationFeed>(null));
+        }
+
+        [Theory]
+        [InlineData(typeof(SyndicationFeed))]
+        [InlineData(typeof(SyndicationFeedSubclass))]
+        public void Ctor_Type(Type feedTypeToCreate)
+        {
+            var formatter = new Formatter(feedTypeToCreate);
+            Assert.Null(formatter.Feed);
+            Assert.Equal(feedTypeToCreate, formatter.FeedTypeEntryPoint);
+            Assert.True(formatter.PreserveAttributeExtensions);
+            Assert.True(formatter.PreserveElementExtensions);
+            Assert.Equal("Rss20", formatter.Version);
+        }
+
+        [Fact]
+        public void Ctor_NullFeedTypeToCreate_ThrowsArgumentNullException()
+        {
+            AssertExtensions.Throws<ArgumentNullException>("feedTypeToCreate", () => new Rss20FeedFormatter((Type)null));
+        }
+
+        [Fact]
+        public void Ctor_InvalidFeedTypeToCreate_ThrowsArgumentException()
+        {
+            AssertExtensions.Throws<ArgumentException>("feedTypeToCreate", () => new Rss20FeedFormatter(typeof(int)));
+        }
+
+        [Fact]
+        public void GetSchema_Invoke_ReturnsNull()
+        {
+            IXmlSerializable formatter = new Rss20FeedFormatter();
+            Assert.Null(formatter.GetSchema());
+        }
+
+        public static IEnumerable<object[]> WriteTo_TestData()
+        {
+            // Empty item.
+            foreach (bool serializeExtensionsAsAtom in new bool[] { true, false })
+            {
+                yield return new object[]
+                {
+                    new SyndicationFeed(),
+                    serializeExtensionsAsAtom,
+@"    <channel>
+        <title />
+        <description />
+    </channel>"
+                };
+            }
+
+            // Full item.
+            SyndicationPerson CreatePerson(string prefix)
+            {
+                var person = new SyndicationPerson();
+                person.AttributeExtensions.Add(new XmlQualifiedName(prefix + "_name1"), null);
+                person.AttributeExtensions.Add(new XmlQualifiedName(prefix + "_name2", prefix + "_namespace"), "");
+                person.AttributeExtensions.Add(new XmlQualifiedName(prefix + "_name3", prefix + "_namespace"), prefix + "_value");
+                person.AttributeExtensions.Add(new XmlQualifiedName(prefix + "_name4", "xmlns"), "");
+
+                person.ElementExtensions.Add(new ExtensionObject { Value = 10 });
+
+                person.Email = prefix + "_email";
+
+                person.Name = prefix + "_name";
+
+                person.Uri = prefix + "_uri";
+
+                return person;
+            }
+
+            TextSyndicationContent CreateContent(string prefix)
+            {
+                var content = new TextSyndicationContent(prefix + "_title", TextSyndicationContentKind.Html);
+
+                content.AttributeExtensions.Add(new XmlQualifiedName(prefix + "_name1"), null);
+                content.AttributeExtensions.Add(new XmlQualifiedName(prefix + "_name2", prefix + "_namespace"), "");
+                content.AttributeExtensions.Add(new XmlQualifiedName(prefix + "_name3", prefix + "_namespace"), prefix + "_value");
+                content.AttributeExtensions.Add(new XmlQualifiedName(prefix + "_name4", "xmlns"), "");
+
+                return content;
+            }
+
+            SyndicationCategory CreateCategory(string prefix)
+            {
+                var category = new SyndicationCategory();
+                category.AttributeExtensions.Add(new XmlQualifiedName(prefix + "category_name1"), null);
+                category.AttributeExtensions.Add(new XmlQualifiedName(prefix + "category_name2", prefix + "category_namespace"), "");
+                category.AttributeExtensions.Add(new XmlQualifiedName(prefix + "category_name3", prefix + "category_namespace"), prefix + "category_value");
+                category.AttributeExtensions.Add(new XmlQualifiedName(prefix + "category_name4", "xmlns"), "");
+
+                category.ElementExtensions.Add(new ExtensionObject { Value = 10 });
+
+                category.Label = prefix + "category_label";
+
+                category.Name = prefix + "category_name";
+
+                category.Scheme = prefix + "category_scheme";
+
+                return category;
+            }
+
+            SyndicationLink CreateLink(string prefix)
+            {
+                var link = new SyndicationLink();
+                link.AttributeExtensions.Add(new XmlQualifiedName(prefix + "_name1"), null);
+                link.AttributeExtensions.Add(new XmlQualifiedName(prefix + "_name2", prefix + "_namespace"), "");
+                link.AttributeExtensions.Add(new XmlQualifiedName(prefix + "_name3", prefix + "_namespace"), prefix + "_value");
+                link.AttributeExtensions.Add(new XmlQualifiedName(prefix + "_name4", "xmlns"), "");
+
+                link.BaseUri = new Uri("http://" + prefix + "_url.com");
+
+                link.ElementExtensions.Add(new ExtensionObject { Value = 10 });
+
+                link.Length = 10;
+
+                link.MediaType = prefix + "_mediaType";
+
+                link.RelationshipType = prefix + "_relationshipType";
+
+                link.Title = prefix + "_title";
+
+                link.Uri = new Uri("http://" + prefix +"_uri.com");
+
+                return link;
+            }
+
+            var attributeSyndicationCategory = new SyndicationCategory
+            {
+                Name = "name",
+                Label = "label",
+                Scheme = "scheme"
+            };
+            attributeSyndicationCategory.AttributeExtensions.Add(new XmlQualifiedName("term"), "term_value");
+            attributeSyndicationCategory.AttributeExtensions.Add(new XmlQualifiedName("label"), "label_value");
+            attributeSyndicationCategory.AttributeExtensions.Add(new XmlQualifiedName("scheme"), "scheme_value");
+
+            var attributeSyndicationLink = new SyndicationLink
+            {
+                RelationshipType = "link_relationshipType",
+                MediaType = "link_mediaType",
+                Title = "link_title",
+                Length = 10,
+                Uri = new Uri("http://link_uri.com")
+            };
+            attributeSyndicationLink.AttributeExtensions.Add(new XmlQualifiedName("rel"), "rel_value");
+            attributeSyndicationLink.AttributeExtensions.Add(new XmlQualifiedName("type"), "type_value");
+            attributeSyndicationLink.AttributeExtensions.Add(new XmlQualifiedName("title"), "title_value");
+            attributeSyndicationLink.AttributeExtensions.Add(new XmlQualifiedName("length"), "100");
+            attributeSyndicationLink.AttributeExtensions.Add(new XmlQualifiedName("href"), "href_value");
+
+            var fullSyndicationItem = new SyndicationItem();
+            fullSyndicationItem.AttributeExtensions.Add(new XmlQualifiedName("item_name1"), null);
+            fullSyndicationItem.AttributeExtensions.Add(new XmlQualifiedName("item_name2", "item_namespace"), "");
+            fullSyndicationItem.AttributeExtensions.Add(new XmlQualifiedName("item_name3", "item_namespace"), "item_value");
+            fullSyndicationItem.AttributeExtensions.Add(new XmlQualifiedName("item_name4", "xmlns"), "");
+
+            fullSyndicationItem.Authors.Add(new SyndicationPerson());
+            fullSyndicationItem.Authors.Add(CreatePerson("author"));
+
+            fullSyndicationItem.BaseUri = new Uri("http://microsoft/relative");
+
+            fullSyndicationItem.Categories.Add(new SyndicationCategory());
+            fullSyndicationItem.Categories.Add(CreateCategory(""));
+            fullSyndicationItem.Categories.Add(attributeSyndicationCategory);
+
+            fullSyndicationItem.Content = CreateContent("content");
+
+            fullSyndicationItem.Contributors.Add(new SyndicationPerson());
+            fullSyndicationItem.Contributors.Add(CreatePerson("contributor"));
+
+            fullSyndicationItem.Copyright = CreateContent("copyright");
+
+            fullSyndicationItem.ElementExtensions.Add(new ExtensionObject { Value = 10 });
+
+            fullSyndicationItem.Id = "id";
+
+            fullSyndicationItem.LastUpdatedTime = DateTimeOffset.MinValue.AddTicks(100);
+
+            fullSyndicationItem.Links.Add(new SyndicationLink());
+            fullSyndicationItem.Links.Add(CreateLink("link"));
+            fullSyndicationItem.Links.Add(attributeSyndicationLink);
+
+            fullSyndicationItem.PublishDate = DateTimeOffset.MinValue.AddTicks(200);
+
+            fullSyndicationItem.Summary = CreateContent("summary");
+
+            fullSyndicationItem.Title = CreateContent("title");
+
+            var fullSyndicationFeed = new SyndicationFeed();
+
+            fullSyndicationFeed.AttributeExtensions.Add(new XmlQualifiedName("feed_name1"), null);
+            fullSyndicationFeed.AttributeExtensions.Add(new XmlQualifiedName("feed_name2", "feed_namespace"), "");
+            fullSyndicationFeed.AttributeExtensions.Add(new XmlQualifiedName("feed_name3", "feed_namespace"), "feed_value");
+            fullSyndicationFeed.AttributeExtensions.Add(new XmlQualifiedName("feed_name4", "xmlns"), "");
+
+            fullSyndicationFeed.Authors.Add(new SyndicationPerson());
+            fullSyndicationFeed.Authors.Add(CreatePerson("feedauthor"));
+
+            fullSyndicationFeed.BaseUri = new Uri("http://microsoft.com");
+
+            fullSyndicationFeed.Categories.Add(new SyndicationCategory());
+            fullSyndicationFeed.Categories.Add(CreateCategory("feed"));
+            fullSyndicationItem.Categories.Add(attributeSyndicationCategory);
+
+            fullSyndicationFeed.Contributors.Add(new SyndicationPerson());
+            fullSyndicationFeed.Contributors.Add(CreatePerson("feedauthor_"));
+
+            fullSyndicationFeed.Copyright = CreateContent("feedcopyright");
+
+            fullSyndicationFeed.Description = CreateContent("feeddescription");
+
+            fullSyndicationFeed.ElementExtensions.Add(new ExtensionObject { Value = 10 });
+
+            fullSyndicationFeed.Generator = "generator";
+
+            fullSyndicationFeed.Id = "id";
+
+            fullSyndicationFeed.ImageUrl = new Uri("http://imageurl.com");
+
+            fullSyndicationFeed.Items = new SyndicationItem[] { new SyndicationItem() { Id = "id", LastUpdatedTime = DateTimeOffset.MinValue.AddTicks(1) }, fullSyndicationItem };
+
+            fullSyndicationFeed.Language = "language";
+
+            fullSyndicationFeed.LastUpdatedTime = DateTimeOffset.MinValue.AddYears(1);
+
+            fullSyndicationFeed.Links.Add(new SyndicationLink());
+            fullSyndicationFeed.Links.Add(CreateLink("syndicationlink"));
+            fullSyndicationFeed.Links.Add(attributeSyndicationLink);
+
+            fullSyndicationFeed.Title = CreateContent("feedtitle");
+
+            foreach (bool serializeExtensionsAsAtom in new bool[] { true, false })
+            {
+                yield return new object[]
+                {
+                    fullSyndicationFeed,
+                    serializeExtensionsAsAtom,
+@"    <channel xml:base=""http://microsoft.com/"" feed_name1="""" d2p1:feed_name2="""" d2p1:feed_name3=""feed_value"" d2p2:feed_name4="""" xmlns:d2p2=""xmlns"" xmlns:d2p1=""feed_namespace"">
+        <title>feedtitle_title</title>
+        <description>feeddescription_title</description>
+        <language>language</language>
+        <copyright>feedcopyright_title</copyright>
+        <a10:author />
+        <a10:author feedauthor_name1="""" d3p1:feedauthor_name2="""" d3p1:feedauthor_name3=""feedauthor_value"" d2p2:feedauthor_name4="""" xmlns:d3p1=""feedauthor_namespace"">
+            <a10:name>feedauthor_name</a10:name>
+            <a10:uri>feedauthor_uri</a10:uri>
+            <a10:email>feedauthor_email</a10:email>
+            <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                <Value>10</Value>
+            </Rss20FeedFormatterTests.ExtensionObject>
+        </a10:author>
+        <lastBuildDate>Tue, 01 Jan 0002 00:00:00 Z</lastBuildDate>
+        <category />
+        <category feedcategory_name1="""" d3p1:feedcategory_name2="""" d3p1:feedcategory_name3=""feedcategory_value"" d2p2:feedcategory_name4="""" domain=""feedcategory_scheme"" xmlns:d3p1=""feedcategory_namespace"">feedcategory_name</category>
+        <generator>generator</generator>
+        <a10:contributor />
+        <a10:contributor feedauthor__name1="""" d3p1:feedauthor__name2="""" d3p1:feedauthor__name3=""feedauthor__value"" d2p2:feedauthor__name4="""" xmlns:d3p1=""feedauthor__namespace"">
+            <a10:name>feedauthor__name</a10:name>
+            <a10:uri>feedauthor__uri</a10:uri>
+            <a10:email>feedauthor__email</a10:email>
+            <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                <Value>10</Value>
+            </Rss20FeedFormatterTests.ExtensionObject>
+        </a10:contributor>
+        <image>
+            <url>http://imageurl.com/</url>
+            <title>feedtitle_title</title>
+            <link />
+        </image>
+        <a10:id>id</a10:id>
+        <a10:link href="""" />
+        <a10:link xml:base=""http://syndicationlink_url.com/"" syndicationlink_name1="""" d3p1:syndicationlink_name2="""" d3p1:syndicationlink_name3=""syndicationlink_value"" d2p2:syndicationlink_name4="""" rel=""syndicationlink_relationshipType"" type=""syndicationlink_mediaType"" title=""syndicationlink_title"" length=""10"" href=""http://syndicationlink_uri.com/"" xmlns:d3p1=""syndicationlink_namespace"">
+            <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                <Value>10</Value>
+            </Rss20FeedFormatterTests.ExtensionObject>
+        </a10:link>
+        <a10:link rel=""rel_value"" type=""type_value"" title=""title_value"" length=""100"" href=""href_value"" />
+        <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+            <Value>10</Value>
+        </Rss20FeedFormatterTests.ExtensionObject>
+        <item>
+            <guid isPermaLink=""false"">id</guid>
+            <description />
+            <a10:updated>0001-01-01T00:00:00Z</a10:updated>
+        </item>
+        <item xml:base=""http://microsoft/relative"" item_name1="""" d3p1:item_name2="""" d3p1:item_name3=""item_value"" d2p2:item_name4="""" xmlns:d3p1=""item_namespace"">
+            <guid isPermaLink=""false"">id</guid>
+            <a10:author />
+            <a10:author author_name1="""" d4p1:author_name2="""" d4p1:author_name3=""author_value"" d2p2:author_name4="""" xmlns:d4p1=""author_namespace"">
+                <a10:name>author_name</a10:name>
+                <a10:uri>author_uri</a10:uri>
+                <a10:email>author_email</a10:email>
+                <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                    <Value>10</Value>
+                </Rss20FeedFormatterTests.ExtensionObject>
+            </a10:author>
+            <category />
+            <category category_name1="""" d4p1:category_name2="""" d4p1:category_name3=""category_value"" d2p2:category_name4="""" domain=""category_scheme"" xmlns:d4p1=""category_namespace"">category_name</category>
+            <category term=""term_value"" label=""label_value"" scheme=""scheme_value"" domain=""scheme"">name</category>
+            <category term=""term_value"" label=""label_value"" scheme=""scheme_value"" domain=""scheme"">name</category>
+            <title>title_title</title>
+            <description>summary_title</description>
+            <pubDate>Mon, 01 Jan 0001 00:00:00 Z</pubDate>
+            <a10:link href="""" />
+            <a10:link xml:base=""http://link_url.com/"" link_name1="""" d4p1:link_name2="""" d4p1:link_name3=""link_value"" d2p2:link_name4="""" rel=""link_relationshipType"" type=""link_mediaType"" title=""link_title"" length=""10"" href=""http://link_uri.com/"" xmlns:d4p1=""link_namespace"">
+                <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                    <Value>10</Value>
+                </Rss20FeedFormatterTests.ExtensionObject>
+            </a10:link>
+            <a10:link rel=""rel_value"" type=""type_value"" title=""title_value"" length=""100"" href=""href_value"" />
+            <a10:updated>0001-01-01T00:00:00Z</a10:updated>
+            <a10:rights type=""html"" copyright_name1="""" d4p1:copyright_name2="""" d4p1:copyright_name3=""copyright_value"" d2p2:copyright_name4="""" xmlns:d4p1=""copyright_namespace"">copyright_title</a10:rights>
+            <a10:content type=""html"" content_name1="""" d4p1:content_name2="""" d4p1:content_name3=""content_value"" d2p2:content_name4="""" xmlns:d4p1=""content_namespace"">content_title</a10:content>
+            <a10:contributor />
+            <a10:contributor contributor_name1="""" d4p1:contributor_name2="""" d4p1:contributor_name3=""contributor_value"" d2p2:contributor_name4="""" xmlns:d4p1=""contributor_namespace"">
+                <a10:name>contributor_name</a10:name>
+                <a10:uri>contributor_uri</a10:uri>
+                <a10:email>contributor_email</a10:email>
+                <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                    <Value>10</Value>
+                </Rss20FeedFormatterTests.ExtensionObject>
+            </a10:contributor>
+            <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                <Value>10</Value>
+            </Rss20FeedFormatterTests.ExtensionObject>
+        </item>
+    </channel>"
+                };
+            }
+        }
+
+        [Theory]
+        [MemberData(nameof(WriteTo_TestData))]
+        public void Write_HasFeed_SerializesExpected(SyndicationFeed feed, bool serializeExtensionsAsAtom, string expected)
+        {
+            var formatter = new Rss20FeedFormatter(feed);
+            string expectedFull = @"<rss xmlns:a10=""http://www.w3.org/2005/Atom"" version=""2.0"">" + Environment.NewLine + expected + Environment.NewLine + "</rss>";
+            string expectedSerializable = @"<feed xmlns:a10=""http://www.w3.org/2005/Atom"" version=""2.0"">" + Environment.NewLine + expected + Environment.NewLine + "</feed>";
+
+            CompareHelper.AssertEqualWriteOutput(expectedFull, writer => formatter.WriteTo(writer));
+            CompareHelper.AssertEqualWriteOutput(expectedFull, writer => feed.SaveAsRss20(writer));
+            CompareHelper.AssertEqualWriteOutput(expectedSerializable, writer =>
+            {
+                writer.WriteStartElement("feed");
+                ((IXmlSerializable)formatter).WriteXml(writer);
+                writer.WriteEndElement();
+            });
+
+            var genericFormatter = new Rss20FeedFormatter<SyndicationFeed>(feed);
+            CompareHelper.AssertEqualWriteOutput(expectedFull, writer => formatter.WriteTo(writer));
+            CompareHelper.AssertEqualWriteOutput(expectedFull, writer => feed.SaveAsRss20(writer));
+        }
+
+        [Fact]
+        public void WriteTo_NullWriter_ThrowsArgumentNullException()
+        {
+            var formatter = new Rss20FeedFormatter(new SyndicationFeed());
+            AssertExtensions.Throws<ArgumentNullException>("writer", () => formatter.WriteTo(null));
+        }
+
+        [Fact]
+        public void WriteTo_NoItem_ThrowsInvalidOperationException()
+        {
+            using (var stringWriter = new StringWriter())
+            using (var writer = XmlWriter.Create(stringWriter))
+            {
+                var formatter = new Rss20FeedFormatter();
+                Assert.Throws<InvalidOperationException>(() => formatter.WriteTo(writer));
+            }
+        }
+
+        [Fact]
+        public void WriteXml_NullWriter_ThrowsArgumentNullException()
+        {
+            IXmlSerializable formatter = new Rss20FeedFormatter();
+            AssertExtensions.Throws<ArgumentNullException>("writer", () => formatter.WriteXml(null));
+        }
+
+        [Fact]
+        public void WriteXml_NoItem_ThrowsInvalidOperationException()
+        {
+            using (var stringWriter = new StringWriter())
+            using (var writer = XmlWriter.Create(stringWriter))
+            {
+                IXmlSerializable formatter = new Rss20FeedFormatter();
+                Assert.Throws<InvalidOperationException>(() => formatter.WriteXml(writer));
+            }
+        }
+
+        public static IEnumerable<object[]> FeedBaseUri_TestData()
+        {
+            yield return new object[] { null };
+            yield return new object[] { new Uri("http://microsoft.com") };
+            yield return new object[] { new Uri("/relative", UriKind.Relative) };
+        }
+
+        [Theory]
+        [MemberData(nameof(FeedBaseUri_TestData))]
+        public void WriteItem_Invoke_Success(Uri feedBaseUri)
+        {
+            var formatter = new Formatter();
+            var item = new SyndicationItem() { Id = "id", LastUpdatedTime = DateTimeOffset.MinValue.AddTicks(1) };
+            CompareHelper.AssertEqualWriteOutput(
+@"<item>
+    <guid isPermaLink=""false"">id</guid>
+    <description />
+    <updated xmlns=""http://www.w3.org/2005/Atom"">0001-01-01T00:00:00Z</updated>
+</item>", writer => formatter.WriteItemEntryPoint(writer, item, feedBaseUri));
+        }
+
+        [Fact]
+        public void WriteItem_NullWriter_ThrowsNullReferenceException()
+        {
+            using (var stringWriter = new StringWriter())
+            using (var writer = XmlWriter.Create(stringWriter))
+            {
+                var formatter = new Formatter();
+                Assert.Throws<NullReferenceException>(() => formatter.WriteItemEntryPoint(null, new SyndicationItem(), new Uri("http://microsoft.com")));
+            }
+        }
+
+        [Fact]
+        public void WriteItem_NullItem_ThrowsNullReferenceException()
+        {
+            using (var stringWriter = new StringWriter())
+            using (var writer = XmlWriter.Create(stringWriter))
+            {
+                var formatter = new Formatter();
+                Assert.Throws<NullReferenceException>(() => formatter.WriteItemEntryPoint(writer, null, new Uri("http://microsoft.com")));
+            }
+        }
+
+        [Theory]
+        [MemberData(nameof(FeedBaseUri_TestData))]
+        public void WriteItems_Invoke_Success(Uri feedBaseUri)
+        {
+            var formatter = new Formatter();
+            var items = new SyndicationItem[]
+            {
+                new SyndicationItem() { Id = "id1", LastUpdatedTime = DateTimeOffset.MinValue.AddTicks(1) },
+                new SyndicationItem() { Id = "id2", LastUpdatedTime = DateTimeOffset.MinValue.AddTicks(1) }
+            };
+            CompareHelper.AssertEqualWriteOutput(
+@"<item>
+    <guid isPermaLink=""false"">id1</guid>
+    <description />
+    <updated xmlns=""http://www.w3.org/2005/Atom"">0001-01-01T00:00:00Z</updated>
+</item>
+<item>
+    <guid isPermaLink=""false"">id2</guid>
+    <description />
+    <updated xmlns=""http://www.w3.org/2005/Atom"">0001-01-01T00:00:00Z</updated>
+</item>", writer => formatter.WriteItemsEntryPoint(writer, items, feedBaseUri));
+        }
+
+        [Fact]
+        public void WriteItems_NullItems_Nop()
+        {
+            using (var stringWriter = new StringWriter())
+            using (var writer = XmlWriter.Create(stringWriter))
+            {
+                var formatter = new Formatter();
+                formatter.WriteItemsEntryPoint(writer, null, new Uri("http://microsoft.com"));
+                formatter.WriteItemsEntryPoint(null, null, new Uri("http://microsoft.com"));
+            }
+        }
+
+        [Fact]
+        public void WriteItems_EmptyItems_Nop()
+        {
+            using (var stringWriter = new StringWriter())
+            using (var writer = XmlWriter.Create(stringWriter))
+            {
+                var formatter = new Formatter();
+                formatter.WriteItemsEntryPoint(writer, new SyndicationItem[0], new Uri("http://microsoft.com"));
+                formatter.WriteItemsEntryPoint(null, new SyndicationItem[0], new Uri("http://microsoft.com"));
+            }
+        }
+
+        [Fact]
+        public void WriteItems_NullWriter_ThrowsNullReferenceException()
+        {
+            using (var stringWriter = new StringWriter())
+            using (var writer = XmlWriter.Create(stringWriter))
+            {
+                var formatter = new Formatter();
+                var items = new SyndicationItem[] { new SyndicationItem() };
+                Assert.Throws<NullReferenceException>(() => formatter.WriteItemsEntryPoint(null, items, new Uri("http://microsoft.com")));
+            }
+        }
+
+        [Fact]
+        public void WriteItems_NullItemInItems_ThrowsNullReferenceException()
+        {
+            using (var stringWriter = new StringWriter())
+            using (var writer = XmlWriter.Create(stringWriter))
+            {
+                var formatter = new Formatter();
+                var items = new SyndicationItem[] { null };
+                Assert.Throws<NullReferenceException>(() => formatter.WriteItemsEntryPoint(writer, items, new Uri("http://microsoft.com")));
+            }
+        }
+
+        public static IEnumerable<object[]> CanRead_TestData()
+        {
+            yield return new object[] { @"<rss />", true };
+            yield return new object[] { @"<rss xmlns=""different"" />", false };
+            yield return new object[] { @"<different xmlns="""" />", false };
+            yield return new object[] { @"<rss xmlns="""" />", true };
+            yield return new object[] { @"<rss xmlns=""""></rss>", true };
+        }
+
+        [Theory]
+        [MemberData(nameof(CanRead_TestData))]
+        public void CanRead_ValidReader_ReturnsExpected(string xmlString, bool expected)
+        {
+            using (var stringReader = new StringReader(xmlString))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                var formatter = new Rss20FeedFormatter();
+                Assert.Equal(expected, formatter.CanRead(reader));
+            }
+        }
+
+        [Fact]
+        public void CanRead_NullReader_ThrowsArgumentNullException()
+        {
+            var formatter = new Rss20FeedFormatter();
+            AssertExtensions.Throws<ArgumentNullException>("reader", () => formatter.CanRead(null));
+        }
+
+        [Theory]
+        [InlineData(true, true)]
+        [InlineData(false, false)]
+        public void Read_FullItem_ReturnsExpected(bool preserveAttributeExtensions, bool preserveElementExtensions)
+        {
+            VerifyRead(
+@"<rss xmlns:a10=""http://www.w3.org/2005/Atom"" version=""2.0"">
+    <channel xml:base=""http://microsoft.com/"" feed_name1="""" d2p1:feed_name2="""" d2p1:feed_name3=""feed_value"" d2p2:feed_name4="""" xmlns:d2p2=""xmlns"" xmlns:d2p1=""feed_namespace"">
+        <title>feedtitle_title</title>
+        <description>feeddescription_title</description>
+        <language>language</language>
+        <copyright>feedcopyright_title</copyright>
+        <a10:author />
+        <a10:author feedauthor_name1="""" d3p1:feedauthor_name2="""" d3p1:feedauthor_name3=""feedauthor_value"" d2p2:feedauthor_name4="""" xmlns:d3p1=""feedauthor_namespace"">
+            <a10:name>feedauthor_name</a10:name>
+            <a10:uri>feedauthor_uri</a10:uri>
+            <a10:email>feedauthor_email</a10:email>
+            <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                <Value>10</Value>
+            </Rss20FeedFormatterTests.ExtensionObject>
+        </a10:author>
+        <lastBuildDate>Tue, 01 Jan 0002 00:00:00 Z</lastBuildDate>
+        <category />
+        <category feedcategory_name1="""" d3p1:feedcategory_name2="""" d3p1:feedcategory_name3=""feedcategory_value"" d2p2:feedcategory_name4="""" domain=""feedcategory_scheme"" xmlns:d3p1=""feedcategory_namespace"">feedcategory_name</category>
+        <generator>generator</generator>
+        <a10:contributor />
+        <a10:contributor feedauthor__name1="""" d3p1:feedauthor__name2="""" d3p1:feedauthor__name3=""feedauthor__value"" d2p2:feedauthor__name4="""" xmlns:d3p1=""feedauthor__namespace"">
+            <a10:name>feedauthor__name</a10:name>
+            <a10:uri>feedauthor__uri</a10:uri>
+            <a10:email>feedauthor__email</a10:email>
+            <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                <Value>10</Value>
+            </Rss20FeedFormatterTests.ExtensionObject>
+        </a10:contributor>
+        <image>
+            <url>http://imageurl.com/</url>
+            <title>feedtitle_title</title>
+            <link />
+        </image>
+        <a10:id>id</a10:id>
+        <a10:link href="""" />
+        <a10:link xml:base=""http://syndicationlink_url.com/"" syndicationlink_name1="""" d3p1:syndicationlink_name2="""" d3p1:syndicationlink_name3=""syndicationlink_value"" d2p2:syndicationlink_name4="""" rel=""syndicationlink_relationshipType"" type=""syndicationlink_mediaType"" title=""syndicationlink_title"" length=""10"" href=""http://syndicationlink_uri.com/"" xmlns:d3p1=""syndicationlink_namespace"">
+            <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                <Value>10</Value>
+            </Rss20FeedFormatterTests.ExtensionObject>
+        </a10:link>
+        <a10:link rel=""rel_value"" type=""type_value"" title=""title_value"" length=""100"" href=""href_value"" />
+        <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+            <Value>10</Value>
+        </Rss20FeedFormatterTests.ExtensionObject>
+        <item>
+            <guid isPermaLink=""false"">id</guid>
+            <description />
+            <a10:updated>0001-01-01T00:00:00Z</a10:updated>
+        </item>
+        <item xml:base=""http://microsoft/relative"" item_name1="""" d3p1:item_name2="""" d3p1:item_name3=""item_value"" d2p2:item_name4="""" xmlns:d3p1=""item_namespace"">
+            <guid isPermaLink=""false"">id</guid>
+            <a10:author />
+            <a10:author author_name1="""" d4p1:author_name2="""" d4p1:author_name3=""author_value"" d2p2:author_name4="""" xmlns:d4p1=""author_namespace"">
+                <a10:name>author_name</a10:name>
+                <a10:uri>author_uri</a10:uri>
+                <a10:email>author_email</a10:email>
+                <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                    <Value>10</Value>
+                </Rss20FeedFormatterTests.ExtensionObject>
+            </a10:author>
+            <category />
+            <category category_name1="""" d4p1:category_name2="""" d4p1:category_name3=""category_value"" d2p2:category_name4="""" domain=""category_scheme"" xmlns:d4p1=""category_namespace"">category_name</category>
+            <category term=""term_value"" label=""label_value"" scheme=""scheme_value"" domain=""scheme"">name</category>
+            <category term=""term_value"" label=""label_value"" scheme=""scheme_value"" domain=""scheme"">name</category>
+            <title>title_title</title>
+            <description>summary_title</description>
+            <pubDate>Mon, 01 Jan 0001 00:00:00 Z</pubDate>
+            <a10:link href="""" />
+            <a10:link xml:base=""http://link_url.com/"" link_name1="""" d4p1:link_name2="""" d4p1:link_name3=""link_value"" d2p2:link_name4="""" rel=""link_relationshipType"" type=""link_mediaType"" title=""link_title"" length=""10"" href=""http://link_uri.com/"" xmlns:d4p1=""link_namespace"">
+                <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                    <Value>10</Value>
+                </Rss20FeedFormatterTests.ExtensionObject>
+            </a10:link>
+            <a10:link rel=""rel_value"" type=""type_value"" title=""title_value"" length=""100"" href=""href_value"" />
+            <a10:updated>0001-01-01T00:00:00Z</a10:updated>
+            <a10:rights type=""html"" copyright_name1="""" d4p1:copyright_name2="""" d4p1:copyright_name3=""copyright_value"" d2p2:copyright_name4="""" xmlns:d4p1=""copyright_namespace"">copyright_title</a10:rights>
+            <a10:content type=""html"" content_name1="""" d4p1:content_name2="""" d4p1:content_name3=""content_value"" d2p2:content_name4="""" xmlns:d4p1=""content_namespace"">content_title</a10:content>
+            <a10:contributor />
+            <a10:contributor contributor_name1="""" d4p1:contributor_name2="""" d4p1:contributor_name3=""contributor_value"" d2p2:contributor_name4="""" xmlns:d4p1=""contributor_namespace"">
+                <a10:name>contributor_name</a10:name>
+                <a10:uri>contributor_uri</a10:uri>
+                <a10:email>contributor_email</a10:email>
+                <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                    <Value>10</Value>
+                </Rss20FeedFormatterTests.ExtensionObject>
+            </a10:contributor>
+            <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                <Value>10</Value>
+            </Rss20FeedFormatterTests.ExtensionObject>
+        </item>
+    </channel>
+</rss>", preserveAttributeExtensions, preserveElementExtensions, feed =>
+            {
+                if (preserveAttributeExtensions)
+                {
+                    Assert.Equal(4, feed.AttributeExtensions.Count);
+                    Assert.Equal(4, feed.AttributeExtensions.Count);
+                    Assert.Equal("", feed.AttributeExtensions[new XmlQualifiedName("feed_name1")]);
+                    Assert.Equal("", feed.AttributeExtensions[new XmlQualifiedName("feed_name2", "feed_namespace")]);
+                    Assert.Equal("feed_value", feed.AttributeExtensions[new XmlQualifiedName("feed_name3", "feed_namespace")]);
+                    Assert.Equal("", feed.AttributeExtensions[new XmlQualifiedName("feed_name4", "xmlns")]);
+                }
+                else
+                {
+                    Assert.Empty(feed.AttributeExtensions);
+                }
+
+                Assert.Equal(2, feed.Authors.Count);
+
+                SyndicationPerson firstAuthor = feed.Authors[0];
+                Assert.Empty(firstAuthor.AttributeExtensions);
+                Assert.Empty(firstAuthor.ElementExtensions);
+                Assert.Null(firstAuthor.Email);
+                Assert.Null(firstAuthor.Name);
+                Assert.Null(firstAuthor.Uri);
+
+                SyndicationPerson secondAuthor = feed.Authors[1];
+                Assert.Equal(4, secondAuthor.AttributeExtensions.Count);
+                Assert.Equal(4, secondAuthor.AttributeExtensions.Count);
+                Assert.Equal("", secondAuthor.AttributeExtensions[new XmlQualifiedName("feedauthor_name1")]);
+                Assert.Equal("", secondAuthor.AttributeExtensions[new XmlQualifiedName("feedauthor_name2", "feedauthor_namespace")]);
+                Assert.Equal("feedauthor_value", secondAuthor.AttributeExtensions[new XmlQualifiedName("feedauthor_name3", "feedauthor_namespace")]);
+                Assert.Equal("", secondAuthor.AttributeExtensions[new XmlQualifiedName("feedauthor_name4", "xmlns")]);
+                Assert.Equal(1, secondAuthor.ElementExtensions.Count());
+                Assert.Equal("feedauthor_email", secondAuthor.Email);
+                Assert.Equal("feedauthor_name", secondAuthor.Name);
+                Assert.Equal("feedauthor_uri", secondAuthor.Uri);
+
+                Assert.Equal(new Uri("http://microsoft.com"), feed.BaseUri);
+
+                Assert.Equal(2, feed.Categories.Count);
+                SyndicationCategory firstCategory = feed.Categories[0];
+                Assert.Empty(firstCategory.AttributeExtensions);
+                Assert.Empty(firstCategory.ElementExtensions);
+                Assert.Null(firstCategory.Name);
+                Assert.Null(firstCategory.Scheme);
+                Assert.Null(firstCategory.Label);
+
+                SyndicationCategory secondCategory = feed.Categories[1];
+                if (preserveAttributeExtensions)
+                {
+                    Assert.Equal(4, secondCategory.AttributeExtensions.Count);
+                    Assert.Equal(4, secondCategory.AttributeExtensions.Count);
+                    Assert.Equal("", secondCategory.AttributeExtensions[new XmlQualifiedName("feedcategory_name1")]);
+                    Assert.Equal("", secondCategory.AttributeExtensions[new XmlQualifiedName("feedcategory_name2", "feedcategory_namespace")]);
+                    Assert.Equal("feedcategory_value", secondCategory.AttributeExtensions[new XmlQualifiedName("feedcategory_name3", "feedcategory_namespace")]);
+                    Assert.Equal("", secondCategory.AttributeExtensions[new XmlQualifiedName("feedcategory_name4", "xmlns")]);
+                }
+                else
+                {
+                    Assert.Empty(secondCategory.AttributeExtensions);
+                }
+                Assert.Empty(secondCategory.ElementExtensions);
+                Assert.Equal("feedcategory_name", secondCategory.Name);
+                Assert.Equal("feedcategory_scheme", secondCategory.Scheme);
+                Assert.Null(secondCategory.Label);
+
+                Assert.Equal(2, feed.Contributors.Count);
+                SyndicationPerson firstContributor = feed.Contributors[0];
+                Assert.Empty(firstContributor.AttributeExtensions);
+                Assert.Empty(firstContributor.ElementExtensions);
+                Assert.Null(firstContributor.Email);
+                Assert.Null(firstContributor.Name);
+                Assert.Null(firstContributor.Uri);
+
+                SyndicationPerson secondContributor = feed.Contributors[1];
+                Assert.Equal(4, secondContributor.AttributeExtensions.Count);
+                Assert.Equal(4, secondContributor.AttributeExtensions.Count);
+                Assert.Equal("", secondContributor.AttributeExtensions[new XmlQualifiedName("feedauthor__name1")]);
+                Assert.Equal("", secondContributor.AttributeExtensions[new XmlQualifiedName("feedauthor__name2", "feedauthor__namespace")]);
+                Assert.Equal("feedauthor__value", secondContributor.AttributeExtensions[new XmlQualifiedName("feedauthor__name3", "feedauthor__namespace")]);
+                Assert.Equal("", secondContributor.AttributeExtensions[new XmlQualifiedName("feedauthor__name4", "xmlns")]);
+                Assert.Equal(1, secondContributor.ElementExtensions.Count());
+                Assert.Equal("feedauthor__email", secondContributor.Email);
+                Assert.Equal("feedauthor__name", secondContributor.Name);
+                Assert.Equal("feedauthor__uri", secondContributor.Uri);
+
+                Assert.Empty(feed.Copyright.AttributeExtensions);
+                Assert.Equal("feedcopyright_title", feed.Copyright.Text);
+                Assert.Equal("text", feed.Copyright.Type);
+
+                Assert.Equal("generator", feed.Generator);
+
+                if (preserveElementExtensions)
+                {
+                    Assert.Equal(1, feed.ElementExtensions.Count());
+                }
+                else
+                {
+                    Assert.Empty(feed.ElementExtensions);
+                }
+
+                Assert.Equal("id", feed.Id);
+
+                Assert.Equal(new Uri("http://imageurl.com/"), feed.ImageUrl);
+
+                SyndicationItem[] items = feed.Items.ToArray();
+                Assert.Equal(2, items.Length);
+
+                SyndicationItem item = items[1];
+
+                if (preserveAttributeExtensions)
+                {
+                    Assert.Equal(4, item.AttributeExtensions.Count);
+                    Assert.Equal(4, item.AttributeExtensions.Count);
+                    Assert.Equal("", item.AttributeExtensions[new XmlQualifiedName("item_name1")]);
+                    Assert.Equal("", item.AttributeExtensions[new XmlQualifiedName("item_name2", "item_namespace")]);
+                    Assert.Equal("item_value", item.AttributeExtensions[new XmlQualifiedName("item_name3", "item_namespace")]);
+                    Assert.Equal("", item.AttributeExtensions[new XmlQualifiedName("item_name4", "xmlns")]);
+                }
+                else
+                {
+                    Assert.Empty(item.AttributeExtensions);
+                }
+                if (preserveElementExtensions)
+                {
+                    Assert.Equal(1, item.ElementExtensions.Count());
+                }
+                else
+                {
+                    Assert.Empty(item.ElementExtensions);
+                }
+
+                Assert.Equal(2, item.Authors.Count);
+
+                SyndicationPerson itemFirstAuthor = item.Authors[0];
+                Assert.Empty(itemFirstAuthor.AttributeExtensions);
+                Assert.Empty(itemFirstAuthor.ElementExtensions);
+                Assert.Null(itemFirstAuthor.Email);
+                Assert.Null(itemFirstAuthor.Name);
+                Assert.Null(itemFirstAuthor.Uri);
+
+                SyndicationPerson itemSecondAuthor = item.Authors[1];
+
+                Assert.Equal(4, itemSecondAuthor.AttributeExtensions.Count);
+                Assert.Equal(4, itemSecondAuthor.AttributeExtensions.Count);
+                Assert.Equal("", itemSecondAuthor.AttributeExtensions[new XmlQualifiedName("author_name1")]);
+                Assert.Equal("", itemSecondAuthor.AttributeExtensions[new XmlQualifiedName("author_name2", "author_namespace")]);
+                Assert.Equal("author_value", itemSecondAuthor.AttributeExtensions[new XmlQualifiedName("author_name3", "author_namespace")]);
+                Assert.Equal("", itemSecondAuthor.AttributeExtensions[new XmlQualifiedName("author_name4", "xmlns")]);
+                Assert.Equal(1, itemSecondAuthor.ElementExtensions.Count());
+                Assert.Equal("author_email", itemSecondAuthor.Email);
+                Assert.Equal("author_name", itemSecondAuthor.Name);
+                Assert.Equal("author_uri", itemSecondAuthor.Uri);
+
+                Assert.Equal(new Uri("http://microsoft/relative"), item.BaseUri);
+
+                Assert.Equal(4, item.Categories.Count);
+                SyndicationCategory itemFirstCategory = item.Categories[0];
+                Assert.Empty(itemFirstCategory.AttributeExtensions);
+                Assert.Empty(itemFirstCategory.ElementExtensions);
+                Assert.Null(itemFirstCategory.Name);
+                Assert.Null(itemFirstCategory.Scheme);
+                Assert.Null(itemFirstCategory.Label);
+
+                SyndicationCategory itemSecondCategory = item.Categories[1];
+
+                if (preserveAttributeExtensions)
+                {
+                    Assert.Equal(4, itemSecondCategory.AttributeExtensions.Count);
+                    Assert.Equal(4, itemSecondCategory.AttributeExtensions.Count);
+                    Assert.Equal("", itemSecondCategory.AttributeExtensions[new XmlQualifiedName("category_name1")]);
+                    Assert.Equal("", itemSecondCategory.AttributeExtensions[new XmlQualifiedName("category_name2", "category_namespace")]);
+                    Assert.Equal("category_value", itemSecondCategory.AttributeExtensions[new XmlQualifiedName("category_name3", "category_namespace")]);
+                    Assert.Equal("", itemSecondCategory.AttributeExtensions[new XmlQualifiedName("category_name4", "xmlns")]);
+                }
+                else
+                {
+                    Assert.Empty(itemSecondCategory.AttributeExtensions);
+                }
+                Assert.Empty(itemSecondCategory.ElementExtensions);
+                Assert.Equal("category_name", itemSecondCategory.Name);
+                Assert.Equal("category_scheme", itemSecondCategory.Scheme);
+                Assert.Null(itemSecondCategory.Label);
+
+                SyndicationCategory itemThirdCategory = item.Categories[2];
+                if (preserveAttributeExtensions)
+                {
+                    Assert.Equal(3, itemThirdCategory.AttributeExtensions.Count);
+                    Assert.Equal("term_value", itemThirdCategory.AttributeExtensions[new XmlQualifiedName("term")]);
+                    Assert.Equal("label_value", itemThirdCategory.AttributeExtensions[new XmlQualifiedName("label")]);
+                    Assert.Equal("scheme_value", itemThirdCategory.AttributeExtensions[new XmlQualifiedName("scheme")]);
+                }
+                else
+                {
+                    Assert.Empty(itemThirdCategory.AttributeExtensions);
+                }
+                Assert.Empty(itemThirdCategory.ElementExtensions);
+                Assert.Equal("name", itemThirdCategory.Name);
+                Assert.Equal("scheme", itemThirdCategory.Scheme);
+                Assert.Null(itemThirdCategory.Label);
+
+                SyndicationCategory itemFourthCategory = item.Categories[3];
+                if (preserveAttributeExtensions)
+                {
+                    Assert.Equal(3, itemFourthCategory.AttributeExtensions.Count);
+                    Assert.Equal("term_value", itemFourthCategory.AttributeExtensions[new XmlQualifiedName("term")]);
+                    Assert.Equal("label_value", itemFourthCategory.AttributeExtensions[new XmlQualifiedName("label")]);
+                    Assert.Equal("scheme_value", itemFourthCategory.AttributeExtensions[new XmlQualifiedName("scheme")]);
+                }
+                else
+                {
+                    Assert.Empty(itemFourthCategory.AttributeExtensions);
+                }
+                Assert.Empty(itemFourthCategory.ElementExtensions);
+                Assert.Equal("name", itemFourthCategory.Name);
+                Assert.Equal("scheme", itemFourthCategory.Scheme);
+                Assert.Null(itemFourthCategory.Label);
+
+                TextSyndicationContent content = Assert.IsType<TextSyndicationContent>(item.Content);
+
+                Assert.Equal(4, content.AttributeExtensions.Count);
+                Assert.Equal(4, content.AttributeExtensions.Count);
+                Assert.Equal("", content.AttributeExtensions[new XmlQualifiedName("content_name1")]);
+                Assert.Equal("", content.AttributeExtensions[new XmlQualifiedName("content_name2", "content_namespace")]);
+                Assert.Equal("content_value", content.AttributeExtensions[new XmlQualifiedName("content_name3", "content_namespace")]);
+                Assert.Equal("", content.AttributeExtensions[new XmlQualifiedName("content_name4", "xmlns")]);
+                Assert.Equal("content_title", content.Text);
+                Assert.Equal("html", content.Type);
+
+                Assert.Equal(2, item.Contributors.Count);
+
+                SyndicationPerson itemFirstContributor = item.Contributors[0];
+                Assert.Empty(itemFirstContributor.AttributeExtensions);
+                Assert.Empty(itemFirstContributor.ElementExtensions);
+                Assert.Null(itemFirstContributor.Email);
+                Assert.Null(itemFirstContributor.Name);
+                Assert.Null(itemFirstContributor.Uri);
+
+                SyndicationPerson itemSecondContributor = item.Contributors[1];
+                Assert.Equal(4, itemSecondContributor.AttributeExtensions.Count);
+                Assert.Equal("", itemSecondContributor.AttributeExtensions[new XmlQualifiedName("contributor_name1")]);
+                Assert.Equal("", itemSecondContributor.AttributeExtensions[new XmlQualifiedName("contributor_name2", "contributor_namespace")]);
+                Assert.Equal("contributor_value", itemSecondContributor.AttributeExtensions[new XmlQualifiedName("contributor_name3", "contributor_namespace")]);
+                Assert.Equal("", itemSecondContributor.AttributeExtensions[new XmlQualifiedName("contributor_name4", "xmlns")]);
+                Assert.Equal(1, itemSecondContributor.ElementExtensions.Count());
+                Assert.Equal("contributor_email", itemSecondContributor.Email);
+                Assert.Equal("contributor_name", itemSecondContributor.Name);
+                Assert.Equal("contributor_uri", itemSecondContributor.Uri);
+
+                Assert.Equal(4, item.Copyright.AttributeExtensions.Count);
+                Assert.Equal("", item.Copyright.AttributeExtensions[new XmlQualifiedName("copyright_name1")]);
+                Assert.Equal("", item.Copyright.AttributeExtensions[new XmlQualifiedName("copyright_name2", "copyright_namespace")]);
+                Assert.Equal("copyright_value", item.Copyright.AttributeExtensions[new XmlQualifiedName("copyright_name3", "copyright_namespace")]);
+                Assert.Equal("", item.Copyright.AttributeExtensions[new XmlQualifiedName("copyright_name4", "xmlns")]);
+                Assert.Equal("copyright_title", item.Copyright.Text);
+                Assert.Equal("html", item.Copyright.Type);
+
+                if (preserveElementExtensions)
+                {
+                    Assert.Equal(1, item.ElementExtensions.Count());
+                }
+                else
+                {
+                    Assert.Empty(item.ElementExtensions);
+                }
+
+                Assert.Equal("id", item.Id);
+
+                Assert.Equal(DateTimeOffset.MinValue, item.LastUpdatedTime);
+
+                Assert.Equal(3, item.Links.Count);
+
+                SyndicationLink itemFirstLink = item.Links[0];
+                Assert.Empty(itemFirstLink.AttributeExtensions);
+                Assert.Empty(itemFirstLink.ElementExtensions);
+                Assert.Equal(0, itemFirstLink.Length);
+                Assert.Null(itemFirstLink.MediaType);
+                Assert.Null(itemFirstLink.RelationshipType);
+                Assert.Null(itemFirstLink.Title);
+                Assert.Equal(string.Empty, itemFirstLink.Uri.OriginalString);
+
+                SyndicationLink itemSecondLink = item.Links[1];
+                Assert.Equal(4, itemSecondLink.AttributeExtensions.Count);
+                Assert.Equal("", itemSecondLink.AttributeExtensions[new XmlQualifiedName("link_name1")]);
+                Assert.Equal("", itemSecondLink.AttributeExtensions[new XmlQualifiedName("link_name2", "link_namespace")]);
+                Assert.Equal("link_value", itemSecondLink.AttributeExtensions[new XmlQualifiedName("link_name3", "link_namespace")]);
+                Assert.Equal("", itemSecondLink.AttributeExtensions[new XmlQualifiedName("link_name4", "xmlns")]);
+                Assert.Equal(1, itemSecondLink.ElementExtensions.Count());
+                Assert.Equal(new Uri("http://link_url.com"), itemSecondLink.BaseUri);
+                Assert.Equal(10, itemSecondLink.Length);
+                Assert.Equal("link_mediaType", itemSecondLink.MediaType);
+                Assert.Equal("link_relationshipType", itemSecondLink.RelationshipType);
+                Assert.Equal("link_title", itemSecondLink.Title);
+                Assert.Equal(new Uri("http://link_uri.com"), itemSecondLink.Uri);
+
+                SyndicationLink itemThirdLink = item.Links[2];
+                Assert.Empty(itemThirdLink.AttributeExtensions);
+                Assert.Empty(itemThirdLink.ElementExtensions);
+                Assert.Equal(100, itemThirdLink.Length);
+                Assert.Equal("type_value", itemThirdLink.MediaType);
+                Assert.Equal("rel_value", itemThirdLink.RelationshipType);
+                Assert.Equal("title_value", itemThirdLink.Title);
+                Assert.Equal("href_value", itemThirdLink.Uri.OriginalString);
+
+                Assert.Equal(DateTimeOffset.MinValue, item.PublishDate);
+
+                Assert.Null(item.SourceFeed);
+
+                Assert.Empty(item.Summary.AttributeExtensions);
+                Assert.Equal("summary_title", item.Summary.Text);
+                Assert.Equal("text", item.Summary.Type);
+
+                Assert.Empty(item.Title.AttributeExtensions);
+                Assert.Equal("title_title", item.Title.Text);
+                Assert.Equal("text", item.Title.Type);
+
+                Assert.Equal("language", feed.Language);
+
+                Assert.Equal(DateTimeOffset.MinValue.AddYears(1), feed.LastUpdatedTime);
+                Assert.Equal(3, feed.Links.Count);
+
+                SyndicationLink firstLink = feed.Links[0];
+                Assert.Empty(firstLink.AttributeExtensions);
+                Assert.Empty(firstLink.ElementExtensions);
+                Assert.Equal(0, firstLink.Length);
+                Assert.Null(firstLink.MediaType);
+                Assert.Null(firstLink.RelationshipType);
+                Assert.Null(firstLink.Title);
+                Assert.Equal("", firstLink.Uri.OriginalString);
+
+                SyndicationLink secondLink = feed.Links[1];
+                Assert.Equal(4, secondLink.AttributeExtensions.Count);
+                Assert.Equal("", secondLink.AttributeExtensions[new XmlQualifiedName("syndicationlink_name1")]);
+                Assert.Equal("", secondLink.AttributeExtensions[new XmlQualifiedName("syndicationlink_name2", "syndicationlink_namespace")]);
+                Assert.Equal("syndicationlink_value", secondLink.AttributeExtensions[new XmlQualifiedName("syndicationlink_name3", "syndicationlink_namespace")]);
+                Assert.Equal("", secondLink.AttributeExtensions[new XmlQualifiedName("syndicationlink_name4", "xmlns")]);
+                Assert.Equal(1, secondLink.ElementExtensions.Count());
+                Assert.Equal(new Uri("http://syndicationlink_url.com/"), secondLink.BaseUri);
+                Assert.Equal(10, secondLink.Length);
+                Assert.Equal("syndicationlink_mediaType", secondLink.MediaType);
+                Assert.Equal("syndicationlink_relationshipType", secondLink.RelationshipType);
+                Assert.Equal("syndicationlink_title", secondLink.Title);
+                Assert.Equal("http://syndicationlink_uri.com/", secondLink.Uri.OriginalString);
+
+                SyndicationLink thirdLink = feed.Links[2];
+                Assert.Empty(thirdLink.AttributeExtensions);
+                Assert.Empty(thirdLink.ElementExtensions);
+                Assert.Equal(100, thirdLink.Length);
+                Assert.Equal("type_value", thirdLink.MediaType);
+                Assert.Equal("rel_value", thirdLink.RelationshipType);
+                Assert.Equal("title_value", thirdLink.Title);
+                Assert.Equal("href_value", thirdLink.Uri.OriginalString);
+
+                Assert.Empty(item.Title.AttributeExtensions);
+                Assert.Equal("feedtitle_title", feed.Title.Text);
+                Assert.Equal("text", feed.Title.Type);
+            });
+        }
+
+        [Theory]
+        [InlineData(true, true)]
+        [InlineData(false, false)]
+        public void Read_TryParseTrue_ReturnsExpected(bool preserveAttributeExtensions, bool preserveElementExtensions)
+        {
+            using (var stringReader = new StringReader(
+@"<rss xmlns:a10=""http://www.w3.org/2005/Atom"" version=""2.0"">
+    <channel xml:base=""http://microsoft.com/"" feed_name1="""" d2p1:feed_name2="""" d2p1:feed_name3=""feed_value"" d2p2:feed_name4="""" xmlns:d2p2=""xmlns"" xmlns:d2p1=""feed_namespace"">
+        <title>feedtitle_title</title>
+        <description>feeddescription_title</description>
+        <language>language</language>
+        <copyright>feedcopyright_title</copyright>
+        <a10:author />
+        <a10:author feedauthor_name1="""" d3p1:feedauthor_name2="""" d3p1:feedauthor_name3=""feedauthor_value"" d2p2:feedauthor_name4="""" xmlns:d3p1=""feedauthor_namespace"">
+            <a10:name>feedauthor_name</a10:name>
+            <a10:uri>feedauthor_uri</a10:uri>
+            <a10:email>feedauthor_email</a10:email>
+            <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                <Value>10</Value>
+            </Rss20FeedFormatterTests.ExtensionObject>
+        </a10:author>
+        <lastBuildDate>Tue, 01 Jan 0002 00:00:00 Z</lastBuildDate>
+        <category />
+        <category feedcategory_name1="""" d3p1:feedcategory_name2="""" d3p1:feedcategory_name3=""feedcategory_value"" d2p2:feedcategory_name4="""" domain=""feedcategory_scheme"" xmlns:d3p1=""feedcategory_namespace"">feedcategory_name</category>
+        <generator>generator</generator>
+        <a10:contributor />
+        <a10:contributor feedauthor__name1="""" d3p1:feedauthor__name2="""" d3p1:feedauthor__name3=""feedauthor__value"" d2p2:feedauthor__name4="""" xmlns:d3p1=""feedauthor__namespace"">
+            <a10:name>feedauthor__name</a10:name>
+            <a10:uri>feedauthor__uri</a10:uri>
+            <a10:email>feedauthor__email</a10:email>
+            <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                <Value>10</Value>
+            </Rss20FeedFormatterTests.ExtensionObject>
+        </a10:contributor>
+        <image>
+            <url>http://imageurl.com/</url>
+            <title>feedtitle_title</title>
+            <link />
+        </image>
+        <a10:id>id</a10:id>
+        <a10:link href="""" />
+        <a10:link xml:base=""http://syndicationlink_url.com/"" syndicationlink_name1="""" d3p1:syndicationlink_name2="""" d3p1:syndicationlink_name3=""syndicationlink_value"" d2p2:syndicationlink_name4="""" rel=""syndicationlink_relationshipType"" type=""syndicationlink_mediaType"" title=""syndicationlink_title"" length=""10"" href=""http://syndicationlink_uri.com/"" xmlns:d3p1=""syndicationlink_namespace"">
+            <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                <Value>10</Value>
+            </Rss20FeedFormatterTests.ExtensionObject>
+        </a10:link>
+        <a10:link rel=""rel_value"" type=""type_value"" title=""title_value"" length=""100"" href=""href_value"" />
+        <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+            <Value>10</Value>
+        </Rss20FeedFormatterTests.ExtensionObject>
+        <item>
+            <guid isPermaLink=""false"">id</guid>
+            <description />
+            <a10:updated>0001-01-01T00:00:00Z</a10:updated>
+        </item>
+        <item xml:base=""http://microsoft/relative"" item_name1="""" d3p1:item_name2="""" d3p1:item_name3=""item_value"" d2p2:item_name4="""" xmlns:d3p1=""item_namespace"">
+            <guid isPermaLink=""false"">id</guid>
+            <a10:author />
+            <a10:author author_name1="""" d4p1:author_name2="""" d4p1:author_name3=""author_value"" d2p2:author_name4="""" xmlns:d4p1=""author_namespace"">
+                <a10:name>author_name</a10:name>
+                <a10:uri>author_uri</a10:uri>
+                <a10:email>author_email</a10:email>
+                <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                    <Value>10</Value>
+                </Rss20FeedFormatterTests.ExtensionObject>
+            </a10:author>
+            <category />
+            <category category_name1="""" d4p1:category_name2="""" d4p1:category_name3=""category_value"" d2p2:category_name4="""" domain=""category_scheme"" xmlns:d4p1=""category_namespace"">category_name</category>
+            <category term=""term_value"" label=""label_value"" scheme=""scheme_value"" domain=""scheme"">name</category>
+            <category term=""term_value"" label=""label_value"" scheme=""scheme_value"" domain=""scheme"">name</category>
+            <title>title_title</title>
+            <description>summary_title</description>
+            <pubDate>Mon, 01 Jan 0001 00:00:00 Z</pubDate>
+            <a10:link href="""" />
+            <a10:link xml:base=""http://link_url.com/"" link_name1="""" d4p1:link_name2="""" d4p1:link_name3=""link_value"" d2p2:link_name4="""" rel=""link_relationshipType"" type=""link_mediaType"" title=""link_title"" length=""10"" href=""http://link_uri.com/"" xmlns:d4p1=""link_namespace"">
+                <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                    <Value>10</Value>
+                </Rss20FeedFormatterTests.ExtensionObject>
+            </a10:link>
+            <a10:link rel=""rel_value"" type=""type_value"" title=""title_value"" length=""100"" href=""href_value"" />
+            <a10:updated>0001-01-01T00:00:00Z</a10:updated>
+            <a10:rights type=""html"" copyright_name1="""" d4p1:copyright_name2="""" d4p1:copyright_name3=""copyright_value"" d2p2:copyright_name4="""" xmlns:d4p1=""copyright_namespace"">copyright_title</a10:rights>
+            <a10:content type=""html"" content_name1="""" d4p1:content_name2="""" d4p1:content_name3=""content_value"" d2p2:content_name4="""" xmlns:d4p1=""content_namespace"">content_title</a10:content>
+            <a10:contributor />
+            <a10:contributor contributor_name1="""" d4p1:contributor_name2="""" d4p1:contributor_name3=""contributor_value"" d2p2:contributor_name4="""" xmlns:d4p1=""contributor_namespace"">
+                <a10:name>contributor_name</a10:name>
+                <a10:uri>contributor_uri</a10:uri>
+                <a10:email>contributor_email</a10:email>
+                <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                    <Value>10</Value>
+                </Rss20FeedFormatterTests.ExtensionObject>
+            </a10:contributor>
+            <Rss20FeedFormatterTests.ExtensionObject xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""http://schemas.datacontract.org/2004/07/System.ServiceModel.Syndication.Tests"">
+                <Value>10</Value>
+            </Rss20FeedFormatterTests.ExtensionObject>
+        </item>
+    </channel>
+</rss>"))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                var formatter = new Rss20FeedFormatter<SyndicationFeedTryParseTrueSubclass>()
+                {
+                    PreserveAttributeExtensions = preserveAttributeExtensions,
+                    PreserveElementExtensions = preserveElementExtensions
+                };
+                formatter.ReadFrom(reader);
+
+                SyndicationFeed feed = formatter.Feed;
+                Assert.Empty(feed.AttributeExtensions);
+
+                Assert.Equal(2, feed.Authors.Count);
+
+                SyndicationPerson firstAuthor = feed.Authors[0];
+                Assert.Empty(firstAuthor.AttributeExtensions);
+                Assert.Empty(firstAuthor.ElementExtensions);
+                Assert.Null(firstAuthor.Email);
+                Assert.Null(firstAuthor.Name);
+                Assert.Null(firstAuthor.Uri);
+
+                SyndicationPerson secondAuthor = feed.Authors[1];
+                Assert.Empty(secondAuthor.AttributeExtensions);
+                Assert.Empty(secondAuthor.ElementExtensions);
+                Assert.Equal("feedauthor_email", secondAuthor.Email);
+                Assert.Equal("feedauthor_name", secondAuthor.Name);
+                Assert.Equal("feedauthor_uri", secondAuthor.Uri);
+
+                Assert.Equal(new Uri("http://microsoft.com"), feed.BaseUri);
+
+                Assert.Equal(2, feed.Categories.Count);
+                SyndicationCategory firstCategory = feed.Categories[0];
+                Assert.Empty(firstCategory.AttributeExtensions);
+                Assert.Empty(firstCategory.ElementExtensions);
+                Assert.Null(firstCategory.Name);
+                Assert.Null(firstCategory.Scheme);
+                Assert.Null(firstCategory.Label);
+
+                SyndicationCategory secondCategory = feed.Categories[1];
+                Assert.Empty(secondCategory.AttributeExtensions);
+                Assert.Empty(secondCategory.ElementExtensions);
+                Assert.Equal("feedcategory_name", secondCategory.Name);
+                Assert.Equal("feedcategory_scheme", secondCategory.Scheme);
+                Assert.Null(secondCategory.Label);
+
+                Assert.Equal(2, feed.Contributors.Count);
+                SyndicationPerson firstContributor = feed.Contributors[0];
+                Assert.Empty(firstContributor.AttributeExtensions);
+                Assert.Empty(firstContributor.ElementExtensions);
+                Assert.Null(firstContributor.Email);
+                Assert.Null(firstContributor.Name);
+                Assert.Null(firstContributor.Uri);
+
+                SyndicationPerson secondContributor = feed.Contributors[1];
+                Assert.Empty(secondContributor.AttributeExtensions);
+                Assert.Empty(secondContributor.ElementExtensions);
+                Assert.Equal("feedauthor__email", secondContributor.Email);
+                Assert.Equal("feedauthor__name", secondContributor.Name);
+                Assert.Equal("feedauthor__uri", secondContributor.Uri);
+
+                Assert.Empty(feed.Copyright.AttributeExtensions);
+                Assert.Equal("feedcopyright_title", feed.Copyright.Text);
+                Assert.Equal("text", feed.Copyright.Type);
+
+                Assert.Equal("generator", feed.Generator);
+
+                Assert.Empty(feed.ElementExtensions);
+
+                Assert.Equal("id", feed.Id);
+
+                Assert.Equal(new Uri("http://imageurl.com/"), feed.ImageUrl);
+
+                SyndicationItem[] items = feed.Items.ToArray();
+                Assert.Equal(2, items.Length);
+
+                SyndicationItem item = items[1];
+                Assert.Empty(item.AttributeExtensions);
+
+                Assert.Equal(2, item.Authors.Count);
+
+                SyndicationPerson itemFirstAuthor = item.Authors[0];
+                Assert.Empty(itemFirstAuthor.AttributeExtensions);
+                Assert.Empty(itemFirstAuthor.ElementExtensions);
+                Assert.Null(itemFirstAuthor.Email);
+                Assert.Null(itemFirstAuthor.Name);
+                Assert.Null(itemFirstAuthor.Uri);
+
+                SyndicationPerson itemSecondAuthor = item.Authors[1];
+                Assert.Empty(itemSecondAuthor.AttributeExtensions);
+                Assert.Empty(itemSecondAuthor.ElementExtensions);
+                Assert.Equal("author_email", itemSecondAuthor.Email);
+                Assert.Equal("author_name", itemSecondAuthor.Name);
+                Assert.Equal("author_uri", itemSecondAuthor.Uri);
+
+                Assert.Equal(new Uri("http://microsoft/relative"), item.BaseUri);
+
+                Assert.Equal(4, item.Categories.Count);
+                SyndicationCategory itemFirstCategory = item.Categories[0];
+                Assert.Empty(itemFirstCategory.AttributeExtensions);
+                Assert.Empty(itemFirstCategory.ElementExtensions);
+                Assert.Null(itemFirstCategory.Name);
+                Assert.Null(itemFirstCategory.Scheme);
+                Assert.Null(itemFirstCategory.Label);
+
+                SyndicationCategory itemSecondCategory = item.Categories[1];
+                Assert.Empty(itemSecondCategory.AttributeExtensions);
+                Assert.Empty(itemSecondCategory.ElementExtensions);
+                Assert.Equal("category_name", itemSecondCategory.Name);
+                Assert.Equal("category_scheme", itemSecondCategory.Scheme);
+                Assert.Null(itemSecondCategory.Label);
+
+                SyndicationCategory itemThirdCategory = item.Categories[2];
+                Assert.Empty(itemThirdCategory.AttributeExtensions);
+                Assert.Empty(itemThirdCategory.ElementExtensions);
+                Assert.Equal("name", itemThirdCategory.Name);
+                Assert.Equal("scheme", itemThirdCategory.Scheme);
+                Assert.Null(itemThirdCategory.Label);
+
+                SyndicationCategory itemFourthCategory = item.Categories[3];
+                Assert.Empty(itemFourthCategory.AttributeExtensions);
+                Assert.Empty(itemFourthCategory.ElementExtensions);
+                Assert.Equal("name", itemFourthCategory.Name);
+                Assert.Equal("scheme", itemFourthCategory.Scheme);
+                Assert.Null(itemFourthCategory.Label);
+
+                TextSyndicationContent content = Assert.IsType<TextSyndicationContent>(item.Content);
+                Assert.Empty(content.AttributeExtensions);
+                Assert.Equal("overriden", content.Text);
+                Assert.Equal("text", content.Type);
+
+                Assert.Equal(2, item.Contributors.Count);
+
+                SyndicationPerson itemFirstContributor = item.Contributors[0];
+                Assert.Empty(itemFirstContributor.AttributeExtensions);
+                Assert.Empty(itemFirstContributor.ElementExtensions);
+                Assert.Null(itemFirstContributor.Email);
+                Assert.Null(itemFirstContributor.Name);
+                Assert.Null(itemFirstContributor.Uri);
+
+                SyndicationPerson itemSecondContributor = item.Contributors[1];
+                Assert.Empty(itemSecondContributor.AttributeExtensions);
+                Assert.Empty(itemSecondContributor.ElementExtensions);
+                Assert.Equal("contributor_email", itemSecondContributor.Email);
+                Assert.Equal("contributor_name", itemSecondContributor.Name);
+                Assert.Equal("contributor_uri", itemSecondContributor.Uri);
+
+                Assert.Equal(4, item.Copyright.AttributeExtensions.Count);
+                Assert.Equal("", item.Copyright.AttributeExtensions[new XmlQualifiedName("copyright_name1")]);
+                Assert.Equal("", item.Copyright.AttributeExtensions[new XmlQualifiedName("copyright_name2", "copyright_namespace")]);
+                Assert.Equal("copyright_value", item.Copyright.AttributeExtensions[new XmlQualifiedName("copyright_name3", "copyright_namespace")]);
+                Assert.Equal("", item.Copyright.AttributeExtensions[new XmlQualifiedName("copyright_name4", "xmlns")]);
+                Assert.Equal("copyright_title", item.Copyright.Text);
+                Assert.Equal("html", item.Copyright.Type);
+
+                Assert.Empty(item.ElementExtensions);
+
+                Assert.Equal("id", item.Id);
+
+                Assert.Equal(DateTimeOffset.MinValue, item.LastUpdatedTime);
+
+                Assert.Equal(3, item.Links.Count);
+
+                SyndicationLink itemFirstLink = item.Links[0];
+                Assert.Empty(itemFirstLink.AttributeExtensions);
+                Assert.Empty(itemFirstLink.ElementExtensions);
+                Assert.Equal(0, itemFirstLink.Length);
+                Assert.Null(itemFirstLink.MediaType);
+                Assert.Null(itemFirstLink.RelationshipType);
+                Assert.Null(itemFirstLink.Title);
+                Assert.Equal(string.Empty, itemFirstLink.Uri.OriginalString);
+
+                SyndicationLink itemSecondLink = item.Links[1];
+                Assert.Equal(4, itemSecondLink.AttributeExtensions.Count);
+                Assert.Equal("", itemSecondLink.AttributeExtensions[new XmlQualifiedName("link_name1")]);
+                Assert.Equal("", itemSecondLink.AttributeExtensions[new XmlQualifiedName("link_name2", "link_namespace")]);
+                Assert.Equal("link_value", itemSecondLink.AttributeExtensions[new XmlQualifiedName("link_name3", "link_namespace")]);
+                Assert.Equal("", itemSecondLink.AttributeExtensions[new XmlQualifiedName("link_name4", "xmlns")]);
+                Assert.Empty(itemSecondLink.ElementExtensions);
+                Assert.Equal(new Uri("http://link_url.com"), itemSecondLink.BaseUri);
+                Assert.Equal(10, itemSecondLink.Length);
+                Assert.Equal("link_mediaType", itemSecondLink.MediaType);
+                Assert.Equal("link_relationshipType", itemSecondLink.RelationshipType);
+                Assert.Equal("link_title", itemSecondLink.Title);
+                Assert.Equal(new Uri("http://link_uri.com"), itemSecondLink.Uri);
+
+                SyndicationLink itemThirdLink = item.Links[2];
+                Assert.Empty(itemThirdLink.AttributeExtensions);
+                Assert.Empty(itemThirdLink.ElementExtensions);
+                Assert.Equal(100, itemThirdLink.Length);
+                Assert.Equal("type_value", itemThirdLink.MediaType);
+                Assert.Equal("rel_value", itemThirdLink.RelationshipType);
+                Assert.Equal("title_value", itemThirdLink.Title);
+                Assert.Equal("href_value", itemThirdLink.Uri.OriginalString);
+
+                Assert.Equal(DateTimeOffset.MinValue, item.PublishDate);
+
+                Assert.Null(item.SourceFeed);
+
+                Assert.Empty(item.Summary.AttributeExtensions);
+                Assert.Equal("summary_title", item.Summary.Text);
+                Assert.Equal("text", item.Summary.Type);
+
+                Assert.Empty(item.Title.AttributeExtensions);
+                Assert.Equal("title_title", item.Title.Text);
+                Assert.Equal("text", item.Title.Type);
+
+                Assert.Equal("language", feed.Language);
+
+                Assert.Equal(DateTimeOffset.MinValue.AddYears(1), feed.LastUpdatedTime);
+                Assert.Equal(3, feed.Links.Count);
+
+                SyndicationLink firstLink = feed.Links[0];
+                Assert.Empty(firstLink.AttributeExtensions);
+                Assert.Empty(firstLink.ElementExtensions);
+                Assert.Equal(0, firstLink.Length);
+                Assert.Null(firstLink.MediaType);
+                Assert.Null(firstLink.RelationshipType);
+                Assert.Null(firstLink.Title);
+                Assert.Equal("", firstLink.Uri.OriginalString);
+
+                SyndicationLink secondLink = feed.Links[1];
+                Assert.Equal(4, secondLink.AttributeExtensions.Count);
+                Assert.Equal("", secondLink.AttributeExtensions[new XmlQualifiedName("syndicationlink_name1")]);
+                Assert.Equal("", secondLink.AttributeExtensions[new XmlQualifiedName("syndicationlink_name2", "syndicationlink_namespace")]);
+                Assert.Equal("syndicationlink_value", secondLink.AttributeExtensions[new XmlQualifiedName("syndicationlink_name3", "syndicationlink_namespace")]);
+                Assert.Equal("", secondLink.AttributeExtensions[new XmlQualifiedName("syndicationlink_name4", "xmlns")]);
+                Assert.Empty(secondLink.ElementExtensions);
+                Assert.Equal(new Uri("http://syndicationlink_url.com/"), secondLink.BaseUri);
+                Assert.Equal(10, secondLink.Length);
+                Assert.Equal("syndicationlink_mediaType", secondLink.MediaType);
+                Assert.Equal("syndicationlink_relationshipType", secondLink.RelationshipType);
+                Assert.Equal("syndicationlink_title", secondLink.Title);
+                Assert.Equal("http://syndicationlink_uri.com/", secondLink.Uri.OriginalString);
+
+                SyndicationLink thirdLink = feed.Links[2];
+                Assert.Empty(thirdLink.AttributeExtensions);
+                Assert.Empty(thirdLink.ElementExtensions);
+                Assert.Equal(100, thirdLink.Length);
+                Assert.Equal("type_value", thirdLink.MediaType);
+                Assert.Equal("rel_value", thirdLink.RelationshipType);
+                Assert.Equal("title_value", thirdLink.Title);
+                Assert.Equal("href_value", thirdLink.Uri.OriginalString);
+
+                Assert.Empty(item.Title.AttributeExtensions);
+                Assert.Equal("feedtitle_title", feed.Title.Text);
+                Assert.Equal("text", feed.Title.Type);
+            }
+        }
+
+        [Theory]
+        [InlineData(true, true)]
+        [InlineData(false, false)]
+        public void Read_EmptyItem_ReturnsExpected(bool preserveAttributeExtensions, bool preserveElementExtensions)
+        {
+            VerifyRead(@"<rss version=""2.0""><channel></channel></rss>", preserveElementExtensions, preserveElementExtensions, feed =>
+            {
+                Assert.Empty(feed.AttributeExtensions);
+                Assert.Empty(feed.Authors);
+                Assert.Null(feed.BaseUri);
+                Assert.Empty(feed.Categories);
+                Assert.Empty(feed.Contributors);
+                Assert.Null(feed.Copyright);
+                Assert.Null(feed.Description);
+                Assert.Empty(feed.ElementExtensions);
+                Assert.Null(feed.Generator);
+                Assert.Null(feed.Id);
+                Assert.Null(feed.ImageUrl);
+                Assert.Empty(feed.Items);
+                Assert.Null(feed.Language);
+                Assert.Equal(DateTimeOffset.MinValue, feed.LastUpdatedTime);
+                Assert.Empty(feed.Links);
+                Assert.Null(feed.Title);
+            });
+        }
+
+        private static void VerifyRead(string xmlString, bool preserveAttributeExtensions, bool preserveElementExtensions, Action<SyndicationFeed> verifyAction)
+        {
+            // ReadFrom.
+            using (var stringReader = new StringReader(xmlString))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                var formatter = new Rss20FeedFormatter()
+                {
+                    PreserveAttributeExtensions = preserveAttributeExtensions,
+                    PreserveElementExtensions = preserveElementExtensions
+                };
+                formatter.ReadFrom(reader);
+                verifyAction(formatter.Feed);
+            }
+
+            // ReadXml.
+            using (var stringReader = new StringReader(xmlString))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                reader.MoveToContent();
+
+                var formatter = new Rss20FeedFormatter()
+                {
+                    PreserveAttributeExtensions = preserveAttributeExtensions,
+                    PreserveElementExtensions = preserveElementExtensions
+                };
+                ((IXmlSerializable)formatter).ReadXml(reader);
+                verifyAction(formatter.Feed);
+            }
+
+            // Derived ReadFrom.
+            using (var stringReader = new StringReader(xmlString))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                var formatter = new Rss20FeedFormatter(typeof(SyndicationFeedSubclass))
+                {
+                    PreserveAttributeExtensions = preserveAttributeExtensions,
+                    PreserveElementExtensions = preserveElementExtensions
+                };
+                formatter.ReadFrom(reader);
+                verifyAction(formatter.Feed);
+            }
+
+            // Derived ReadXml.
+            using (var stringReader = new StringReader(xmlString))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                reader.MoveToContent();
+
+                var formatter = new Rss20FeedFormatter(typeof(SyndicationFeedSubclass))
+                {
+                    PreserveAttributeExtensions = preserveAttributeExtensions,
+                    PreserveElementExtensions = preserveElementExtensions
+                };
+                ((IXmlSerializable)formatter).ReadXml(reader);
+                verifyAction(formatter.Feed);
+            }
+
+            // Generic ReadFrom.
+            using (var stringReader = new StringReader(xmlString))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                var formatter = new Rss20FeedFormatter<SyndicationFeed>()
+                {
+                    PreserveAttributeExtensions = preserveAttributeExtensions,
+                    PreserveElementExtensions = preserveElementExtensions
+                };
+                formatter.ReadFrom(reader);
+                verifyAction(formatter.Feed);
+            }
+
+            // Generic ReadXml.
+            using (var stringReader = new StringReader(xmlString))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                reader.MoveToContent();
+
+                var formatter = new Rss20FeedFormatter<SyndicationFeed>()
+                {
+                    PreserveAttributeExtensions = preserveAttributeExtensions,
+                    PreserveElementExtensions = preserveElementExtensions
+                };
+                ((IXmlSerializable)formatter).ReadXml(reader);
+                verifyAction(formatter.Feed);
+            }
+
+            // Generic Derived ReadFrom.
+            using (var stringReader = new StringReader(xmlString))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                var formatter = new Rss20FeedFormatter<SyndicationFeedSubclass>()
+                {
+                    PreserveAttributeExtensions = preserveAttributeExtensions,
+                    PreserveElementExtensions = preserveElementExtensions
+                };
+                formatter.ReadFrom(reader);
+                verifyAction(formatter.Feed);
+            }
+
+            // Generic Derived ReadXml.
+            using (var stringReader = new StringReader(xmlString))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                reader.MoveToContent();
+
+                var formatter = new Rss20FeedFormatter<SyndicationFeedSubclass>()
+                {
+                    PreserveAttributeExtensions = preserveAttributeExtensions,
+                    PreserveElementExtensions = preserveElementExtensions
+                };
+                ((IXmlSerializable)formatter).ReadXml(reader);
+                verifyAction(formatter.Feed);
+            }
+
+            if (preserveAttributeExtensions && preserveElementExtensions)
+            {
+                // Load.
+                using (var stringReader = new StringReader(xmlString))
+                using (XmlReader reader = XmlReader.Create(stringReader))
+                {
+                    SyndicationFeed feed = SyndicationFeed.Load(reader);
+                    verifyAction(feed);
+                }
+
+                // Generic Load.
+                using (var stringReader = new StringReader(xmlString))
+                using (XmlReader reader = XmlReader.Create(stringReader))
+                {
+                    SyndicationFeed feed = SyndicationFeed.Load<SyndicationFeed>(reader);
+                    verifyAction(feed);
+                }
+            }
+        }
+
+        [Fact]
+        public void ReadFrom_NullReader_ThrowsArgumentNullException()
+        {
+            var formatter = new Rss20FeedFormatter();
+            AssertExtensions.Throws<ArgumentNullException>("reader", () => formatter.ReadFrom(null));
+        }
+
+        [Fact]
+        public void ReadFrom_NullCreatedFeed_ThrowsArgumentNullException()
+        {
+            using (var stringReader = new StringReader(@"<feed xmlns=""http://www.w3.org/2005/Atom""></feed>"))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                var formatter = new NullCreatedFeedFormatter();
+                AssertExtensions.Throws<ArgumentNullException>("feed", () => formatter.ReadFrom(reader));
+            }
+        }
+
+        [Theory]
+        [InlineData(@"<different xmlns=""http://www.w3.org/2005/Atom""></different>")]
+        [InlineData(@"<rss xmlns=""different""></rss>")]
+        [InlineData(@"<rss version=""2.0""></rss>")]
+        [InlineData(@"<rss version=""2.0""><title></title></rss>")]
+        public void ReadFrom_CantRead_ThrowsXmlException(string xmlString)
+        {
+            using (var stringReader = new StringReader(xmlString))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                var formatter = new Rss20FeedFormatter();
+                Assert.Throws<XmlException>(() => formatter.ReadFrom(reader));
+            }
+        }
+
+        [Theory]
+        [InlineData(@"<rss />")]
+        [InlineData(@"<rss version=""""></rss>")]
+        [InlineData(@"<rss version=""1.0""></rss>")]
+        [InlineData(@"<rss version=""2.0.1""></rss>")]
+        public void ReadFrom_InvalidVersion_ThrowsNotSupportedException(string xmlString)
+        {
+            using (var stringReader = new StringReader(xmlString))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                var formatter = new Rss20FeedFormatter();
+                Assert.Throws<NotSupportedException>(() => formatter.ReadFrom(reader));
+            }
+        }
+
+        [Theory]
+        [InlineData(@"<rss version=""2.0""><channel></channel></rss>")]
+        public void ReadXml_ValidReader_Success(string xmlString)
+        {
+            using (var stringReader = new StringReader(xmlString))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                reader.MoveToContent();
+
+                var formatter = new Rss20FeedFormatter();
+                ((IXmlSerializable)formatter).ReadXml(reader);
+
+                SyndicationFeed feed = formatter.Feed;
+                Assert.Empty(feed.AttributeExtensions);
+                Assert.Empty(feed.Authors);
+                Assert.Null(feed.BaseUri);
+                Assert.Empty(feed.Categories);
+                Assert.Empty(feed.Contributors);
+                Assert.Null(feed.Copyright);
+                Assert.Null(feed.Description);
+                Assert.Empty(feed.ElementExtensions);
+                Assert.Null(feed.Generator);
+                Assert.Null(feed.Id);
+                Assert.Null(feed.ImageUrl);
+                Assert.Empty(feed.Items);
+                Assert.Null(feed.Language);
+                Assert.Equal(DateTimeOffset.MinValue, feed.LastUpdatedTime);
+                Assert.Empty(feed.Links);
+                Assert.Null(feed.Title);
+            }
+        }
+
+        [Fact]
+        public void ReadXml_NullReader_ThrowsArgumentNullException()
+        {
+            IXmlSerializable formatter = new Rss20FeedFormatter();
+            AssertExtensions.Throws<ArgumentNullException>("reader", () => formatter.ReadXml(null));
+        }
+
+        [Fact]
+        public void ReadXml_NullCreatedFeed_ThrowsArgumentNullException()
+        {
+            using (var stringReader = new StringReader(@"<entry xmlns=""http://www.w3.org/2005/Atom""></entry>"))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                reader.MoveToContent();
+
+                IXmlSerializable formatter = new NullCreatedFeedFormatter();
+                AssertExtensions.Throws<ArgumentNullException>("feed", () => formatter.ReadXml(reader));
+            }
+        }
+
+        [Theory]
+        [InlineData(@"<rss version=""2.0""></rss>")]
+        [InlineData(@"<app:feed xmlns:app=""http://www.w3.org/2005/Atom"" version=""2.0""></app:feed>")]
+        [InlineData(@"<rss xmlns=""different"" version=""2.0""></rss>")]
+        [InlineData(@"<different xmlns=""http://www.w3.org/2005/Atom"" version=""2.0""></different>")]
+        public void ReadXml_CantRead_ThrowsXmlException(string xmlString)
+        {
+            using (var stringReader = new StringReader(xmlString))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                IXmlSerializable formatter = new Rss20FeedFormatter();
+                Assert.Throws<XmlException>(() => formatter.ReadXml(reader));
+            }
+        }
+
+        [Fact]
+        public void ReadXml_ThrowsArgumentException_RethrowsAsXmlException()
+        {
+            var reader = new ThrowingXmlReader(new ArgumentException());
+            IXmlSerializable formatter = new Rss20FeedFormatter();
+            Assert.Throws<XmlException>(() => formatter.ReadXml(reader));
+        }
+
+        [Fact]
+        public void ReadXml_ThrowsFormatException_RethrowsAsXmlException()
+        {
+            var reader = new ThrowingXmlReader(new FormatException());
+            IXmlSerializable formatter = new Rss20FeedFormatter();
+            Assert.Throws<XmlException>(() => formatter.ReadXml(reader));
+        }
+
+        [Fact]
+        public void ReadItem_ValidItem_ReturnsExpected()
+        {
+            using (var stringReader = new StringReader(@"<entry><id xmlns=""http://www.w3.org/2005/Atom"">id</id></entry>"))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                reader.MoveToContent();
+
+                var formatter = new Formatter();
+                var feed = new SyndicationFeed();
+                SyndicationItem item = formatter.ReadItemEntryPoint(reader, feed);
+                Assert.Equal("id", item.Id);
+                Assert.Null(item.SourceFeed);
+                Assert.Empty(feed.Items);
+            }
+        }
+
+        [Fact]
+        public void ReadItem_NullReader_ThrowsArgumentNullException()
+        {
+            var formatter = new Formatter();
+            AssertExtensions.Throws<ArgumentNullException>("reader", () => formatter.ReadItemEntryPoint(null, new SyndicationFeed()));
+        }
+
+        [Fact]
+        public void ReadItem_NullFeed_ThrowsArgumentNullException()
+        {
+            using (var stringReader = new StringReader("<entry></entry>"))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                var formatter = new Formatter();
+                AssertExtensions.Throws<ArgumentNullException>("feed", () => formatter.ReadItemEntryPoint(reader, null));
+            }
+        }
+
+        [Fact]
+        public void ReadItems_ValidItems_ReturnsExpected()
+        {
+            using (var stringReader = new StringReader(
+@"<parent>
+    <item><guid>id1</guid></item>
+    <item><guid>id2</guid></item>
+    <unknown></unknown>
+    <item><guid>id3</guid></item>
+</parent>"))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                reader.MoveToContent();
+                reader.ReadStartElement();
+                reader.MoveToElement();
+
+                var formatter = new Formatter();
+                var feed = new SyndicationFeed();
+                SyndicationItem[] items = formatter.ReadItemsEntryPoint(reader, feed, out var areAllItemsRead).ToArray();
+                Assert.True(areAllItemsRead);
+                Assert.Empty(feed.Items);
+
+                Assert.Equal(2, items.Length);
+
+                Assert.Equal("id1", items[0].Id);
+                Assert.Null(items[0].SourceFeed);
+
+                Assert.Equal("id2", items[1].Id);
+                Assert.Null(items[1].SourceFeed);
+            }
+        }
+
+        [Fact]
+        public void ReadItems_NullReader_ThrowsArgumentNullException()
+        {
+            var formatter = new Formatter();
+            AssertExtensions.Throws<ArgumentNullException>("reader", () => formatter.ReadItemsEntryPoint(null, new SyndicationFeed(), out var areAllItemsReader));
+        }
+
+        [Fact]
+        public void ReadItems_NullFeed_ThrowsArgumentNullException()
+        {
+            using (var stringReader = new StringReader("<entry></entry>"))
+            using (XmlReader reader = XmlReader.Create(stringReader))
+            {
+                var formatter = new Formatter();
+                AssertExtensions.Throws<ArgumentNullException>("feed", () => formatter.ReadItemsEntryPoint(reader, null, out var areAllItemsReader));
+            }
+        }
+
+        [Theory]
+        [InlineData(true)]
+        [InlineData(false)]
+        public void PreserveAttributeExtensions_Set_GetReturnsExpected(bool preserveAttributeExtensions)
+        {
+            var formatter = new Rss20FeedFormatter() { PreserveAttributeExtensions = preserveAttributeExtensions };
+            Assert.Equal(preserveAttributeExtensions, formatter.PreserveAttributeExtensions);
+        }
+
+        [Theory]
+        [InlineData(true)]
+        [InlineData(false)]
+        public void PreserveElementExtensions_Set_GetReturnsExpected(bool preserveElementExtensions)
+        {
+            var formatter = new Rss20FeedFormatter { PreserveElementExtensions = preserveElementExtensions };
+            Assert.Equal(preserveElementExtensions, formatter.PreserveElementExtensions);
+        }
+
+        [Fact]
+        public void CreateFeedInstance_NonGeneric_Success()
+        {
+            var formatter = new Formatter();
+            SyndicationFeed feed = Assert.IsType<SyndicationFeed>(formatter.CreateFeedInstanceEntryPoint());
+            Assert.Empty(feed.AttributeExtensions);
+            Assert.Empty(feed.Authors);
+            Assert.Null(feed.BaseUri);
+            Assert.Empty(feed.Categories);
+            Assert.Empty(feed.Contributors);
+            Assert.Null(feed.Copyright);
+            Assert.Null(feed.Description);
+            Assert.Empty(feed.ElementExtensions);
+            Assert.Null(feed.Generator);
+            Assert.Null(feed.Id);
+            Assert.Null(feed.ImageUrl);
+            Assert.Empty(feed.Items);
+            Assert.Null(feed.Language);
+            Assert.Equal(DateTimeOffset.MinValue, feed.LastUpdatedTime);
+            Assert.Empty(feed.Links);
+            Assert.Null(feed.Title);
+
+            var typedFormatter = new Formatter(typeof(SyndicationFeedSubclass));
+            feed = Assert.IsType<SyndicationFeedSubclass>(typedFormatter.CreateFeedInstanceEntryPoint());
+            Assert.Empty(feed.AttributeExtensions);
+            Assert.Empty(feed.Authors);
+            Assert.Null(feed.BaseUri);
+            Assert.Empty(feed.Categories);
+            Assert.Empty(feed.Contributors);
+            Assert.Null(feed.Copyright);
+            Assert.Null(feed.Description);
+            Assert.Empty(feed.ElementExtensions);
+            Assert.Null(feed.Generator);
+            Assert.Null(feed.Id);
+            Assert.Null(feed.ImageUrl);
+            Assert.Empty(feed.Items);
+            Assert.Null(feed.Language);
+            Assert.Equal(DateTimeOffset.MinValue, feed.LastUpdatedTime);
+            Assert.Empty(feed.Links);
+            Assert.Null(feed.Title);
+        }
+
+        [Fact]
+        public void CreateItemInstance_Generic_Success()
+        {
+            var formatter = new GenericFormatter<SyndicationFeed>();
+            SyndicationFeed feed = Assert.IsType<SyndicationFeed>(formatter.CreateFeedInstanceEntryPoint());
+            Assert.Empty(feed.AttributeExtensions);
+            Assert.Empty(feed.Authors);
+            Assert.Null(feed.BaseUri);
+            Assert.Empty(feed.Categories);
+            Assert.Empty(feed.Contributors);
+            Assert.Null(feed.Copyright);
+            Assert.Null(feed.Description);
+            Assert.Empty(feed.ElementExtensions);
+            Assert.Null(feed.Generator);
+            Assert.Null(feed.Id);
+            Assert.Null(feed.ImageUrl);
+            Assert.Empty(feed.Items);
+            Assert.Null(feed.Language);
+            Assert.Equal(DateTimeOffset.MinValue, feed.LastUpdatedTime);
+            Assert.Empty(feed.Links);
+            Assert.Null(feed.Title);
+
+            var typedFormatter = new GenericFormatter<SyndicationFeedSubclass>();
+            feed = Assert.IsType<SyndicationFeedSubclass>(typedFormatter.CreateFeedInstanceEntryPoint());
+            Assert.Empty(feed.AttributeExtensions);
+            Assert.Empty(feed.Authors);
+            Assert.Null(feed.BaseUri);
+            Assert.Empty(feed.Categories);
+            Assert.Empty(feed.Contributors);
+            Assert.Null(feed.Copyright);
+            Assert.Null(feed.Description);
+            Assert.Empty(feed.ElementExtensions);
+            Assert.Null(feed.Generator);
+            Assert.Null(feed.Id);
+            Assert.Null(feed.ImageUrl);
+            Assert.Empty(feed.Items);
+            Assert.Null(feed.Language);
+            Assert.Equal(DateTimeOffset.MinValue, feed.LastUpdatedTime);
+            Assert.Empty(feed.Links);
+            Assert.Null(feed.Title);
+        }
+
+        public class SyndicationFeedSubclass : SyndicationFeed { }
+
+        public class SyndicationFeedTryParseTrueSubclass : SyndicationFeed
+        {
+            protected override bool TryParseAttribute(string name, string ns, string value, string version) => true;
+
+            protected override bool TryParseElement(XmlReader reader, string version)
+            {
+                reader.Skip();
+                return true;
+            }
+
+            protected override SyndicationCategory CreateCategory() => new SyndicationCategoryTryParseTrueSubclass();
+
+            protected override SyndicationItem CreateItem() => new SyndicationItemTryParseTrueSubclass();
+
+            protected override SyndicationLink CreateLink() => new SyndicationLinkTryParseTrueSubclass();
+
+            protected override SyndicationPerson CreatePerson() => new SyndicationPersonTryParseTrueSubclass();
+        }
+
+        public class SyndicationItemTryParseTrueSubclass : SyndicationItem
+        {
+            protected override bool TryParseAttribute(string name, string ns, string value, string version) => true;
+
+            protected override bool TryParseContent(XmlReader reader, string contentType, string version, out SyndicationContent content)
+            {
+                reader.Skip();
+
+                content = new TextSyndicationContent("overriden");
+                return true;
+            }
+
+            protected override bool TryParseElement(XmlReader reader, string version)
+            {
+                reader.Skip();
+                return true;
+            }
+
+            protected override SyndicationCategory CreateCategory() => new SyndicationCategoryTryParseTrueSubclass();
+
+            protected override SyndicationPerson CreatePerson() => new SyndicationPersonTryParseTrueSubclass();
+
+            protected override SyndicationLink CreateLink() => new SyndicationLinkTryParseTrueSubclass();
+        }
+
+        public class SyndicationCategoryTryParseTrueSubclass : SyndicationCategory
+        {
+            protected override bool TryParseAttribute(string name, string ns, string value, string version) => true;
+
+            protected override bool TryParseElement(XmlReader reader, string version)
+            {
+                reader.Skip();
+                return true;
+            }
+        }
+
+        public class SyndicationPersonTryParseTrueSubclass : SyndicationPerson
+        {
+            protected override bool TryParseAttribute(string name, string ns, string value, string version) => true;
+
+            protected override bool TryParseElement(XmlReader reader, string version)
+            {
+                reader.Skip();
+                return true;
+            }
+        }
+
+        public class SyndicationLinkTryParseTrueSubclass : SyndicationLink
+        {
+            protected override bool TryParseAttribute(string name, string ns, string value, string version) => true;
+
+            protected override bool TryParseElement(XmlReader reader, string version)
+            {
+                reader.Skip();
+                return true;
+            }
+        }
+
+        public class NullCreatedFeedFormatter : Atom10FeedFormatter
+        {
+            protected override SyndicationFeed CreateFeedInstance() => null;
+        }
+
+        public class Formatter : Rss20FeedFormatter
+        {
+            public Formatter() : base() { }
+
+            public Formatter(SyndicationFeed feedToWrite) : base(feedToWrite) { }
+
+            public Formatter(SyndicationFeed feedToWrite, bool serializeExtensionsAsAtom) : base(feedToWrite, serializeExtensionsAsAtom) { }
+
+            public Formatter(Type feedTypeToCreate) : base(feedTypeToCreate) { }
+
+            public Type FeedTypeEntryPoint => FeedType;
+
+            public SyndicationFeed CreateFeedInstanceEntryPoint() => CreateFeedInstance();
+
+            public void WriteItemEntryPoint(XmlWriter writer, SyndicationItem item, Uri feedBaseUri) => WriteItem(writer, item, feedBaseUri);
+
+            public void WriteItemsEntryPoint(XmlWriter writer, IEnumerable<SyndicationItem> items, Uri feedBaseUri) => WriteItems(writer, items, feedBaseUri);
+
+            public SyndicationItem ReadItemEntryPoint(XmlReader reader, SyndicationFeed feed) => ReadItem(reader, feed);
+
+            public IEnumerable<SyndicationItem> ReadItemsEntryPoint(XmlReader reader, SyndicationFeed feed, out bool areAllItemsRead)
+            {
+                return ReadItems(reader, feed, out areAllItemsRead);
+            }
+        }
+
+        public class GenericFormatter<T> : Rss20FeedFormatter<T> where T : SyndicationFeed, new()
+        {
+            public GenericFormatter() : base() { }
+
+            public GenericFormatter(T feedToWrite) : base(feedToWrite) { }
+
+            public GenericFormatter(T feedToWrite, bool serializeExtensionsAsAtom) : base(feedToWrite, serializeExtensionsAsAtom) { }
+
+            public Type FeedTypeEntryPoint => FeedType;
+
+            public SyndicationFeed CreateFeedInstanceEntryPoint() => CreateFeedInstance();
+        }
+
+        [DataContract]
+        public class ExtensionObject
+        {
+            [DataMember]
+            public int Value { get; set; }
+        }
+    }
+}
index 64d1717..56b7c00 100644 (file)
@@ -51,6 +51,20 @@ namespace System.ServiceModel.Syndication.Tests
             Assert.Equal("Rss20", formatter.Version);
         }
 
+        [Theory]
+        [InlineData(true)]
+        [InlineData(false)]
+        public void Ctor_SyndicationItem_Bool(bool serializeExtensionsAsAtom)
+        {
+            var item = new SyndicationItem();
+            var formatter = new Formatter(item, serializeExtensionsAsAtom);
+            Assert.Equal(typeof(SyndicationItem), formatter.ItemTypeEntryPoint);
+            Assert.Same(item, formatter.Item);
+            Assert.True(formatter.PreserveAttributeExtensions);
+            Assert.True(formatter.PreserveElementExtensions);
+            Assert.Equal("Rss20", formatter.Version);
+        }
+
         [Fact]
         public void Ctor_GenericSyndicationItem()
         {
@@ -63,6 +77,20 @@ namespace System.ServiceModel.Syndication.Tests
             Assert.Equal("Rss20", formatter.Version);
         }
 
+        [Theory]
+        [InlineData(true)]
+        [InlineData(false)]
+        public void Ctor_GenericSyndicationItem_Bool(bool serializeExtensionsAsAtom)
+        {
+            var item = new SyndicationItem();
+            var formatter = new GenericFormatter<SyndicationItem>(item, serializeExtensionsAsAtom);
+            Assert.Equal(typeof(SyndicationItem), formatter.ItemTypeEntryPoint);
+            Assert.Same(item, formatter.Item);
+            Assert.True(formatter.PreserveAttributeExtensions);
+            Assert.True(formatter.PreserveElementExtensions);
+            Assert.Equal("Rss20", formatter.Version);
+        }
+
         [Fact]
         public void Ctor_NullItemToWrite_ThrowsArgumentNullException()
         {
@@ -512,7 +540,7 @@ namespace System.ServiceModel.Syndication.Tests
 
         [Theory]
         [MemberData(nameof(WriteTo_TestData))]
-        public void WriteTo_HasItem_SerializesExpected(SyndicationItem item, bool serializeExtensionsAsAtom, string expected)
+        public void WriteTo_Invoke_SerializesExpected(SyndicationItem item, bool serializeExtensionsAsAtom, string expected)
         {
             var formatter = new Rss20ItemFormatter(item) { SerializeExtensionsAsAtom = serializeExtensionsAsAtom };
             CompareHelper.AssertEqualWriteOutput(expected, writer => formatter.WriteTo(writer));
@@ -1843,6 +1871,8 @@ namespace System.ServiceModel.Syndication.Tests
 
             public Formatter(SyndicationItem itemToWrite) : base(itemToWrite) { }
 
+            public Formatter(SyndicationItem itemToWrite, bool serializeExtensionsAsAtom) : base(itemToWrite, serializeExtensionsAsAtom) { }
+
             public Formatter(Type itemTypeToCreate) : base(itemTypeToCreate) { }
 
             public Type ItemTypeEntryPoint => ItemType;
@@ -1856,6 +1886,8 @@ namespace System.ServiceModel.Syndication.Tests
 
             public GenericFormatter(T itemToWrite) : base(itemToWrite) { }
 
+            public GenericFormatter(T itemToWrite, bool serializeExtensionsAsAtom) : base(itemToWrite, serializeExtensionsAsAtom) { }
+
             public Type ItemTypeEntryPoint => ItemType;
 
             public SyndicationItem CreateItemInstanceEntryPoint() => CreateItemInstance();
index 1119260..fc4e519 100644 (file)
@@ -22,12 +22,12 @@ namespace System.ServiceModel.Syndication.Tests
 
         public static IEnumerable<object[]> Ctor_Items_TestData()
         {
-            yield return new object[] { null };
             yield return new object[] { new SyndicationItem[0] };
             yield return new object[] { new SyndicationItem[] { new SyndicationItem(), null } };
         }
 
         [Theory]
+        [InlineData(null)]
         [MemberData(nameof(Ctor_Items_TestData))]
         public void Ctor_Items(IEnumerable<SyndicationItem> items)
         {
@@ -236,6 +236,14 @@ namespace System.ServiceModel.Syndication.Tests
         }
 
         [Fact]
+        public void Load_InvalidReader_ThrowsXmlException()
+        {
+            XmlReader reader = new XElement("invalid").CreateReader();
+            Assert.Throws<XmlException>(() => SyndicationFeed.Load(reader));
+            Assert.Throws<XmlException>(() => SyndicationFeed.Load<SyndicationFeed>(reader));
+        }
+
+        [Fact]
         public void GetAtom10Formatter_Invoke_ReturnsExpected()
         {
             var feed = new SyndicationFeed();
@@ -389,6 +397,24 @@ namespace System.ServiceModel.Syndication.Tests
             AssertExtensions.Throws<ArgumentNullException>("writer", () => feed.WriteElementExtensionsEntryPoint(null, "version"));
         }
 
+        [Theory]
+        [MemberData(nameof(Ctor_Items_TestData))]
+        public void Items_Set_GetReturnsExpected(IEnumerable<SyndicationItem> value)
+        {
+            var feed = new SyndicationFeed
+            {
+                Items = value
+            };
+            Assert.Same(value, feed.Items);
+        }
+
+        [Fact]
+        public void Items_SetNull_ThrowsArgumentNullException()
+        {
+            var feed = new SyndicationFeed();
+            AssertExtensions.Throws<ArgumentNullException>("value", () => feed.Items = null);
+        }
+
         private static void VerifySyndicationFeed(SyndicationFeed feed, string title, string description, Uri feedAlternateLink, string id, DateTimeOffset lastUpdatedTime, IEnumerable<SyndicationItem> items)
         {
             Assert.Empty(feed.AttributeExtensions);
index 52ebf7a..fbb391c 100644 (file)
@@ -2,7 +2,9 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
+using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Xml.Linq;
 using Xunit;
 
 namespace System.ServiceModel.Syndication.Tests
@@ -125,5 +127,200 @@ namespace System.ServiceModel.Syndication.Tests
             Assert.Null(clone.TextInput);
             Assert.Null(clone.TimeToLive);
         }
+        
+        [Fact]
+        public void SkipHours_GetWithElementExtension_ReturnsExpected()
+        {
+            var feed = new SyndicationFeed();
+            feed.ElementExtensions.Add(new SyndicationElementExtension("other", "", 10));
+            feed.ElementExtensions.Add(new SyndicationElementExtension("skipHours", "other", 10));
+            feed.ElementExtensions.Add(new SyndicationElementExtension(
+                new XElement("skipHours",
+                    new XElement("hour", 0),
+                    new XElement("hour", 10),
+                    new XElement("other", 10),
+                    new XElement("hour", 23)
+                ).CreateReader())
+            );
+
+            Assert.Equal(new int[] { 0, 10, 23 }, feed.SkipHours);
+            Assert.Same(feed.SkipHours, feed.SkipHours);
+        }
+        
+        [Theory]
+        [InlineData(-1)]
+        [InlineData(24)]
+        [InlineData("invalid")]
+        [InlineData("")]
+        public void SkipHours_GetWithInvalidElementExtension_ThrowsFormatException(string value)
+        {
+            var feed = new SyndicationFeed();
+            feed.ElementExtensions.Add(new SyndicationElementExtension("other", "", 10));
+            feed.ElementExtensions.Add(new SyndicationElementExtension("skipHours", "other", 10));
+            feed.ElementExtensions.Add(new SyndicationElementExtension(
+                new XElement("skipHours",
+                    new XElement("hour", 0),
+                    new XElement("hour", 10),
+                    new XElement("other", 10),
+                    new XElement("hour", value)
+                ).CreateReader())
+            );
+
+            Assert.Throws<FormatException>(() => feed.SkipHours);
+        }
+        
+        [Fact]
+        public void SkipDays_GetWithElementExtension_ReturnsExpected()
+        {
+            var feed = new SyndicationFeed();
+            feed.ElementExtensions.Add(new SyndicationElementExtension("other", "", 10));
+            feed.ElementExtensions.Add(new SyndicationElementExtension("skipDays", "other", 10));
+            feed.ElementExtensions.Add(new SyndicationElementExtension(
+                new XElement("skipDays",
+                    new XElement("day", "monday"),
+                    new XElement("day", "tuesday"),
+                    new XElement("other", 10),
+                    new XElement("day", "wednesday"),
+                    new XElement("day", "thursday"),
+                    new XElement("day", "friday"),
+                    new XElement("day", "SATURDAY"),
+                    new XElement("day", ""),
+                    new XElement("day", "invalid"),
+                    new XElement("day", "sunday")
+                ).CreateReader())
+            );
+
+            Assert.Equal(new string[] { "monday", "tuesday", "wednesday", "thursday", "friday", "SATURDAY", "sunday"  }, feed.SkipDays);
+            Assert.Same(feed.SkipDays, feed.SkipDays);
+        }
+        
+        [Fact]
+        public void TextInput_GetWithElementExtension_ReturnsExpected()
+        {
+            var feed = new SyndicationFeed();
+            feed.ElementExtensions.Add(new SyndicationElementExtension("other", "", 10));
+            feed.ElementExtensions.Add(new SyndicationElementExtension("textInput", "other", 10));
+            feed.ElementExtensions.Add(new SyndicationElementExtension(
+                new XElement("textInput",
+                    new XElement("name", "Name"),
+                    new XElement("description", "Description"),
+                    new XElement("other", 10),
+                    new XElement("title", "Title"),
+                    new XElement("link", "http://google.com")
+                ).CreateReader())
+            );
+
+            Assert.Equal("Name", feed.TextInput.Name);
+            Assert.Equal("Description", feed.TextInput.Description);
+            Assert.Equal("Title", feed.TextInput.Title);
+            Assert.Equal(new Uri("http://google.com"), feed.TextInput.Link.Uri);
+        }
+
+        public static IEnumerable<object[]> TextInput_GetInvalid_TestData()
+        {
+            yield return new object[]
+            {
+                new SyndicationElementExtension(
+                    new XElement("textInput",
+                        new XElement("description", "Description"),
+                        new XElement("title", "Title"),
+                        new XElement("link", "http://google.com")
+                    ).CreateReader()
+                )
+            };
+            yield return new object[]
+            {
+                new SyndicationElementExtension(
+                    new XElement("textInput",
+                        new XElement("name", "Name"),
+                        new XElement("title", "Title"),
+                        new XElement("link", "http://google.com")
+                    ).CreateReader()
+                )
+            };
+            yield return new object[]
+            {
+                new SyndicationElementExtension(
+                    new XElement("textInput",
+                        new XElement("name", "Name"),
+                        new XElement("description", "Description"),
+                        new XElement("link", "http://google.com")
+                    ).CreateReader()
+                )
+            };
+            yield return new object[]
+            {
+                new SyndicationElementExtension(
+                    new XElement("textInput",
+                        new XElement("name", "Name"),
+                        new XElement("description", "Description"),
+                        new XElement("title", "Title")
+                    ).CreateReader()
+                )
+            };
+        }
+
+        [Theory]
+        [MemberData(nameof(TextInput_GetInvalid_TestData))]
+        public void TextInput_GetInvalid_ReturnsNull(SyndicationElementExtension extension)
+        {
+            var feed = new SyndicationFeed();
+            feed.ElementExtensions.Add(extension);
+            Assert.Null(feed.TextInput);
+        }
+        
+        public static IEnumerable<object[]> TimeToLive_GetWithElementExtension_TestData()
+        {
+            yield return new object[] { 10, TimeSpan.FromMinutes(10) };
+            yield return new object[] { -1, null };
+            yield return new object[] { "", null };
+            yield return new object[] { "invalid", null };
+        }
+
+        [Theory]
+        [MemberData(nameof(TimeToLive_GetWithElementExtension_TestData))]
+        public void TimeToLive_GetWithElementExtension_ReturnsExpected(object value, TimeSpan? expectedValue)
+        {
+            var feed = new SyndicationFeed();
+            feed.ElementExtensions.Add(new SyndicationElementExtension("other", "", 10));
+            feed.ElementExtensions.Add(new SyndicationElementExtension("ttl", "other", 10));
+            feed.ElementExtensions.Add(new SyndicationElementExtension("ttl", "", value));
+
+            Assert.Equal(expectedValue, feed.TimeToLive);
+        }
+
+        public static IEnumerable<object[]> TimeToLive_Set_TestData()
+        {
+            yield return new object[] { null };
+            yield return new object[] { new TimeSpan(hours: 0, minutes: 0, seconds: 0) };
+            yield return new object[] { new TimeSpan(hours: 0, minutes: 1, seconds: 0) };
+            yield return new object[] { new TimeSpan(hours: 0, minutes: 1000, seconds: 0) };
+        }
+
+        [Theory]
+        [MemberData(nameof(TimeToLive_Set_TestData))]
+        public static void TimeToLive_Set_GetReturnsExpected(TimeSpan? value)
+        {
+            var feed = new SyndicationFeed
+            {
+                TimeToLive = value
+            };
+            Assert.Equal(value, feed.TimeToLive);
+        }
+
+        public static IEnumerable<object[]> TimeToLive_SetInvalid_TestData()
+        {
+            yield return new object[] { new TimeSpan(days: 0, hours: 0, minutes: 1, seconds: 1, milliseconds: 0) };
+            yield return new object[] { new TimeSpan(days: 0, hours: 0, minutes: 1, seconds: 0, milliseconds: 1) };
+            yield return new object[] { new TimeSpan(hours: 0, minutes: -1, seconds: 0) };
+        }
+
+        [Theory]
+        [MemberData(nameof(TimeToLive_SetInvalid_TestData))]
+        public void TimeToLive_SetInvalid_ThrowsArgumentOutOfRangeException(TimeSpan value)
+        {
+            var feed = new SyndicationFeed();
+            Assert.Throws<ArgumentOutOfRangeException>("value", () => feed.TimeToLive = value);
+        }
     }
 }
index 8da9b2a..6cce9b3 100644 (file)
@@ -200,62 +200,5 @@ namespace System.ServiceModel.Syndication.Tests
                 });
         }
 
-        public static TheoryData<TimeSpan> InvalidTimeToLiveValues
-        {
-            get
-            {
-                TheoryData<TimeSpan> data = new TheoryData<TimeSpan>();
-
-                data.Add(new TimeSpan(days: 0, hours: 0, minutes: 1, seconds: 1, milliseconds: 0));
-                data.Add(new TimeSpan(days: 0, hours: 0, minutes: 1, seconds: 0, milliseconds: 1));
-                data.Add(new TimeSpan(hours: 0, minutes: -1, seconds: 0));
-
-                return data;
-            }
-        }
-
-        public static TheoryData<TimeSpan> ValidTimeToLiveValues
-        {
-            get
-            {
-                TheoryData<TimeSpan> data = new TheoryData<TimeSpan>();
-
-                data.Add(new TimeSpan(hours: 0, minutes: 0, seconds: 0));
-                data.Add(new TimeSpan(hours: 0, minutes: 1, seconds: 0));
-                data.Add(new TimeSpan(hours: 0, minutes: 1000, seconds: 0));
-
-                return data;
-            }
-        }
-
-        [Theory, MemberData(nameof(InvalidTimeToLiveValues))]
-        public static void SyndicationFeed_TimeToLive_Validation_Throws(TimeSpan invalidTimeToLive)
-        {
-            Assert.Throws<ArgumentOutOfRangeException>(() =>
-            {
-                var feed = new SyndicationFeed("Contoso News", "<div>Most recent news from Contoso</div>",
-                    new Uri("http://www.Contoso.com/news"));
-                feed.TimeToLive = invalidTimeToLive;
-            });
-        }
-
-        [Theory, MemberData(nameof(ValidTimeToLiveValues))]
-        public static void SyndicationFeed_TimeToLive_Success(TimeSpan validTimeToLive)
-        {
-            var feed = new SyndicationFeed("Contoso News", "<div>Most recent news from Contoso</div>",
-                new Uri("http://www.Contoso.com/news"));
-            feed.TimeToLive = validTimeToLive;
-            Assert.Equal(validTimeToLive, feed.TimeToLive.Value);
-        }
-
-        [Fact]
-        public static void SyndicationFeed_TimeToLive_Null_Success()
-        {
-            var feed = new SyndicationFeed("Contoso News", "<div>Most recent news from Contoso</div>",
-                new Uri("http://www.Contoso.com/news"));
-            feed.TimeToLive = null;
-            Assert.False(feed.TimeToLive.HasValue);
-        }
-
     }
 }