public void DefaultValuesTest()
 {
     AtomResourceCollectionMetadata resourceCollection = new AtomResourceCollectionMetadata();
     this.Assert.IsNull(resourceCollection.Title, "Expected null default value for property 'Title'.");
     this.Assert.IsNull(resourceCollection.Accept, "Expected null default value for property 'Accept'.");
     this.Assert.IsNull(resourceCollection.Categories, "Expected null default value for property 'Categories'.");
 }
        public void PropertySettersNullTest()
        {
            AtomResourceCollectionMetadata resourceCollection = new AtomResourceCollectionMetadata()
            {
                Title = null,
                Accept = null,
                Categories = null,
            };

            this.Assert.IsNull(resourceCollection.Title, "Expected null value for property 'Title'.");
            this.Assert.IsNull(resourceCollection.Accept, "Expected null value for property 'Accept'.");
            this.Assert.IsNull(resourceCollection.Categories, "Expected null value for property 'Categories'.");
        }
Esempio n. 3
0
        /// <summary>Determines an extension method to get the <see cref="T:Microsoft.OData.Core.Atom.AtomResourceCollectionMetadata" /> for an entity set in service document.</summary>
        /// <returns>An <see cref="T:Microsoft.OData.Core.Atom.AtomResourceCollectionMetadata" /> instance or null if no annotation of that type exists.</returns>
        /// <param name="entitySet">The entity set in service document to get the annotation from.</param>
        public static AtomResourceCollectionMetadata Atom(this ODataEntitySetInfo entitySet)
        {
            ExceptionUtils.CheckArgumentNotNull(entitySet, "entitySet");

            AtomResourceCollectionMetadata collectionMetadata = entitySet.GetAnnotation <AtomResourceCollectionMetadata>();

            if (collectionMetadata == null)
            {
                collectionMetadata = new AtomResourceCollectionMetadata();
                entitySet.SetAnnotation(collectionMetadata);
            }

            return(collectionMetadata);
        }
        /// <summary>
        /// Reads an "app:accept" element and adds the new information to <paramref name="collectionMetadata"/>.
        /// </summary>
        /// <param name="collectionMetadata">The non-null collection metadata object to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element - The start of the app:accept element.
        /// Post-Condition: Any                 - The next node after the app:accept element.
        /// </remarks>
        internal void ReadAcceptElementInCollection(AtomResourceCollectionMetadata collectionMetadata)
        {
            Debug.Assert(collectionMetadata != null, "collectionMetadata != null");
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(this.XmlReader.LocalName == AtomConstants.AtomPublishingAcceptElementName, "Expected element named 'accept'.");
            Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomPublishingNamespace, "Element 'accept' should be in the atom publishing namespace.");

            if (collectionMetadata.Accept != null)
            {
                throw new ODataException(Strings.ODataAtomServiceDocumentMetadataDeserializer_MultipleAcceptElementsFoundInCollection);
            }

            collectionMetadata.Accept = this.XmlReader.ReadElementValue();
        }
        public void PropertyGettersAndSettersTest()
        {
            AtomTextConstruct title = new AtomTextConstruct();
            string accept = "mime/type";
            AtomCategoriesMetadata categories = new AtomCategoriesMetadata();

            AtomResourceCollectionMetadata resourceCollection = new AtomResourceCollectionMetadata()
            {
                Title = title,
                Accept = accept,
                Categories = categories
            };

            this.Assert.AreSame(title, resourceCollection.Title, "Expected reference equal values for property 'Title'.");
            this.Assert.AreSame(accept, resourceCollection.Accept, "Expected reference equal values for property 'Accept'.");
            this.Assert.AreSame(categories, resourceCollection.Categories, "Expected reference equal values for property 'Categories'.");
        }
        /// <summary>
        /// Reads an atom:title element and adds the new information to <paramref name="odataServiceDocumentElement"/> and (if ATOM metadata reading is on) <paramref name="collectionMetadata"/>.
        /// </summary>
        /// <param name="collectionMetadata">The collection metadata object to augment, or null if metadata reading is not on.</param>
        /// <param name="odataServiceDocumentElement">The non-null service document element info object being populated.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element - The start of the title element.
        /// Post-Condition: Any                 - The next node after the title element.
        /// </remarks>
        internal void ReadTitleElementInCollection(AtomResourceCollectionMetadata collectionMetadata, ODataServiceDocumentElement odataServiceDocumentElement)
        {
            Debug.Assert(!this.ReadAtomMetadata || collectionMetadata != null, "collectionMetadata parameter should be non-null when ATOM metadata reading is enabled.");
            Debug.Assert(odataServiceDocumentElement != null, "collectionInfo != null");
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(this.XmlReader.LocalName == AtomConstants.AtomTitleElementName, "Expected element named 'title'.");
            Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Element 'title' should be in the atom namespace.");

            AtomTextConstruct titleTextConstruct = this.ReadTitleElement();

            if (odataServiceDocumentElement.Name == null)
            {
                odataServiceDocumentElement.Name = titleTextConstruct.Text;
            }

            if (this.ReadAtomMetadata)
            {
                collectionMetadata.Title = titleTextConstruct;
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Reads the service document element and returns the new element instance.
        /// </summary>
        /// <typeparam name="T">Type of service element to read.</typeparam>
        /// <returns>Service Element instance.</returns>
        private T ReadServiceDocumentElement <T>() where T : ODataServiceDocumentElement, new()
        {
            T      serviceDocumentElement = new T();
            string href = this.XmlReader.GetAttribute(this.AtomHRefAttributeName, this.EmptyNamespace);

            ValidationUtils.ValidateServiceDocumentElementUrl(href);

            serviceDocumentElement.Url = this.ProcessUriFromPayload(href, this.XmlReader.XmlBaseUri);
            bool enableAtomMetadataReading = this.MessageReaderSettings.EnableAtomMetadataReading;

            string name = this.XmlReader.GetAttribute(this.ODataNameAttribute, this.ODataMetadataNamespace);

            serviceDocumentElement.Name = name;

            AtomResourceCollectionMetadata collectionMetadata = null;

            if (enableAtomMetadataReading)
            {
                collectionMetadata = new AtomResourceCollectionMetadata();
            }

            if (!this.XmlReader.IsEmptyElement)
            {
                // read over the service document element.
                this.XmlReader.ReadStartElement();

                bool atomTitlesReadAlready = false;

                do
                {
                    switch (this.XmlReader.NodeType)
                    {
                    case XmlNodeType.Element:
                        if (this.XmlReader.NamespaceEquals(this.AtomPublishingNamespace))
                        {
                            if (this.XmlReader.LocalNameEquals(this.AtomPublishingCategoriesElementName))
                            {
                                if (enableAtomMetadataReading)
                                {
                                    this.ServiceDocumentMetadataDeserializer.ReadCategoriesElementInCollection(collectionMetadata);
                                }
                                else
                                {
                                    this.XmlReader.Skip();
                                }
                            }
                            else if (this.XmlReader.LocalNameEquals(this.AtomPublishingAcceptElementName))
                            {
                                if (enableAtomMetadataReading)
                                {
                                    this.ServiceDocumentMetadataDeserializer.ReadAcceptElementInCollection(collectionMetadata);
                                }
                                else
                                {
                                    this.XmlReader.Skip();
                                }
                            }
                            else
                            {
                                // Throw error if we find anything other then a 'app:categories' or an 'app:accept' element in the ATOM publishing namespace.
                                throw new ODataException(Strings.ODataAtomServiceDocumentDeserializer_UnexpectedElementInResourceCollection(this.XmlReader.LocalName));
                            }
                        }
                        else if (this.XmlReader.NamespaceEquals(this.AtomNamespace))
                        {
                            if (this.XmlReader.LocalNameEquals(this.AtomTitleElementName))
                            {
                                if (atomTitlesReadAlready)
                                {
                                    throw new ODataException(Strings.ODataAtomServiceDocumentMetadataDeserializer_MultipleTitleElementsFound(AtomConstants.AtomPublishingCollectionElementName));
                                }

                                this.ServiceDocumentMetadataDeserializer.ReadTitleElementInCollection(collectionMetadata, serviceDocumentElement);
                                atomTitlesReadAlready = true;
                            }
                            else
                            {
                                // Skip all other elements in the atom namespace
                                this.XmlReader.Skip();
                            }
                        }
                        else
                        {
                            // For now, skip all other elements.
                            this.XmlReader.Skip();
                        }

                        break;

                    case XmlNodeType.EndElement:
                        // end of service document element.
                        break;

                    default:
                        // ignore all other nodes.
                        this.XmlReader.Skip();
                        break;
                    }
                }while (this.XmlReader.NodeType != XmlNodeType.EndElement);
            }

            this.AssertXmlCondition(true, XmlNodeType.EndElement);

            // read over the end tag of the service document element or the start tag if the collection element is empty.
            this.XmlReader.Read();

            if (enableAtomMetadataReading)
            {
                serviceDocumentElement.SetAnnotation(collectionMetadata);
            }

            return(serviceDocumentElement);
        }
        /// <summary>
        /// Writes the ATOM metadata for a single entity set element.
        /// </summary>
        /// <param name="entitySetInfo">The entity set element to get the metadata for and write it.</param>
        internal void WriteEntitySetInfoMetadata(ODataEntitySetInfo entitySetInfo)
        {
            Debug.Assert(entitySetInfo != null, "collection != null");
            Debug.Assert(entitySetInfo.Url != null, "collection.Url should have been validated at this point");

            AtomResourceCollectionMetadata metadata = entitySetInfo.GetAnnotation <AtomResourceCollectionMetadata>();

            AtomTextConstruct title = null;

            if (metadata != null)
            {
                title = metadata.Title;
            }

            if (entitySetInfo.Name != null)
            {
                if (title == null)
                {
                    title = new AtomTextConstruct {
                        Text = entitySetInfo.Name
                    };
                }
                else if (string.CompareOrdinal(title.Text, entitySetInfo.Name) != 0)
                {
                    throw new ODataException(ODataErrorStrings.ODataAtomServiceDocumentMetadataSerializer_ResourceCollectionNameAndTitleMismatch(entitySetInfo.Name, title.Text));
                }
            }

            // The ATOMPUB specification requires a title.
            // <atom:title>title</atom:title>
            // Note that this will write an empty atom:title element even if the title is null.
            if (this.UseServerFormatBehavior && title.Kind == AtomTextConstructKind.Text)
            {
                // For WCF DS server we must not write the type attribute, just a simple <atom:title>title<atom:title>
                this.WriteElementWithTextContent(
                    AtomConstants.NonEmptyAtomNamespacePrefix,
                    AtomConstants.AtomTitleElementName,
                    AtomConstants.AtomNamespace,
                    title.Text);
            }
            else
            {
                this.WriteTextConstruct(AtomConstants.NonEmptyAtomNamespacePrefix, AtomConstants.AtomTitleElementName, AtomConstants.AtomNamespace, title);
            }

            if (metadata != null)
            {
                string accept = metadata.Accept;
                if (accept != null)
                {
                    // <app:accept>accept</app:accept>
                    this.WriteElementWithTextContent(
                        string.Empty,
                        AtomConstants.AtomPublishingAcceptElementName,
                        AtomConstants.AtomPublishingNamespace,
                        accept);
                }

                AtomCategoriesMetadata categories = metadata.Categories;
                if (categories != null)
                {
                    // <app:categories>
                    this.XmlWriter.WriteStartElement(string.Empty, AtomConstants.AtomPublishingCategoriesElementName, AtomConstants.AtomPublishingNamespace);

                    Uri    href       = categories.Href;
                    bool?  fixedValue = categories.Fixed;
                    string scheme     = categories.Scheme;
                    IEnumerable <AtomCategoryMetadata> categoriesCollection = categories.Categories;
                    if (href != null)
                    {
                        // Out of line categories document
                        if (fixedValue.HasValue || scheme != null ||
                            (categoriesCollection != null && categoriesCollection.Any()))
                        {
                            throw new ODataException(ODataErrorStrings.ODataAtomWriterMetadataUtils_CategoriesHrefWithOtherValues);
                        }

                        this.XmlWriter.WriteAttributeString(AtomConstants.AtomHRefAttributeName, this.UriToUrlAttributeValue(href));
                    }
                    else
                    {
                        // Inline categories document

                        // fixed='yes|no'
                        if (fixedValue.HasValue)
                        {
                            this.XmlWriter.WriteAttributeString(
                                AtomConstants.AtomPublishingFixedAttributeName,
                                fixedValue.Value ? AtomConstants.AtomPublishingFixedYesValue : AtomConstants.AtomPublishingFixedNoValue);
                        }

                        // scheme='scheme'
                        if (scheme != null)
                        {
                            this.XmlWriter.WriteAttributeString(AtomConstants.AtomCategorySchemeAttributeName, scheme);
                        }

                        if (categoriesCollection != null)
                        {
                            foreach (AtomCategoryMetadata category in categoriesCollection)
                            {
                                // <atom:category/>
                                this.WriteCategory(AtomConstants.NonEmptyAtomNamespacePrefix, category.Term, category.Scheme, category.Label);
                            }
                        }
                    }

                    // </app:categories>
                    this.XmlWriter.WriteEndElement();
                }
            }
        }
        /// <summary>
        /// Reads an app:categories element as well as each atom:category element contained within it, and adds the new information to <paramref name="collectionMetadata"/>.
        /// </summary>
        /// <param name="collectionMetadata">The non-null collection metadata object to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element - The start of the app:categories element.
        /// Post-Condition: Any                 - The next node after the app:categories element.
        /// </remarks>
        internal void ReadCategoriesElementInCollection(AtomResourceCollectionMetadata collectionMetadata)
        {
            Debug.Assert(collectionMetadata != null, "collectionMetadata != null");
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(this.XmlReader.LocalName == AtomConstants.AtomPublishingCategoriesElementName, "Expected element named 'categories'.");
            Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomPublishingNamespace, "Element 'categories' should be in the atom publishing namespace.");

            AtomCategoriesMetadata      categoriesMetadata = new AtomCategoriesMetadata();
            List <AtomCategoryMetadata> categoryList       = new List <AtomCategoryMetadata>();

            while (this.XmlReader.MoveToNextAttribute())
            {
                string attributeValue = this.XmlReader.Value;

                if (this.XmlReader.NamespaceEquals(this.EmptyNamespace))
                {
                    if (this.XmlReader.LocalNameEquals(this.AtomHRefAttributeName))
                    {
                        categoriesMetadata.Href = this.ProcessUriFromPayload(attributeValue, this.XmlReader.XmlBaseUri);
                    }
                    else if (this.XmlReader.LocalNameEquals(this.AtomPublishingFixedAttributeName))
                    {
                        if (String.CompareOrdinal(attributeValue, AtomConstants.AtomPublishingFixedYesValue) == 0)
                        {
                            categoriesMetadata.Fixed = true;
                        }
                        else if (String.CompareOrdinal(attributeValue, AtomConstants.AtomPublishingFixedNoValue) == 0)
                        {
                            categoriesMetadata.Fixed = false;
                        }
                        else
                        {
                            throw new ODataException(Strings.ODataAtomServiceDocumentMetadataDeserializer_InvalidFixedAttributeValue(attributeValue));
                        }
                    }
                    else if (this.XmlReader.LocalNameEquals(this.AtomCategorySchemeAttributeName))
                    {
                        categoriesMetadata.Scheme = attributeValue;
                    }
                }
            }

            this.XmlReader.MoveToElement();

            if (!this.XmlReader.IsEmptyElement)
            {
                // read over the categories element
                this.XmlReader.ReadStartElement();

                do
                {
                    switch (this.XmlReader.NodeType)
                    {
                    case XmlNodeType.Element:
                        if (this.XmlReader.NamespaceEquals(this.AtomNamespace) && this.XmlReader.LocalNameEquals(this.AtomCategoryElementName))
                        {
                            categoryList.Add(this.ReadCategoryElementInCollection());
                        }

                        break;

                    case XmlNodeType.EndElement:
                        // end of 'categories' element.
                        break;

                    default:
                        // ignore all other nodes.
                        this.XmlReader.Skip();
                        break;
                    }
                }while (this.XmlReader.NodeType != XmlNodeType.EndElement);
            } // if (!this.XmlReader.IsEmptyElement)

            // read over the end tag of the categories element or the start tag if the categories element is empty.
            this.XmlReader.Read();

            categoriesMetadata.Categories = new ReadOnlyEnumerable <AtomCategoryMetadata>(categoryList);
            collectionMetadata.Categories = categoriesMetadata;
        }
Esempio n. 10
0
        /// <summary>Determines an extension method to get the <see cref="T:Microsoft.OData.Core.Atom.AtomResourceCollectionMetadata" /> for an entity set in service document.</summary>
        /// <returns>An <see cref="T:Microsoft.OData.Core.Atom.AtomResourceCollectionMetadata" /> instance or null if no annotation of that type exists.</returns>
        /// <param name="entitySet">The entity set in service document to get the annotation from.</param>
        public static AtomResourceCollectionMetadata Atom(this ODataEntitySetInfo entitySet)
        {
            ExceptionUtils.CheckArgumentNotNull(entitySet, "entitySet");

            AtomResourceCollectionMetadata collectionMetadata = entitySet.GetAnnotation<AtomResourceCollectionMetadata>();
            if (collectionMetadata == null)
            {
                collectionMetadata = new AtomResourceCollectionMetadata();
                entitySet.SetAnnotation(collectionMetadata);
            }

            return collectionMetadata;
        }
        public void CategoryMetadataOnWorkspaceCollectionCategoriesWriterTest()
        {
            var testCases = this.CreateAtomCategoryTestCases();

            // Convert test cases to test descriptions
            var testDescriptors = testCases.Select(testCase =>
            {
                AtomResourceCollectionMetadata metadata = new AtomResourceCollectionMetadata
                {
                    Categories = new AtomCategoriesMetadata
                    {
                        Categories = new[] { testCase.Category }
                    }
                };

                ODataEntitySetInfo collection = new ODataEntitySetInfo { Url = new Uri("http://odata.org/collection") };
                collection.SetAnnotation(metadata);
                ODataServiceDocument serviceDocument = new ODataServiceDocument { EntitySets = new[] { collection } };

                return new PayloadWriterTestDescriptor<ODataServiceDocument>(
                    this.Settings, 
                    serviceDocument,
                    testConfiguration =>
                        new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings) 
                        { 
                            Xml = testCase.Xml, 
                            ExpectedException2 = testCase.ExpectedException, 
                            FragmentExtractor = result => result
                                .Element(TestAtomConstants.AtomPublishingXNamespace + TestAtomConstants.AtomPublishingWorkspaceElementName)
                                .Element(TestAtomConstants.AtomPublishingXNamespace + TestAtomConstants.AtomPublishingCollectionElementName)
                                .Element(TestAtomConstants.AtomPublishingXNamespace + TestAtomConstants.AtomPublishingCategoriesElementName)
                                .Element(TestAtomConstants.AtomXNamespace + TestAtomConstants.AtomCategoryElementName)
                        });
            });

            this.CombinatorialEngineProvider.RunCombinations(
                testDescriptors,
                this.WriterTestConfigurationProvider.AtomFormatConfigurations.Where(tc => !tc.IsRequest),
                (testDescriptor, testConfiguration) =>
                {
                    testConfiguration = testConfiguration.Clone();
                    testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri);

                    TestWriterUtils.WriteAndVerifyTopLevelContent(
                        testDescriptor,
                        testConfiguration,
                        (messageWriter) => messageWriter.WriteServiceDocument(testDescriptor.PayloadItems.Single()),
                        this.Assert,
                        baselineLogger:this.Logger);
                });
        }
        public void AtomCategoriesMetadataTest()
        {
            var testCases = new []
            {
                // Empty categories
                new
                {
                    CategoriesMetadata = new AtomCategoriesMetadata(),
                    Xml = "<categories xmlns='http://www.w3.org/2007/app'/>",
                    ExpectedException = (ExpectedException)null
                },
                // Categories with href
                new
                {
                    CategoriesMetadata = new AtomCategoriesMetadata() { Href = new Uri("http://odata.org/href") },
                    Xml = "<categories href='http://odata.org/href' xmlns='http://www.w3.org/2007/app'/>",
                    ExpectedException = (ExpectedException)null
                },
                // Categories with href and non-null empty categories (null and empty collection should be treated the same)
                new
                {
                    CategoriesMetadata = new AtomCategoriesMetadata() { Href = new Uri("http://odata.org/href"), Categories = new AtomCategoryMetadata[0] },
                    Xml = "<categories href='http://odata.org/href' xmlns='http://www.w3.org/2007/app'/>",
                    ExpectedException = (ExpectedException)null
                },
                // Categories with fixed yes
                new
                {
                    CategoriesMetadata = new AtomCategoriesMetadata() { Fixed = true },
                    Xml = "<categories fixed='yes' xmlns='http://www.w3.org/2007/app'/>",
                    ExpectedException = (ExpectedException)null
                },
                // Categories with fixed no
                new
                {
                    CategoriesMetadata = new AtomCategoriesMetadata() { Fixed = false },
                    Xml = "<categories fixed='no' xmlns='http://www.w3.org/2007/app'/>",
                    ExpectedException = (ExpectedException)null
                },
                // Categories with scheme
                new
                {
                    CategoriesMetadata = new AtomCategoriesMetadata() { Scheme = "http://odata.org/scheme" },
                    Xml = "<categories scheme='http://odata.org/scheme' xmlns='http://www.w3.org/2007/app'/>",
                    ExpectedException = (ExpectedException)null
                },
                // Categories with scheme and fixed
                new
                {
                    CategoriesMetadata = new AtomCategoriesMetadata() { Fixed = true, Scheme = "http://odata.org/scheme" },
                    Xml = "<categories fixed='yes' scheme='http://odata.org/scheme' xmlns='http://www.w3.org/2007/app'/>",
                    ExpectedException = (ExpectedException)null
                },
                // Categories with single category
                new
                {
                    CategoriesMetadata = new AtomCategoriesMetadata() { Categories = new [] { new AtomCategoryMetadata { Term = "myterm", Scheme = "http://odata.org/scheme" } } },
                    Xml = "<categories xmlns='http://www.w3.org/2007/app'><atom:category term='myterm' scheme='http://odata.org/scheme' xmlns:atom='http://www.w3.org/2005/Atom'/></categories>",
                    ExpectedException = (ExpectedException)null
                },
                // Categories with two categories
                new
                {
                    CategoriesMetadata = new AtomCategoriesMetadata() { Categories = new [] { 
                        new AtomCategoryMetadata { Term = "myterm", Scheme = "http://odata.org/scheme" },
                        new AtomCategoryMetadata { Term = "second", Scheme = "http://odata.org/scheme2" } } },
                    Xml = "<categories xmlns='http://www.w3.org/2007/app'>" +
                            "<atom:category term='myterm' scheme='http://odata.org/scheme' xmlns:atom='http://www.w3.org/2005/Atom'/>" +
                            "<atom:category term='second' scheme='http://odata.org/scheme2' xmlns:atom='http://www.w3.org/2005/Atom'/>" +
                        "</categories>",
                    ExpectedException = (ExpectedException)null
                },
                // Categories with href and fixed (error)
                new
                {
                    CategoriesMetadata = new AtomCategoriesMetadata() { Href = new Uri("http://odata.org/href"), Fixed = true },
                    Xml = string.Empty,
                    ExpectedException = ODataExpectedExceptions.ODataException("ODataAtomWriterMetadataUtils_CategoriesHrefWithOtherValues")
                },
                // Categories with href and scheme (error)
                new
                {
                    CategoriesMetadata = new AtomCategoriesMetadata() { Href = new Uri("http://odata.org/href"), Scheme = "http://odata.org/scheme" },
                    Xml = string.Empty,
                    ExpectedException = ODataExpectedExceptions.ODataException("ODataAtomWriterMetadataUtils_CategoriesHrefWithOtherValues")
                },
                // Categories with href and non-empty categories (error)
                new
                {
                    CategoriesMetadata = new AtomCategoriesMetadata() { Href = new Uri("http://odata.org/href"), Categories = new [] { new AtomCategoryMetadata() } },
                    Xml = string.Empty,
                    ExpectedException = ODataExpectedExceptions.ODataException("ODataAtomWriterMetadataUtils_CategoriesHrefWithOtherValues")
                },
            };

            var testDescriptors = testCases.Select(testCase =>
            {
                ODataEntitySetInfo collection = new ODataEntitySetInfo { Url = new Uri("http://odata.org/url") };
                AtomResourceCollectionMetadata metadata = new AtomResourceCollectionMetadata() { Categories = testCase.CategoriesMetadata };
                collection.SetAnnotation(metadata);
                ODataServiceDocument serviceDocument = new ODataServiceDocument { EntitySets = new [] { collection } };
                return new PayloadWriterTestDescriptor<ODataServiceDocument>(
                    this.Settings,
                    new [] { serviceDocument },
                    tc =>
                    {
                        if (testCase.ExpectedException != null)
                        {
                            return new WriterTestExpectedResults(this.Settings.ExpectedResultSettings) { ExpectedException2 = testCase.ExpectedException };
                        }
                        else
                        {
                            return new AtomWriterTestExpectedResults(this.Settings.ExpectedResultSettings)
                            {
                                Xml = testCase.Xml,
                                FragmentExtractor = result =>
                                    result
                                        .Element(TestAtomConstants.AtomPublishingXNamespace + TestAtomConstants.AtomPublishingWorkspaceElementName)
                                        .Element(TestAtomConstants.AtomPublishingXNamespace + TestAtomConstants.AtomPublishingCollectionElementName)
                                        .Element(TestAtomConstants.AtomPublishingXNamespace + TestAtomConstants.AtomPublishingCategoriesElementName)
                            };
                        }
                    });
            });

            this.CombinatorialEngineProvider.RunCombinations(
                testDescriptors,
                this.WriterTestConfigurationProvider.AtomFormatConfigurations.Where(tc => !tc.IsRequest),
                (testDescriptor, testConfiguration) =>
                {
                    testConfiguration = testConfiguration.Clone();
                    testConfiguration.MessageWriterSettings.SetServiceDocumentUri(ServiceDocumentUri);

                    TestWriterUtils.WriteAndVerifyTopLevelContent(
                        testDescriptor,
                        testConfiguration,
                        (messageWriter) => messageWriter.WriteServiceDocument(testDescriptor.PayloadItems.Single()),
                        this.Assert,
                        baselineLogger: this.Logger);
                });
        }
 /// <summary>
 /// Visits an ATOM resource collection metadata.
 /// </summary>
 /// <param name="atomResourceCollectionMetadata">The resource collection metadata to visit.</param>
 protected virtual void VisitAtomResourceCollectionMetadata(AtomResourceCollectionMetadata atomResourceCollectionMetadata)
 {
     this.VisitAtomMetadata(atomResourceCollectionMetadata.Title);
     this.VisitAtomMetadata(atomResourceCollectionMetadata.Categories);
 }
        /// <summary>
        /// Reads an atom:title element and adds the new information to <paramref name="odataServiceDocumentElement"/> and (if ATOM metadata reading is on) <paramref name="collectionMetadata"/>.
        /// </summary>
        /// <param name="collectionMetadata">The collection metadata object to augment, or null if metadata reading is not on.</param>
        /// <param name="odataServiceDocumentElement">The non-null service document element info object being populated.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element - The start of the title element.
        /// Post-Condition: Any                 - The next node after the title element. 
        /// </remarks>
        internal void ReadTitleElementInCollection(AtomResourceCollectionMetadata collectionMetadata, ODataServiceDocumentElement odataServiceDocumentElement)
        {
            Debug.Assert(!this.ReadAtomMetadata || collectionMetadata != null, "collectionMetadata parameter should be non-null when ATOM metadata reading is enabled.");
            Debug.Assert(odataServiceDocumentElement != null, "collectionInfo != null");
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(this.XmlReader.LocalName == AtomConstants.AtomTitleElementName, "Expected element named 'title'.");
            Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomNamespace, "Element 'title' should be in the atom namespace.");

            AtomTextConstruct titleTextConstruct = this.ReadTitleElement();

            if (odataServiceDocumentElement.Name == null)
            {
                odataServiceDocumentElement.Name = titleTextConstruct.Text;
            }

            if (this.ReadAtomMetadata)
            {
                collectionMetadata.Title = titleTextConstruct;
            }
        }
        /// <summary>
        /// Reads an "app:accept" element and adds the new information to <paramref name="collectionMetadata"/>.
        /// </summary>
        /// <param name="collectionMetadata">The non-null collection metadata object to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element - The start of the app:accept element.
        /// Post-Condition: Any                 - The next node after the app:accept element. 
        /// </remarks>
        internal void ReadAcceptElementInCollection(AtomResourceCollectionMetadata collectionMetadata)
        {
            Debug.Assert(collectionMetadata != null, "collectionMetadata != null");
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(this.XmlReader.LocalName == AtomConstants.AtomPublishingAcceptElementName, "Expected element named 'accept'.");
            Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomPublishingNamespace, "Element 'accept' should be in the atom publishing namespace.");

            if (collectionMetadata.Accept != null)
            {
                throw new ODataException(Strings.ODataAtomServiceDocumentMetadataDeserializer_MultipleAcceptElementsFoundInCollection);
            }

            collectionMetadata.Accept = this.XmlReader.ReadElementValue();
        }
        /// <summary>
        /// Reads an app:categories element as well as each atom:category element contained within it, and adds the new information to <paramref name="collectionMetadata"/>.
        /// </summary>
        /// <param name="collectionMetadata">The non-null collection metadata object to augment.</param>
        /// <remarks>
        /// Pre-Condition:  XmlNodeType.Element - The start of the app:categories element.
        /// Post-Condition: Any                 - The next node after the app:categories element. 
        /// </remarks>
        internal void ReadCategoriesElementInCollection(AtomResourceCollectionMetadata collectionMetadata)
        {
            Debug.Assert(collectionMetadata != null, "collectionMetadata != null");
            this.AssertXmlCondition(XmlNodeType.Element);
            Debug.Assert(this.XmlReader.LocalName == AtomConstants.AtomPublishingCategoriesElementName, "Expected element named 'categories'.");
            Debug.Assert(this.XmlReader.NamespaceURI == AtomConstants.AtomPublishingNamespace, "Element 'categories' should be in the atom publishing namespace.");

            AtomCategoriesMetadata categoriesMetadata = new AtomCategoriesMetadata();
            List<AtomCategoryMetadata> categoryList = new List<AtomCategoryMetadata>();

            while (this.XmlReader.MoveToNextAttribute())
            {
                string attributeValue = this.XmlReader.Value;

                if (this.XmlReader.NamespaceEquals(this.EmptyNamespace))
                {
                    if (this.XmlReader.LocalNameEquals(this.AtomHRefAttributeName))
                    {
                        categoriesMetadata.Href = this.ProcessUriFromPayload(attributeValue, this.XmlReader.XmlBaseUri);
                    }
                    else if (this.XmlReader.LocalNameEquals(this.AtomPublishingFixedAttributeName))
                    {
                        if (String.CompareOrdinal(attributeValue, AtomConstants.AtomPublishingFixedYesValue) == 0)
                        {
                            categoriesMetadata.Fixed = true;
                        }
                        else if (String.CompareOrdinal(attributeValue, AtomConstants.AtomPublishingFixedNoValue) == 0)
                        {
                            categoriesMetadata.Fixed = false;
                        }
                        else
                        {
                            throw new ODataException(Strings.ODataAtomServiceDocumentMetadataDeserializer_InvalidFixedAttributeValue(attributeValue));
                        }
                    }
                    else if (this.XmlReader.LocalNameEquals(this.AtomCategorySchemeAttributeName))
                    {
                        categoriesMetadata.Scheme = attributeValue;
                    }
                }
            }

            this.XmlReader.MoveToElement();

            if (!this.XmlReader.IsEmptyElement)
            {
                // read over the categories element
                this.XmlReader.ReadStartElement();

                do
                {
                    switch (this.XmlReader.NodeType)
                    {
                        case XmlNodeType.Element:
                            if (this.XmlReader.NamespaceEquals(this.AtomNamespace) && this.XmlReader.LocalNameEquals(this.AtomCategoryElementName))
                            {
                                categoryList.Add(this.ReadCategoryElementInCollection());
                            }

                            break;
                        case XmlNodeType.EndElement:
                            // end of 'categories' element.
                            break;
                        default:
                            // ignore all other nodes.
                            this.XmlReader.Skip();
                            break;
                    }
                }
                while (this.XmlReader.NodeType != XmlNodeType.EndElement);
            } // if (!this.XmlReader.IsEmptyElement)

            // read over the end tag of the categories element or the start tag if the categories element is empty.
            this.XmlReader.Read();

            categoriesMetadata.Categories = new ReadOnlyEnumerable<AtomCategoryMetadata>(categoryList);
            collectionMetadata.Categories = categoriesMetadata;
        }